First commit
This commit is contained in:
commit
9c27f339be
|
@ -0,0 +1,7 @@
|
|||
build
|
||||
bin
|
||||
*.db
|
||||
*.crvm
|
||||
*.o
|
||||
*.a
|
||||
*.exe
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/include",
|
||||
"${workspaceFolder}/build/_deps/json-src/include/nlohmann",
|
||||
"${workspaceFolder}/build/_deps/json-src/include"
|
||||
],
|
||||
"defines": ["CROSSLANG_ENABLE_JSON=1","CROSSLANG_ENABLE_THREADING=1","CROSSLANG_ENABLE_FILE_IO=1","CROSSLANG_ENABLE_NETWORK=1","CROSSLANG_ENABLE_TERMIOS=1","CROSSLANG_ENABLE_MBED=1","CROSSLANG_ENABLE_SQLITE=1","CROSSLANG_ENABLE_SDL2=1","-DCROSSLANG_ENABLE_PROCESS=1"],
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "gnu++23",
|
||||
"intelliSenseMode": "linux-gcc-x64"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(gdb) Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/crossint",
|
||||
"args": ["/home/mike/crossint.tcross"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"description": "Set Disassembly Flavor to Intel",
|
||||
"text": "-gdb-set disassembly-flavor intel",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"variant": "cpp",
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"hash_map": "cpp",
|
||||
"bit": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"cctype": "cpp",
|
||||
"charconv": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"compare": "cpp",
|
||||
"concepts": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"list": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"vector": "cpp",
|
||||
"exception": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"functional": "cpp",
|
||||
"iterator": "cpp",
|
||||
"memory": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"random": "cpp",
|
||||
"string_view": "cpp",
|
||||
"system_error": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"format": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"limits": "cpp",
|
||||
"new": "cpp",
|
||||
"numbers": "cpp",
|
||||
"ostream": "cpp",
|
||||
"span": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"text_encoding": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"fstream": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"sstream": "cpp",
|
||||
"any": "cpp",
|
||||
"codecvt": "cpp",
|
||||
"cstring": "cpp",
|
||||
"forward_list": "cpp",
|
||||
"map": "cpp",
|
||||
"ratio": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"ranges": "cpp",
|
||||
"valarray": "cpp",
|
||||
"chrono": "cpp",
|
||||
"filesystem": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"csignal": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"mutex": "cpp",
|
||||
"semaphore": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"thread": "cpp"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
project(TessesCrossLang)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(ENABLE_TESTING OFF)
|
||||
option(CROSSLANG_ENABLE_STATIC "Enable Tesses CrossLang static libraries" ON)
|
||||
option(CROSSLANG_ENABLE_SHARED "Enable Tesses CrossLang shared libraries" ON)
|
||||
option(CROSSLANG_ENABLE_BINARIES "Enable Tesses CrossLang binaries" ON)
|
||||
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_64BIT_FILE "Allow files over 2GiB" ON)
|
||||
|
||||
|
||||
set(JANSSON_DIR "Directory for Jansson" "")
|
||||
|
||||
find_package(TessesFramework REQUIRED)
|
||||
|
||||
function(CROSSLANG_LINK_DEPS CROSSLANG_TARGET_NAME)
|
||||
if(CROSSLANG_ENABLE_PROCESS)
|
||||
target_compile_definitions(${CROSSLANG_TARGET_NAME} PUBLIC CROSSLANG_ENABLE_PROCESS)
|
||||
endif()
|
||||
if(CROSSLANG_ENABLE_JSON)
|
||||
target_compile_definitions(${CROSSLANG_TARGET_NAME} PUBLIC CROSSLANG_ENABLE_JSON)
|
||||
if(JANSSON_DIR STREQUAL "")
|
||||
else()
|
||||
target_include_directories(${CROSSLANG_TARGET_NAME} PUBLIC ${JANSSON_DIR}/include)
|
||||
target_link_directories(${CROSSLANG_TARGET_NAME} PUBLIC ${JANSSON_DIR}/lib)
|
||||
endif()
|
||||
target_link_libraries(${CROSSLANG_TARGET_NAME} PUBLIC jansson)
|
||||
endif()
|
||||
if(CROSSLANG_ENABLE_THREADING)
|
||||
target_compile_definitions(${CROSSLANG_TARGET_NAME} PUBLIC CROSSLANG_ENABLE_THREADING)
|
||||
endif()
|
||||
if(CROSSLANG_ENABLE_TERMIOS)
|
||||
target_compile_definitions(${CROSSLANG_TARGET_NAME} PUBLIC CROSSLANG_ENABLE_TERMIOS)
|
||||
endif()
|
||||
|
||||
if(CROSSLANG_ENABLE_SQLITE)
|
||||
target_compile_definitions(${CROSSLANG_TARGET_NAME} PUBLIC CROSSLANG_ENABLE_SQLITE)
|
||||
endif()
|
||||
|
||||
|
||||
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_LIBRARIES})
|
||||
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)
|
||||
endif()
|
||||
if("${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoWii")
|
||||
target_link_libraries(${CROSSLANG_TARGET_NAME} PUBLIC wiisocket)
|
||||
endif()
|
||||
|
||||
target_include_directories(${CROSSLANG_TARGET_NAME} PUBLIC include)
|
||||
target_link_libraries(${CROSSLANG_TARGET_NAME} PUBLIC TessesFramework::tessesframework)
|
||||
endfunction()
|
||||
|
||||
|
||||
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
|
||||
src/runtime_methods/net.cpp
|
||||
src/runtime_methods/vm.cpp
|
||||
src/runtime_methods/json.cpp
|
||||
src/runtime_methods/sqlite.cpp
|
||||
src/runtime_methods/dictionary.cpp
|
||||
src/runtime_methods/crypto.cpp
|
||||
src/runtime_methods/ogc.cpp
|
||||
src/runtime_methods/path.cpp
|
||||
src/runtime_methods/env.cpp
|
||||
src/sago/platform_folders.cpp
|
||||
src/types/ittr.cpp
|
||||
src/types/closure.cpp
|
||||
src/types/dictionary.cpp
|
||||
src/types/externalmethod.cpp
|
||||
src/types/list.cpp
|
||||
src/types/native.cpp
|
||||
src/types/rootenvironment.cpp
|
||||
src/types/subenvironment.cpp
|
||||
src/types/vfsheapobject.cpp
|
||||
src/types/streamheapobject.cpp
|
||||
src/vm/filereader.cpp
|
||||
src/vm/gc.cpp
|
||||
src/vm/gclist.cpp
|
||||
src/vm/vm.cpp
|
||||
src/bitconverter.cpp
|
||||
)
|
||||
|
||||
if(CROSSLANG_ENABLE_SQLITE)
|
||||
list(APPEND CROSSLANG_SOURCE src/sqlite/sqlite3.c src/sqlite/vfs.c)
|
||||
endif()
|
||||
|
||||
|
||||
if(CROSSLANG_ENABLE_STATIC)
|
||||
add_library(crosslang_static STATIC ${CROSSLANG_SOURCE})
|
||||
CROSSLANG_LINK_DEPS(crosslang_static)
|
||||
install(TARGETS crosslang_static DESTINATION lib)
|
||||
endif()
|
||||
|
||||
|
||||
#if(CROSSLANG_ENABLE_SHARED)
|
||||
#add_library(crosslang_shared SHARED ${CROSSLANG_SOURCE})
|
||||
#CROSSLANG_LINK_DEPS(crosslang_shared)
|
||||
#install(TARGETS crosslang_shared DESTINATION lib)
|
||||
#endif()
|
||||
|
||||
if(CROSSLANG_ENABLE_BINARIES)
|
||||
|
||||
|
||||
if(CROSSLANG_ENABLE_STATIC)
|
||||
add_executable(crossc src/crosslangcompiler.cpp)
|
||||
add_executable(crossvm src/crosslangvm.cpp)
|
||||
add_executable(crossint src/crosslanginterperter.cpp)
|
||||
add_executable(crossdump src/crosslangdump.cpp)
|
||||
add_executable(crosslang src/crosslang.cpp)
|
||||
target_link_libraries(crossc PUBLIC crosslang_static)
|
||||
target_link_libraries(crossvm PUBLIC crosslang_static)
|
||||
target_link_libraries(crossint PUBLIC crosslang_static)
|
||||
target_link_libraries(crossdump PUBLIC crosslang_static)
|
||||
target_link_libraries(crosslang PUBLIC crosslang_static)
|
||||
#elseif(CROSSLANG_ENABLE_SHARED)
|
||||
#add_executable(crossc src/crosslangcompiler.cpp)
|
||||
#add_executable(crossvm src/crosslangvm.cpp)
|
||||
#add_executable(crossint src/crosslanginterperter.cpp)
|
||||
#target_link_libraries(crossc PUBLIC crosslang_shared)
|
||||
#target_link_libraries(crossvm PUBLIC crosslang_shared)
|
||||
#target_link_libraries(crossint PUBLIC crosslang_shared)
|
||||
else()
|
||||
add_executable(crossc src/crosslangcompiler.cpp ${CROSSLANG_SOURCE})
|
||||
add_executable(crossvm src/crosslangvm.cpp ${CROSSLANG_SOURCE})
|
||||
add_executable(crossint src/crosslanginterperter.cpp ${CROSSLANG_SOURCE})
|
||||
add_executable(crossdump src/crosslangdump.cpp ${CROSSLANG_SOURCE})
|
||||
add_executable(crosslang src/crosslang.cpp ${CROSSLANG_SOURCE})
|
||||
|
||||
CROSSLANG_LINK_DEPS(crossc)
|
||||
CROSSLANG_LINK_DEPS(crossvm)
|
||||
CROSSLANG_LINK_DEPS(crossint)
|
||||
CROSSLANG_LINK_DEPS(crosslang)
|
||||
|
||||
endif()
|
||||
install(TARGETS crossc DESTINATION bin)
|
||||
install(TARGETS crossvm DESTINATION bin)
|
||||
install(TARGETS crossint DESTINATION bin)
|
||||
install(TARGETS crossdump DESTINATION bin)
|
||||
install(TARGETS crosslang DESTINATION bin)
|
||||
endif()
|
||||
|
||||
install(FILES ${PROJECT_SOURCE_DIR}/include/CrossLang.hpp DESTINATION include)
|
||||
|
||||
include(InstallRequiredSystemLibraries)
|
||||
set(CPACK_PACKAGE_CONTACT "Mike Nolan <tesses@tesses.net>")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.md")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "${TessesCrossLang_VERSION_MAJOR}")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "${TessesCrossLang_VERSION_MINOR}")
|
||||
include(CPack)
|
|
@ -0,0 +1,675 @@
|
|||
# GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc.
|
||||
<https://fsf.org/>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
## Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom
|
||||
to share and change all versions of a program--to make sure it remains
|
||||
free software for all its users. We, the Free Software Foundation, use
|
||||
the GNU General Public License for most of our software; it applies
|
||||
also to any other work released this way by its authors. You can apply
|
||||
it to your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you
|
||||
have certain responsibilities if you distribute copies of the
|
||||
software, or if you modify it: responsibilities to respect the freedom
|
||||
of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the
|
||||
manufacturer can do so. This is fundamentally incompatible with the
|
||||
aim of protecting users' freedom to change the software. The
|
||||
systematic pattern of such abuse occurs in the area of products for
|
||||
individuals to use, which is precisely where it is most unacceptable.
|
||||
Therefore, we have designed this version of the GPL to prohibit the
|
||||
practice for those products. If such problems arise substantially in
|
||||
other domains, we stand ready to extend this provision to those
|
||||
domains in future versions of the GPL, as needed to protect the
|
||||
freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish
|
||||
to avoid the special danger that patents applied to a free program
|
||||
could make it effectively proprietary. To prevent this, the GPL
|
||||
assures that patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
## TERMS AND CONDITIONS
|
||||
|
||||
### 0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds
|
||||
of works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of
|
||||
an exact copy. The resulting work is called a "modified version" of
|
||||
the earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user
|
||||
through a computer network, with no transfer of a copy, is not
|
||||
conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices" to
|
||||
the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
### 1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work for
|
||||
making modifications to it. "Object code" means any non-source form of
|
||||
a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users can
|
||||
regenerate automatically from other parts of the Corresponding Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that same
|
||||
work.
|
||||
|
||||
### 2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not convey,
|
||||
without conditions so long as your license otherwise remains in force.
|
||||
You may convey covered works to others for the sole purpose of having
|
||||
them make modifications exclusively for you, or provide you with
|
||||
facilities for running those works, provided that you comply with the
|
||||
terms of this License in conveying all material for which you do not
|
||||
control copyright. Those thus making or running the covered works for
|
||||
you must do so exclusively on your behalf, under your direction and
|
||||
control, on terms that prohibit them from making any copies of your
|
||||
copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under the
|
||||
conditions stated below. Sublicensing is not allowed; section 10 makes
|
||||
it unnecessary.
|
||||
|
||||
### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such
|
||||
circumvention is effected by exercising rights under this License with
|
||||
respect to the covered work, and you disclaim any intention to limit
|
||||
operation or modification of the work as a means of enforcing, against
|
||||
the work's users, your or third parties' legal rights to forbid
|
||||
circumvention of technological measures.
|
||||
|
||||
### 4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
### 5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these
|
||||
conditions:
|
||||
|
||||
- a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
- b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under
|
||||
section 7. This requirement modifies the requirement in section 4
|
||||
to "keep intact all notices".
|
||||
- c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
- d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
### 6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms of
|
||||
sections 4 and 5, provided that you also convey the machine-readable
|
||||
Corresponding Source under the terms of this License, in one of these
|
||||
ways:
|
||||
|
||||
- a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
- b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the Corresponding
|
||||
Source from a network server at no charge.
|
||||
- c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
- d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
- e) Convey the object code using peer-to-peer transmission,
|
||||
provided you inform other peers where the object code and
|
||||
Corresponding Source of the work are being offered to the general
|
||||
public at no charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal,
|
||||
family, or household purposes, or (2) anything designed or sold for
|
||||
incorporation into a dwelling. In determining whether a product is a
|
||||
consumer product, doubtful cases shall be resolved in favor of
|
||||
coverage. For a particular product received by a particular user,
|
||||
"normally used" refers to a typical or common use of that class of
|
||||
product, regardless of the status of the particular user or of the way
|
||||
in which the particular user actually uses, or expects or is expected
|
||||
to use, the product. A product is a consumer product regardless of
|
||||
whether the product has substantial commercial, industrial or
|
||||
non-consumer uses, unless such uses represent the only significant
|
||||
mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to
|
||||
install and execute modified versions of a covered work in that User
|
||||
Product from a modified version of its Corresponding Source. The
|
||||
information must suffice to ensure that the continued functioning of
|
||||
the modified object code is in no case prevented or interfered with
|
||||
solely because modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or
|
||||
updates for a work that has been modified or installed by the
|
||||
recipient, or for the User Product in which it has been modified or
|
||||
installed. Access to a network may be denied when the modification
|
||||
itself materially and adversely affects the operation of the network
|
||||
or violates the rules and protocols for communication across the
|
||||
network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
### 7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders
|
||||
of that material) supplement the terms of this License with terms:
|
||||
|
||||
- a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
- b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
- c) Prohibiting misrepresentation of the origin of that material,
|
||||
or requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
- d) Limiting the use for publicity purposes of names of licensors
|
||||
or authors of the material; or
|
||||
- e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
- f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions
|
||||
of it) with contractual assumptions of liability to the recipient,
|
||||
for any liability that these contractual assumptions directly
|
||||
impose on those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions; the
|
||||
above requirements apply either way.
|
||||
|
||||
### 8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your license
|
||||
from a particular copyright holder is reinstated (a) provisionally,
|
||||
unless and until the copyright holder explicitly and finally
|
||||
terminates your license, and (b) permanently, if the copyright holder
|
||||
fails to notify you of the violation by some reasonable means prior to
|
||||
60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
### 9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or run
|
||||
a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
### 10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
### 11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims owned
|
||||
or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within the
|
||||
scope of its coverage, prohibits the exercise of, or is conditioned on
|
||||
the non-exercise of one or more of the rights that are specifically
|
||||
granted under this License. You may not convey a covered work if you
|
||||
are a party to an arrangement with a third party that is in the
|
||||
business of distributing software, under which you make payment to the
|
||||
third party based on the extent of your activity of conveying the
|
||||
work, and under which the third party grants, to any of the parties
|
||||
who would receive the covered work from you, a discriminatory patent
|
||||
license (a) in connection with copies of the covered work conveyed by
|
||||
you (or copies made from those copies), or (b) primarily for and in
|
||||
connection with specific products or compilations that contain the
|
||||
covered work, unless you entered into that arrangement, or that patent
|
||||
license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
### 12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under
|
||||
this License and any other pertinent obligations, then as a
|
||||
consequence you may not convey it at all. For example, if you agree to
|
||||
terms that obligate you to collect a royalty for further conveying
|
||||
from those to whom you convey the Program, the only way you could
|
||||
satisfy both those terms and this License would be to refrain entirely
|
||||
from conveying the Program.
|
||||
|
||||
### 13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
### 14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in
|
||||
detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies that a certain numbered version of the GNU General Public
|
||||
License "or any later version" applies to it, you have the option of
|
||||
following the terms and conditions either of that numbered version or
|
||||
of any later version published by the Free Software Foundation. If the
|
||||
Program does not specify a version number of the GNU General Public
|
||||
License, you may choose any version ever published by the Free
|
||||
Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future versions
|
||||
of the GNU General Public License can be used, that proxy's public
|
||||
statement of acceptance of a version permanently authorizes you to
|
||||
choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
### 15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT
|
||||
WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
|
||||
PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
|
||||
DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
|
||||
CORRECTION.
|
||||
|
||||
### 16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR
|
||||
CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
|
||||
ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT
|
||||
NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR
|
||||
LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM
|
||||
TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
|
||||
PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
### 17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
## How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these
|
||||
terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest to
|
||||
attach them to the start of each source file to most effectively state
|
||||
the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper
|
||||
mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands \`show w' and \`show c' should show the
|
||||
appropriate parts of the General Public License. Of course, your
|
||||
program's commands might be different; for a GUI interface, you would
|
||||
use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. For more information on this, and how to apply and follow
|
||||
the GNU GPL, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your
|
||||
program into proprietary programs. If your program is a subroutine
|
||||
library, you may consider it more useful to permit linking proprietary
|
||||
applications with the library. If this is what you want to do, use the
|
||||
GNU Lesser General Public License instead of this License. But first,
|
||||
please read <https://www.gnu.org/licenses/why-not-lgpl.html>.
|
|
@ -0,0 +1,12 @@
|
|||
Tesses Cross Language
|
||||
=====================
|
||||
|
||||
<svg width="100" height="100">
|
||||
<polygon points="50,0,100,25,100,75,50,100,0,75,0,25" style="fill:orange;stroke:black;stroke-width:3" />
|
||||
<polygon points="45,20,55,20,55,35,80,35,80,45,55,45,55,85,45,85,45,45,20,45,20,35,45,35" style="fill:white;stroke:black;stroke-width:3"/>
|
||||
</svg>
|
||||
|
||||
```
|
||||
Ye are of God, little children, and have overcome them: because greater is he that is in you, than he that is in the world. (1 John 4:4)
|
||||
```
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,8 @@
|
|||
<svg width="375" height="100">
|
||||
<polygon points="50,0,100,25,100,75,50,100,0,75,0,25" style="fill:orange;stroke:black;stroke-width:3" />
|
||||
<polygon points="45,20,55,20,55,35,80,35,80,45,55,45,55,85,45,85,45,45,20,45,20,35,45,35" style="fill:white;stroke:black;stroke-width:3"/>
|
||||
<polygon points="160,25,160,35,130,35,130,75,160,75,160,85,120,85,120,25" style="fill:orange;stroke:black;stroke-width:3" />
|
||||
<polygon points="180,25,210,25,210,35,190,35,190,85,180,85" style="fill:orange;stroke:black;stroke-width:3" />
|
||||
<polygon points="230,25,230,70,235,85,255,85,260,70,260,25,250,25,250,60,248.5,75,242.5,75,240,60,240,25" style="fill:orange;stroke:black;stroke-width:3" />
|
||||
<polygon points="280,25,280,85,290,85,290,35,310,35,310,85,320,85,320,35,340,35,340,85,350,85,350,25" style="fill:orange;stroke:black;stroke-width:3" />
|
||||
</svg>
|
After Width: | Height: | Size: 851 B |
|
@ -0,0 +1,4 @@
|
|||
<svg width="100" height="100">
|
||||
<polygon points="50,0,100,25,100,75,50,100,0,75,0,25" style="fill:orange;stroke:black;stroke-width:3" />
|
||||
<polygon points="45,20,55,20,55,35,80,35,80,45,55,45,55,85,45,85,45,45,20,45,20,35,45,35" style="fill:white;stroke:black;stroke-width:3"/>
|
||||
</svg>
|
After Width: | Height: | Size: 289 B |
|
@ -0,0 +1,85 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
double BitConverter::ToDoubleBits(uint64_t v)
|
||||
{
|
||||
return *(double*)&v;
|
||||
}
|
||||
uint64_t BitConverter::ToUintBits(double v)
|
||||
{
|
||||
return *(uint64_t*)&v;
|
||||
}
|
||||
double BitConverter::ToDoubleBE(uint8_t& b)
|
||||
{
|
||||
return ToDoubleBits(ToUint64BE(b));
|
||||
}
|
||||
uint64_t BitConverter::ToUint64BE(uint8_t& b)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
uint64_t v = 0;
|
||||
v |= ((uint64_t)b2[0] << 56);
|
||||
v |= ((uint64_t)b2[1] << 48);
|
||||
v |= ((uint64_t)b2[2] << 40);
|
||||
v |= ((uint64_t)b2[3] << 32);
|
||||
v |= ((uint64_t)b2[4] << 24);
|
||||
v |= ((uint64_t)b2[5] << 16);
|
||||
v |= ((uint64_t)b2[6] << 8);
|
||||
v |= (uint64_t)b2[7];
|
||||
return v;
|
||||
}
|
||||
uint32_t BitConverter::ToUint32BE(uint8_t& b)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
uint32_t v = 0;
|
||||
|
||||
v |= ((uint32_t)b2[0] << 24);
|
||||
v |= ((uint32_t)b2[1] << 16);
|
||||
v |= ((uint32_t)b2[2] << 8);
|
||||
v |= (uint32_t)b2[3];
|
||||
return v;
|
||||
}
|
||||
uint16_t BitConverter::ToUint16BE(uint8_t& b)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
uint16_t v = 0;
|
||||
|
||||
|
||||
v |= ((uint16_t)b2[0] << 8);
|
||||
v |= (uint16_t)b2[1];
|
||||
return v;
|
||||
}
|
||||
void BitConverter::FromDoubleBE(uint8_t& b, double v)
|
||||
{
|
||||
FromUint64BE(b,ToUintBits(v));
|
||||
}
|
||||
void BitConverter::FromUint64BE(uint8_t& b, uint64_t v)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
b2[0] = (uint8_t)(v >> 56);
|
||||
b2[1] = (uint8_t)(v >> 48);
|
||||
b2[2] = (uint8_t)(v >> 40);
|
||||
b2[3] = (uint8_t)(v >> 32);
|
||||
b2[4] = (uint8_t)(v >> 24);
|
||||
b2[5] = (uint8_t)(v >> 16);
|
||||
b2[6] = (uint8_t)(v >> 8);
|
||||
b2[7] = (uint8_t)v;
|
||||
}
|
||||
void BitConverter::FromUint32BE(uint8_t& b, uint32_t v)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
|
||||
b2[0] = (uint8_t)(v >> 24);
|
||||
b2[1] = (uint8_t)(v >> 16);
|
||||
b2[2] = (uint8_t)(v >> 8);
|
||||
b2[3] = (uint8_t)v;
|
||||
}
|
||||
void BitConverter::FromUint16BE(uint8_t& b, uint16_t v)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
|
||||
b2[0] = (uint8_t)(v >> 8);
|
||||
b2[1] = (uint8_t)v;
|
||||
}
|
||||
|
||||
};
|
|
@ -0,0 +1,151 @@
|
|||
#include "CrossLang.hpp"
|
||||
#if defined(CROSSLANG_ENABLE_JSON)
|
||||
#include <jansson.h>
|
||||
#endif
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
AdvancedSyntaxNode AdvancedSyntaxNode::Create(std::string_view nodeName,bool isExpression, std::vector<SyntaxNode> nodes)
|
||||
{
|
||||
AdvancedSyntaxNode asn;
|
||||
asn.nodeName = std::string(nodeName);
|
||||
asn.isExpression = isExpression;
|
||||
asn.nodes = nodes;
|
||||
return asn;
|
||||
}
|
||||
|
||||
#if defined(CROSSLANG_ENABLE_JSON)
|
||||
static SyntaxNode Deserialize2(json_t* json)
|
||||
{
|
||||
if(json_is_null(json)) return nullptr;
|
||||
if(json_is_true(json)) return true;
|
||||
if(json_is_false(json)) return false;
|
||||
if(json_is_integer(json)) return (int64_t)json_integer_value(json);
|
||||
if(json_is_real(json)) return json_real_value(json);
|
||||
if(json_is_string(json)) return std::string(json_string_value(json),json_string_length(json));
|
||||
if(json_is_object(json))
|
||||
{
|
||||
json_t* typ = json_object_get(json, "type");
|
||||
if(json_is_string(typ))
|
||||
{
|
||||
std::string type(json_string_value(typ),json_string_length(typ));
|
||||
if(type == CharExpression)
|
||||
{
|
||||
json_t* chrData = json_object_get(json,"value");
|
||||
if(json_is_integer(chrData))
|
||||
{
|
||||
return (char)(uint8_t)json_integer_value(chrData);
|
||||
}
|
||||
}
|
||||
if(type == UndefinedExpression)
|
||||
{
|
||||
return Undefined();
|
||||
}
|
||||
json_t* isExpr = json_object_get(json,"isExpression");
|
||||
bool isExprB = json_is_true(isExpr);
|
||||
AdvancedSyntaxNode asn;
|
||||
asn.nodeName = type;
|
||||
asn.isExpression = isExprB;
|
||||
json_t* args = json_object_get(json, "args");
|
||||
if(json_is_array(args))
|
||||
{
|
||||
size_t index;
|
||||
json_t* value;
|
||||
|
||||
json_array_foreach(args, index, value)
|
||||
{
|
||||
asn.nodes.push_back(Deserialize2(value));
|
||||
}
|
||||
}
|
||||
return asn;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
SyntaxNode Deserialize(std::string astData)
|
||||
{
|
||||
#if defined(CROSSLANG_ENABLE_JSON)
|
||||
json_t* json = json_loadb(astData.c_str(),astData.size(), 0, NULL);
|
||||
|
||||
auto r = Deserialize2(json);
|
||||
json_decref(json);
|
||||
|
||||
return r;
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
#if defined(CROSSLANG_ENABLE_JSON)
|
||||
static json_t* Serialize2(SyntaxNode node)
|
||||
{
|
||||
if(std::holds_alternative<std::nullptr_t>(node))
|
||||
return json_null();
|
||||
if(std::holds_alternative<int64_t>(node))
|
||||
{
|
||||
return json_integer(std::get<int64_t>(node));
|
||||
}
|
||||
if(std::holds_alternative<double>(node))
|
||||
{
|
||||
return json_real(std::get<double>(node));
|
||||
}
|
||||
if(std::holds_alternative<bool>(node))
|
||||
{
|
||||
return json_boolean(std::get<bool>(node));
|
||||
}
|
||||
if(std::holds_alternative<std::string>(node))
|
||||
{
|
||||
std::string str = std::get<std::string>(node);
|
||||
return json_stringn(str.c_str(),str.size());
|
||||
}
|
||||
if(std::holds_alternative<char>(node))
|
||||
{
|
||||
char c = std::get<char>(node);
|
||||
json_t* json = json_object();
|
||||
json_object_set_new(json,"type",json_string(CharExpression.begin()));
|
||||
json_object_set_new(json, "value", json_integer((uint8_t)c));
|
||||
return json;
|
||||
}
|
||||
if(std::holds_alternative<Undefined>(node))
|
||||
{
|
||||
json_t* json = json_object();
|
||||
json_object_set_new(json,"type",json_string(UndefinedExpression.begin()));
|
||||
return json;
|
||||
}
|
||||
|
||||
|
||||
if(std::holds_alternative<AdvancedSyntaxNode>(node))
|
||||
{
|
||||
auto asn = std::get<AdvancedSyntaxNode>(node);
|
||||
json_t* j = json_object();
|
||||
json_object_set_new(j, "type", json_string(asn.nodeName.c_str()));
|
||||
json_object_set_new(j, "isExpression",json_boolean(asn.isExpression));
|
||||
json_t* arr=json_array();
|
||||
for(auto item : asn.nodes)
|
||||
{
|
||||
json_array_append_new(arr,Serialize2(item));
|
||||
}
|
||||
json_object_set_new(j, "args", arr);
|
||||
return j;
|
||||
}
|
||||
|
||||
return json_null();
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string Serialize(SyntaxNode node)
|
||||
{
|
||||
|
||||
#if defined(CROSSLANG_ENABLE_JSON)
|
||||
json_t* json = Serialize2(node);
|
||||
char* txt = json_dumps(json,0);
|
||||
std::string str(txt);
|
||||
free(json);
|
||||
json_decref(json);
|
||||
return str;
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,687 @@
|
|||
#include "CrossLang.hpp"
|
||||
#include <iostream>
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
std::string EscapeString(std::string text,bool quote)
|
||||
{
|
||||
std::string str={};
|
||||
if(quote) str.push_back('\"');
|
||||
for(auto item : text)
|
||||
{
|
||||
if(item == '\\' || item == '\"' || item == '\'')
|
||||
{
|
||||
str.push_back('\\');
|
||||
str.push_back(item);
|
||||
}
|
||||
else if(item == '\n')
|
||||
{
|
||||
str.push_back('\\');
|
||||
str.push_back('n');
|
||||
}
|
||||
else if(item == '\r')
|
||||
{
|
||||
str.push_back('\\');
|
||||
str.push_back('r');
|
||||
}
|
||||
else if(item == '\t')
|
||||
{
|
||||
str.push_back('\\');
|
||||
str.push_back('t');
|
||||
}
|
||||
else if(item == '\f')
|
||||
{
|
||||
str.push_back('\\');
|
||||
str.push_back('f');
|
||||
}
|
||||
else if(item == '\0')
|
||||
{
|
||||
str.push_back('\\');
|
||||
str.push_back('0');
|
||||
}
|
||||
else if(item == '\b')
|
||||
{
|
||||
str.push_back('\\');
|
||||
str.push_back('b');
|
||||
}
|
||||
else if(item == '\a')
|
||||
{
|
||||
str.push_back('\\');
|
||||
str.push_back('a');
|
||||
}
|
||||
else if(item == '\v')
|
||||
{
|
||||
str.push_back('\\');
|
||||
str.push_back('v');
|
||||
}
|
||||
else if(item == '\e')
|
||||
{
|
||||
str.push_back('\\');
|
||||
str.push_back('e');
|
||||
}
|
||||
else if((uint8_t)item < 32 || (uint8_t)item > 126)
|
||||
{
|
||||
str.append("\\x");
|
||||
str.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(((uint8_t)item >> 4)&0x0F));
|
||||
str.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex((uint8_t)item & 0x0F));
|
||||
}
|
||||
else
|
||||
{
|
||||
str.push_back(item);
|
||||
}
|
||||
}
|
||||
|
||||
if(quote) str.push_back('\"');
|
||||
return str;
|
||||
}
|
||||
|
||||
void LexTokenLineInfo::Subtract(size_t len)
|
||||
{
|
||||
this->offset -= len;
|
||||
this->column -= len;
|
||||
}
|
||||
void LexTokenLineInfo::Add(int c)
|
||||
{
|
||||
this->offset++;
|
||||
switch(c)
|
||||
{
|
||||
case ' ':
|
||||
this->column++;
|
||||
break;
|
||||
case '\n':
|
||||
this->column=1;
|
||||
this->line++;
|
||||
break;
|
||||
case '\t':
|
||||
this->column += 4;
|
||||
break;
|
||||
case '\r':
|
||||
this->column++;
|
||||
break;
|
||||
default:
|
||||
this->column++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int Lex(std::string filename, std::istream& strm, std::vector<LexToken>& tokens)
|
||||
{
|
||||
int _peeked=-1;
|
||||
|
||||
auto Read = [&_peeked,&strm]()->int {
|
||||
if(_peeked > -1)
|
||||
{
|
||||
int _peek2 = _peeked;
|
||||
_peeked=-1;
|
||||
return _peek2;
|
||||
}
|
||||
uint8_t b;
|
||||
|
||||
strm.read((char*)&b,1);
|
||||
if(strm.eof()) return -1;
|
||||
return b;
|
||||
};
|
||||
|
||||
auto Peek = [&_peeked,Read]()->int {
|
||||
if(_peeked > -1) return _peeked;
|
||||
_peeked = Read();
|
||||
return _peeked;
|
||||
};
|
||||
|
||||
int read;
|
||||
int peek;
|
||||
|
||||
std::string buffer={};
|
||||
|
||||
LexTokenLineInfo lineInfo;
|
||||
|
||||
lineInfo.filename = filename;
|
||||
lineInfo.column = 1;
|
||||
lineInfo.line = 1;
|
||||
lineInfo.offset = 0;
|
||||
|
||||
|
||||
|
||||
auto Flush = [&buffer,&tokens,&lineInfo]() -> void {
|
||||
if(!buffer.empty())
|
||||
{
|
||||
LexToken token;
|
||||
token.text = buffer;
|
||||
token.type = LexTokenType::Identifier;
|
||||
token.lineInfo = lineInfo;
|
||||
token.lineInfo.Subtract(buffer.size());
|
||||
tokens.push_back(token);
|
||||
buffer.clear();
|
||||
}
|
||||
};
|
||||
|
||||
auto Symbol = [&tokens,&lineInfo](std::initializer_list<int> chrs)-> void {
|
||||
LexToken token;
|
||||
|
||||
token.type = LexTokenType::Symbol;
|
||||
token.lineInfo = lineInfo;
|
||||
|
||||
token.text.reserve(chrs.size());
|
||||
|
||||
for(auto i : chrs)
|
||||
token.text.push_back((char)i);
|
||||
|
||||
tokens.push_back(token);
|
||||
};
|
||||
|
||||
auto ReadChr = [&lineInfo, &strm, Read]() -> std::pair<int,bool> {
|
||||
int read=Read();
|
||||
lineInfo.Add(read);
|
||||
|
||||
if(read == -1)
|
||||
{
|
||||
|
||||
return std::pair<int,bool>(-1,false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(read == '\\')
|
||||
{
|
||||
read = Read();
|
||||
lineInfo.Add(read);
|
||||
if(read == -1)
|
||||
{
|
||||
return std::pair<int,bool>(-1,true);
|
||||
}
|
||||
else if(read == 'n')
|
||||
{
|
||||
return std::pair<int,bool>('\n',true);
|
||||
}
|
||||
else if(read == 'r')
|
||||
{
|
||||
return std::pair<int,bool>('\r',true);
|
||||
}
|
||||
else if(read == 'f')
|
||||
{
|
||||
return std::pair<int,bool>('\f',true);
|
||||
}
|
||||
else if(read == 'b')
|
||||
{
|
||||
return std::pair<int,bool>('\b',true);
|
||||
}
|
||||
else if(read == 'a')
|
||||
{
|
||||
return std::pair<int,bool>('\a',true);
|
||||
}
|
||||
else if(read == '0')
|
||||
{
|
||||
return std::pair<int,bool>('\0',true);
|
||||
}
|
||||
else if(read == 'v')
|
||||
{
|
||||
return std::pair<int,bool>('\v',true);
|
||||
}
|
||||
else if(read == 'e')
|
||||
{
|
||||
return std::pair<int,bool>('\e',true);
|
||||
}
|
||||
else if(read == 't')
|
||||
{
|
||||
return std::pair<int,bool>('\t',true);
|
||||
}
|
||||
else if(read == 'x')
|
||||
{
|
||||
int r1 = Read();
|
||||
lineInfo.Add(r1);
|
||||
if(r1 == -1)
|
||||
{
|
||||
return std::pair<int,bool>(-1,true);
|
||||
}
|
||||
int r2 = Read();
|
||||
lineInfo.Add(r2);
|
||||
if(r2 == -1)
|
||||
{
|
||||
return std::pair<int,bool>(-1,true);
|
||||
}
|
||||
|
||||
uint8_t c = (uint8_t)std::stoi(std::string{(char)r1,(char)r2},nullptr,16);
|
||||
|
||||
return std::pair<int,bool>(c,true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::pair<int,bool>(read,true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::pair<int,bool>(read,false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
auto ParseString = [&lineInfo, &strm, Read, ReadChr,&tokens](bool interopolated)->int {
|
||||
auto lI = lineInfo;
|
||||
|
||||
std::string b={};
|
||||
|
||||
|
||||
auto rChr = ReadChr();
|
||||
lineInfo.Add(rChr.first);
|
||||
|
||||
while(rChr.first != '\"' || rChr.second)
|
||||
{
|
||||
if(rChr.first == -1) return lineInfo.line;
|
||||
|
||||
b.push_back((char)rChr.first);
|
||||
rChr = ReadChr();
|
||||
lineInfo.Add(rChr.first);
|
||||
}
|
||||
|
||||
if(interopolated)
|
||||
{
|
||||
int e = 0;
|
||||
int escapeI = 0;
|
||||
std::string b2 = {};
|
||||
|
||||
for(size_t i = 0; i< b.size();i++)
|
||||
{
|
||||
if(b[i] == '{')
|
||||
{
|
||||
if((i+1 < b.size() && b[i+1] != '{') || escapeI >= 1)
|
||||
{
|
||||
if(b2.size() > 0 && escapeI < 1)
|
||||
{
|
||||
if(e > 0)
|
||||
{
|
||||
LexToken _tkn;
|
||||
_tkn.type = LexTokenType::Symbol;
|
||||
_tkn.text = "+";
|
||||
_tkn.lineInfo = lI;
|
||||
tokens.push_back(_tkn);
|
||||
}
|
||||
LexToken _tkn2;
|
||||
_tkn2.type = LexTokenType::String;
|
||||
_tkn2.text = b2;
|
||||
_tkn2.lineInfo = lI;
|
||||
tokens.push_back(_tkn2);
|
||||
b2.clear();
|
||||
e++;
|
||||
|
||||
}
|
||||
escapeI++;
|
||||
if(escapeI > 1)
|
||||
{
|
||||
b2.push_back('{');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
b2.push_back('{');
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if(b[i] == '}')
|
||||
{
|
||||
if(escapeI >= 1)
|
||||
{
|
||||
escapeI--;
|
||||
if(b2.size() > 0 && escapeI == 0)
|
||||
{
|
||||
if(e > 0)
|
||||
{
|
||||
LexToken _tkn;
|
||||
_tkn.type = LexTokenType::Symbol;
|
||||
_tkn.text = "+";
|
||||
_tkn.lineInfo = lI;
|
||||
tokens.push_back(_tkn);
|
||||
}
|
||||
LexToken _tkn2;
|
||||
_tkn2.type = LexTokenType::Symbol;
|
||||
_tkn2.text = "(";
|
||||
_tkn2.lineInfo = lI;
|
||||
tokens.push_back(_tkn2);
|
||||
std::stringstream strm2(b2,std::ios_base::in | std::ios_base::binary);
|
||||
int res = Lex("lexGen", strm2, tokens);
|
||||
if(res != 0) return res;
|
||||
|
||||
_tkn2.text = ")";
|
||||
tokens.push_back(_tkn2);
|
||||
|
||||
_tkn2.text = ".";
|
||||
tokens.push_back(_tkn2);
|
||||
_tkn2.type = LexTokenType::Identifier;
|
||||
_tkn2.text = "ToString";
|
||||
tokens.push_back(_tkn2);
|
||||
_tkn2.type = LexTokenType::Symbol;
|
||||
_tkn2.text = "(";
|
||||
tokens.push_back(_tkn2);
|
||||
_tkn2.text = ")";
|
||||
tokens.push_back(_tkn2);
|
||||
b2.clear();
|
||||
e++;
|
||||
}
|
||||
if(escapeI >= 1)
|
||||
{
|
||||
b2.push_back('}');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
b2.push_back(b[i]);
|
||||
}
|
||||
}
|
||||
if(b2.size() > 0)
|
||||
{
|
||||
if(escapeI > 0)
|
||||
{
|
||||
if(e > 0)
|
||||
{
|
||||
LexToken _tkn;
|
||||
_tkn.type = LexTokenType::Symbol;
|
||||
_tkn.text = "+";
|
||||
_tkn.lineInfo = lI;
|
||||
tokens.push_back(_tkn);
|
||||
}
|
||||
LexToken _tkn2;
|
||||
_tkn2.type = LexTokenType::Symbol;
|
||||
_tkn2.text = "(";
|
||||
_tkn2.lineInfo = lI;
|
||||
tokens.push_back(_tkn2);
|
||||
std::stringstream strm2(b2,std::ios_base::in | std::ios_base::binary);
|
||||
int res = Lex("lexGen", strm2, tokens);
|
||||
if(res != 0) return res;
|
||||
|
||||
_tkn2.text = ")";
|
||||
tokens.push_back(_tkn2);
|
||||
|
||||
_tkn2.text = ".";
|
||||
tokens.push_back(_tkn2);
|
||||
_tkn2.type = LexTokenType::Identifier;
|
||||
_tkn2.text = "ToString";
|
||||
tokens.push_back(_tkn2);
|
||||
_tkn2.type = LexTokenType::Symbol;
|
||||
_tkn2.text = "(";
|
||||
tokens.push_back(_tkn2);
|
||||
_tkn2.text = ")";
|
||||
tokens.push_back(_tkn2);
|
||||
b2.clear();
|
||||
e++;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if(e > 0)
|
||||
{
|
||||
LexToken _tkn;
|
||||
_tkn.type = LexTokenType::Symbol;
|
||||
_tkn.text = "+";
|
||||
_tkn.lineInfo = lI;
|
||||
tokens.push_back(_tkn);
|
||||
}
|
||||
LexToken _tkn2;
|
||||
_tkn2.type = LexTokenType::String;
|
||||
_tkn2.text = b2;
|
||||
_tkn2.lineInfo = lI;
|
||||
tokens.push_back(_tkn2);
|
||||
b2.clear();
|
||||
e++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LexToken _tkn2;
|
||||
_tkn2.type = LexTokenType::String;
|
||||
_tkn2.text = b;
|
||||
_tkn2.lineInfo = lI;
|
||||
tokens.push_back(_tkn2);
|
||||
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
while((read = Read()) != -1)
|
||||
{
|
||||
|
||||
peek = Peek();
|
||||
|
||||
switch(read)
|
||||
{
|
||||
case '$':
|
||||
if(peek == '\"')
|
||||
{
|
||||
Flush();
|
||||
lineInfo.Add(Read());
|
||||
int re = ParseString(true);
|
||||
if(re != 0) return re;
|
||||
}
|
||||
else
|
||||
{
|
||||
Flush();
|
||||
Symbol({read});
|
||||
}
|
||||
break;
|
||||
case '\"':
|
||||
{
|
||||
Flush();
|
||||
int re = ParseString(false);
|
||||
if(re != 0) return re;
|
||||
}
|
||||
break;
|
||||
case '\'':
|
||||
{
|
||||
Flush();
|
||||
auto res = ReadChr();
|
||||
if(res.first == -1) return lineInfo.line;
|
||||
int r = Read();
|
||||
lineInfo.Add(r);
|
||||
if(r != '\'')
|
||||
return lineInfo.line;
|
||||
LexToken token;
|
||||
token.text = {(char)(uint8_t)res.first};
|
||||
token.lineInfo = lineInfo;
|
||||
token.type = LexTokenType::Char;
|
||||
|
||||
tokens.push_back(token);
|
||||
}
|
||||
break;
|
||||
case '#':
|
||||
Flush();
|
||||
while(true)
|
||||
{
|
||||
int r = Read();
|
||||
lineInfo.Add(r);
|
||||
if(r == '\n' || r == -1) break;
|
||||
}
|
||||
break;
|
||||
case '/':
|
||||
if(peek == '/')
|
||||
{
|
||||
Flush();
|
||||
while(true)
|
||||
{
|
||||
int r = Read();
|
||||
lineInfo.Add(r);
|
||||
if(r == '\n' || r == -1) break;
|
||||
}
|
||||
}
|
||||
else if(peek == '*')
|
||||
{
|
||||
Flush();
|
||||
while(true)
|
||||
{
|
||||
int r = Read();
|
||||
lineInfo.Add(r);
|
||||
if(r == -1)
|
||||
{
|
||||
return lineInfo.line;
|
||||
}
|
||||
if(r == '*')
|
||||
{
|
||||
r = Read();
|
||||
lineInfo.Add(r);
|
||||
if(r == -1)
|
||||
return lineInfo.line;
|
||||
if(r == '/')
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(peek == '^')
|
||||
{
|
||||
Flush();
|
||||
lineInfo.Add(Read());
|
||||
std::string str={};
|
||||
while(true)
|
||||
{
|
||||
int r = Read();
|
||||
std::cout << r <<std::endl;
|
||||
lineInfo.Add(r);
|
||||
if(r == -1)
|
||||
{
|
||||
return lineInfo.line;
|
||||
}
|
||||
if(r == '^')
|
||||
{
|
||||
r = Read();
|
||||
lineInfo.Add(r);
|
||||
if(r == -1)
|
||||
return lineInfo.line;
|
||||
if(r == '^')
|
||||
{
|
||||
str.push_back('^');
|
||||
continue;
|
||||
}
|
||||
if(r == '/')
|
||||
break;
|
||||
str.push_back('^');
|
||||
}
|
||||
str.push_back((char)r);
|
||||
}
|
||||
LexToken token;
|
||||
|
||||
token.type = LexTokenType::Documentation;
|
||||
token.lineInfo = lineInfo;
|
||||
token.text = str;
|
||||
tokens.push_back(token);
|
||||
}
|
||||
else if(peek == '=')
|
||||
{
|
||||
Flush();
|
||||
lineInfo.Add(Read());
|
||||
Symbol({read,peek});
|
||||
}
|
||||
else
|
||||
{
|
||||
Flush();
|
||||
Symbol({read});
|
||||
}
|
||||
break;
|
||||
case '<':
|
||||
case '>':
|
||||
if(peek == read)
|
||||
{
|
||||
Flush();
|
||||
lineInfo.Add(Read());
|
||||
int peek2=Peek();
|
||||
if(peek2 == '=')
|
||||
{
|
||||
lineInfo.Add(Read());
|
||||
Symbol({read,peek,peek2});
|
||||
}
|
||||
else
|
||||
{
|
||||
Symbol({read,peek});
|
||||
}
|
||||
}
|
||||
else if(peek == '=')
|
||||
{
|
||||
Flush();
|
||||
lineInfo.Add(Read());
|
||||
Symbol({read,peek});
|
||||
}
|
||||
else
|
||||
{
|
||||
Flush();
|
||||
Symbol({read});
|
||||
}
|
||||
break;
|
||||
case '+':
|
||||
case '-':
|
||||
case '|':
|
||||
case '&':
|
||||
if(peek == '=' || peek == read)
|
||||
{
|
||||
Flush();
|
||||
lineInfo.Add(Read());
|
||||
Symbol({read,peek});
|
||||
}
|
||||
else
|
||||
{
|
||||
Flush();
|
||||
Symbol({read});
|
||||
}
|
||||
break;
|
||||
case '=':
|
||||
if(peek == '>')
|
||||
{
|
||||
Flush();
|
||||
lineInfo.Add(Read());
|
||||
Symbol({read,peek});
|
||||
}
|
||||
else if(peek == '=')
|
||||
{
|
||||
Flush();
|
||||
lineInfo.Add(Read());
|
||||
Symbol({read,peek});
|
||||
}
|
||||
else
|
||||
{
|
||||
Flush();
|
||||
Symbol({read});
|
||||
}
|
||||
break;
|
||||
case '^':
|
||||
case '~':
|
||||
case '!':
|
||||
case '*':
|
||||
case '%':
|
||||
//*
|
||||
//*=
|
||||
if(peek == '=')
|
||||
{
|
||||
Flush();
|
||||
lineInfo.Add(Read());
|
||||
Symbol({read,peek});
|
||||
}
|
||||
else
|
||||
{
|
||||
Flush();
|
||||
Symbol({read});
|
||||
}
|
||||
break;
|
||||
case '(':
|
||||
case ')':
|
||||
case '[':
|
||||
case ']':
|
||||
case '{':
|
||||
case '}':
|
||||
case '.':
|
||||
case ':':
|
||||
case ';':
|
||||
case ',':
|
||||
case '?':
|
||||
Flush();
|
||||
Symbol({read});
|
||||
break;
|
||||
case '\n':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case ' ':
|
||||
Flush();
|
||||
|
||||
break;
|
||||
default:
|
||||
buffer.push_back((char)read);
|
||||
break;
|
||||
}
|
||||
|
||||
lineInfo.Add(read);
|
||||
}
|
||||
Flush();
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,750 @@
|
|||
#include "CrossLang.hpp"
|
||||
#include <iostream>
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
std::string LexTokenType_ToString(LexTokenType t)
|
||||
{
|
||||
switch(t)
|
||||
{
|
||||
case LexTokenType::Char:
|
||||
return "char";
|
||||
case LexTokenType::Documentation:
|
||||
return "documentation";
|
||||
case LexTokenType::Identifier:
|
||||
return "identifier";
|
||||
case LexTokenType::String:
|
||||
return "string";
|
||||
case LexTokenType::Symbol:
|
||||
return "symbol";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
bool Parser::IsAnyIdentifier(std::initializer_list<std::string> idents, bool pop)
|
||||
{
|
||||
if(i < tokens.size())
|
||||
{
|
||||
if(tokens[i].type != LexTokenType::Identifier) return false;
|
||||
for(auto item : idents)
|
||||
{
|
||||
if(item == tokens[i].text)
|
||||
{
|
||||
tkn = tokens[i];
|
||||
if(pop) i++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool Parser::IsIdentifier(std::string txt,bool pop)
|
||||
{
|
||||
if(i < tokens.size())
|
||||
{
|
||||
if(tokens[i].type != LexTokenType::Identifier) return false;
|
||||
if(tokens[i].text == txt)
|
||||
{
|
||||
tkn = tokens[i];
|
||||
if(pop) i++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Parser::IsAnySymbol(std::initializer_list<std::string> idents, bool pop)
|
||||
{
|
||||
if(i < tokens.size())
|
||||
{
|
||||
if(tokens[i].type != LexTokenType::Symbol) return false;
|
||||
for(auto item : idents)
|
||||
{
|
||||
if(item == tokens[i].text)
|
||||
{
|
||||
tkn = tokens[i];
|
||||
if(pop) i++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool Parser::IsSymbol(std::string txt,bool pop)
|
||||
{
|
||||
if(i < tokens.size())
|
||||
{
|
||||
if(tokens[i].type != LexTokenType::Symbol) return false;
|
||||
if(tokens[i].text == txt)
|
||||
{
|
||||
tkn = tokens[i];
|
||||
if(pop) i++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void Parser::EnsureSymbol(std::string txt)
|
||||
{
|
||||
if(i < tokens.size())
|
||||
{
|
||||
if(tokens[i].type != LexTokenType::Symbol)
|
||||
{
|
||||
throw SyntaxException(tokens[i].lineInfo, "expected the symbol \"" + txt + "\" but got the " + LexTokenType_ToString(tokens[i].type) + " \"" + tokens[i].text + "\" which is not a symbol at all.");
|
||||
}
|
||||
if(tokens[i].text != txt)
|
||||
{
|
||||
|
||||
throw SyntaxException(tokens[i].lineInfo, "expected the symbol \"" + txt + "\" but got the symbol \"" + tokens[i].text + "\"");
|
||||
}
|
||||
tkn = tokens[i];
|
||||
i++;
|
||||
return;
|
||||
}
|
||||
throw std::out_of_range("End of file");
|
||||
}
|
||||
Parser::Parser(std::vector<LexToken> tokens)
|
||||
{
|
||||
this->i = 0;
|
||||
this->tokens = tokens;
|
||||
}
|
||||
SyntaxNode Parser::ParseValue()
|
||||
{
|
||||
if(i >= tokens.size()) throw std::out_of_range("End of file");
|
||||
auto tkn2 = tokens[i];
|
||||
SyntaxNode node = nullptr;
|
||||
if(tokens[i].type == LexTokenType::String)
|
||||
{
|
||||
node = tkn2.text;
|
||||
i++;
|
||||
|
||||
}
|
||||
else if(tokens[i].type == LexTokenType::Char)
|
||||
{
|
||||
node = tkn2.text.empty() ? '\0' : tkn2.text.front();
|
||||
i++;
|
||||
|
||||
}
|
||||
else if(IsSymbol("["))
|
||||
{
|
||||
if(IsSymbol("]",false))
|
||||
node = AdvancedSyntaxNode::Create(ArrayExpression, true, {});
|
||||
else
|
||||
node = AdvancedSyntaxNode::Create(ArrayExpression, true, {ParseExpression()});
|
||||
EnsureSymbol("]");
|
||||
}
|
||||
else if(IsSymbol("{"))
|
||||
{
|
||||
if(IsSymbol("}",false))
|
||||
node = AdvancedSyntaxNode::Create(DictionaryExpression,true,{});
|
||||
else
|
||||
node = AdvancedSyntaxNode::Create(DictionaryExpression,true,{ParseExpression()});
|
||||
EnsureSymbol("}");
|
||||
}
|
||||
else if(IsSymbol("("))
|
||||
{
|
||||
if(IsSymbol(")",false))
|
||||
node = AdvancedSyntaxNode::Create(ParenthesesExpression,true,{});
|
||||
else
|
||||
node = AdvancedSyntaxNode::Create(ParenthesesExpression,true,{ParseExpression()});
|
||||
EnsureSymbol(")");
|
||||
}
|
||||
else if(IsIdentifier("var"))
|
||||
{
|
||||
if(i >= tokens.size()) throw std::out_of_range("End of file");
|
||||
auto variable = tokens[i];
|
||||
i++;
|
||||
if(variable.type != LexTokenType::Identifier) throw SyntaxException(variable.lineInfo, "Expected an identifier got a " + LexTokenType_ToString(variable.type) + " \"" + variable.text + "\"");
|
||||
node = AdvancedSyntaxNode::Create(DeclareExpression,true,{variable.text});
|
||||
}
|
||||
else if(IsIdentifier("operator"))
|
||||
{
|
||||
if(i >= tokens.size()) throw std::out_of_range("End of file");
|
||||
auto variable = tokens[i];
|
||||
i++;
|
||||
if(variable.type != LexTokenType::Identifier && variable.type != LexTokenType::Symbol) throw SyntaxException(variable.lineInfo, "Expected an identifier or a symbol got a " + LexTokenType_ToString(variable.type) + " \"" + variable.text + "\"");
|
||||
node = AdvancedSyntaxNode::Create(GetVariableExpression, true,{"operator"+variable.text});
|
||||
|
||||
}
|
||||
else if(IsIdentifier("embed"))
|
||||
{
|
||||
EnsureSymbol("(");
|
||||
if(i >= tokens.size()) throw std::out_of_range("End of file");
|
||||
auto embed = tokens[i];
|
||||
i++;
|
||||
if(embed.type != LexTokenType::String) throw SyntaxException(embed.lineInfo, "Expected an string for embed got a " + LexTokenType_ToString(embed.type) + " \"" + embed.text + "\"");
|
||||
EnsureSymbol(")");
|
||||
node = AdvancedSyntaxNode::Create(EmbedExpression, true,{embed.text});
|
||||
}
|
||||
else if(tokens[i].type == LexTokenType::Identifier)
|
||||
{
|
||||
std::string token = tokens[i].text;
|
||||
|
||||
i++;
|
||||
|
||||
bool hasNumber=true;
|
||||
int64_t lngNum = 0;
|
||||
|
||||
if(token.size() == 1 && token[0] == '0')
|
||||
{
|
||||
lngNum = 0;
|
||||
}
|
||||
else
|
||||
if(token.size() > 0 && token[0] == '0')
|
||||
{
|
||||
if(token.size() > 1 && token[1] == 'x')
|
||||
{
|
||||
lngNum = std::stoll(token.substr(2),nullptr,16);
|
||||
}
|
||||
else if(token.size() > 1 && token[1] == 'b')
|
||||
{
|
||||
lngNum = std::stoll(token.substr(2),nullptr,2);
|
||||
}
|
||||
else
|
||||
{
|
||||
lngNum = std::stoll(token.substr(1),nullptr,8);
|
||||
}
|
||||
|
||||
}
|
||||
else if(token.size() > 0 && token[0] >= '0' && token[0] <= '9')
|
||||
{
|
||||
lngNum=std::stoll(token,nullptr,10);
|
||||
}
|
||||
else
|
||||
{
|
||||
hasNumber = false;
|
||||
}
|
||||
|
||||
if(hasNumber && this->IsSymbol(".",false) && i+1 < tokens.size() && tokens[i+1].type == LexTokenType::Identifier)
|
||||
{
|
||||
std::string myToken = tokens[i+1].text;
|
||||
if(myToken.size() > 0 && myToken[0] >= '0' && myToken[0] <= '9')
|
||||
{
|
||||
i+=2;
|
||||
std::string myN = std::to_string(lngNum) + "." + myToken;
|
||||
|
||||
double v = std::stod(myN,nullptr);
|
||||
|
||||
node = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = lngNum;
|
||||
}
|
||||
}
|
||||
else if(hasNumber)
|
||||
{
|
||||
|
||||
node = lngNum;
|
||||
}
|
||||
|
||||
if(!hasNumber)
|
||||
{
|
||||
if(token == "true")
|
||||
node = true;
|
||||
else if(token == "false")
|
||||
node = false;
|
||||
else if(token == "null")
|
||||
node = nullptr;
|
||||
else if(token == "undefined")
|
||||
node = Undefined();
|
||||
else {
|
||||
node = AdvancedSyntaxNode::Create(GetVariableExpression,true,{token});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(IsSymbol("=>"))
|
||||
{
|
||||
if(IsSymbol("{",false))
|
||||
{
|
||||
node = AdvancedSyntaxNode::Create(ClosureExpression,true,{node,ParseNode()});
|
||||
}
|
||||
else
|
||||
{
|
||||
node = AdvancedSyntaxNode::Create(ClosureExpression,true,{node, AdvancedSyntaxNode::Create(ReturnStatement,false,{ParseExpression()})});
|
||||
}
|
||||
}
|
||||
while(IsAnySymbol({".","[","("}))
|
||||
{
|
||||
if(tkn.text == ".")
|
||||
{
|
||||
if(i>=tokens.size()) throw std::out_of_range("End of file");
|
||||
if(tokens[i].type != LexTokenType::Identifier) throw std::exception();
|
||||
std::string name = tokens[i].text;
|
||||
if(name == "operator")
|
||||
{
|
||||
if(i >= tokens.size()) throw std::out_of_range("End of file");
|
||||
auto op = tokens[i];
|
||||
if(op.type != LexTokenType::Identifier && op.type != LexTokenType::Symbol) throw SyntaxException(op.lineInfo, "Expected an identifier or a symbol got a " + LexTokenType_ToString(op.type) + " \"" + op.text + "\"");
|
||||
|
||||
name += tokens[i+1].text;
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
node = AdvancedSyntaxNode::Create(GetFieldExpression, true, {node, name});
|
||||
}
|
||||
else if(tkn.text == "[")
|
||||
{
|
||||
node = AdvancedSyntaxNode::Create(GetArrayExpression,true,{node,ParseExpression()});
|
||||
EnsureSymbol("]");
|
||||
}
|
||||
else if(tkn.text == "(")
|
||||
{
|
||||
if(IsSymbol(")",false))
|
||||
{
|
||||
node = AdvancedSyntaxNode::Create(FunctionCallExpression,true,{node});
|
||||
}
|
||||
else
|
||||
{
|
||||
node = AdvancedSyntaxNode::Create(FunctionCallExpression,true,{node,ParseExpression()});
|
||||
|
||||
}
|
||||
EnsureSymbol(")");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(IsSymbol("++"))
|
||||
{
|
||||
node = AdvancedSyntaxNode::Create(PostfixIncrementExpression,true,{node});
|
||||
}
|
||||
else if(IsSymbol("--"))
|
||||
{
|
||||
node = AdvancedSyntaxNode::Create(PostfixDecrementExpression,true,{node});
|
||||
}
|
||||
|
||||
|
||||
return node;
|
||||
}
|
||||
SyntaxNode Parser::ParseUnary()
|
||||
{
|
||||
if(IsSymbol("-"))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(NegativeExpression,true,{ParseUnary()});
|
||||
}
|
||||
else if(IsSymbol("!"))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(NotExpression,true,{ParseUnary()});
|
||||
}
|
||||
else if(IsSymbol("~"))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(BitwiseNotExpression,true,{ParseUnary()});
|
||||
}
|
||||
else if(IsSymbol("++"))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(PrefixIncrementExpression,true,{ParseUnary()});
|
||||
}
|
||||
else if(IsSymbol("--"))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(PrefixDecrementExpression,true,{ParseUnary()});
|
||||
}
|
||||
return ParseValue();
|
||||
}
|
||||
|
||||
SyntaxNode Parser::ParseFactor()
|
||||
{
|
||||
SyntaxNode expr = ParseUnary();
|
||||
while(IsAnySymbol({"*","/","%"},true))
|
||||
{
|
||||
if(tkn.text == "*")
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(TimesExpression, true, {expr,ParseUnary()});
|
||||
}
|
||||
else if(tkn.text == "/")
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(DivideExpression, true, {expr,ParseUnary()});
|
||||
}
|
||||
else if(tkn.text == "%")
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(ModExpression, true, {expr,ParseUnary()});
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
SyntaxNode Parser::ParseSum()
|
||||
{
|
||||
SyntaxNode expr = ParseFactor();
|
||||
while(IsAnySymbol({"+","-"},true))
|
||||
{
|
||||
if(tkn.text == "+")
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(AddExpression, true, {expr,ParseFactor()});
|
||||
}
|
||||
else if(tkn.text == "-")
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(SubExpression, true, {expr,ParseFactor()});
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
SyntaxNode Parser::ParseAssignment()
|
||||
{
|
||||
SyntaxNode node = ParseTernary();
|
||||
if(IsSymbol("="))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(AssignExpression,true,{node,ParseAssignment()});
|
||||
}
|
||||
else if(IsSymbol("+="))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(AddExpression,true,{ node,ParseAssignment()})});
|
||||
}
|
||||
else if(IsSymbol("-="))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(SubExpression,true,{ node,ParseAssignment()})});
|
||||
}
|
||||
else if(IsSymbol("*="))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(TimesExpression,true,{ node,ParseAssignment()})});
|
||||
}
|
||||
else if(IsSymbol("/="))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(DivideExpression,true,{ node,ParseAssignment()})});
|
||||
}
|
||||
else if(IsSymbol("%="))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(ModExpression,true,{ node,ParseAssignment()})});
|
||||
}
|
||||
else if(IsSymbol("<<="))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(LeftShiftExpression,true,{ node,ParseAssignment()})});
|
||||
}
|
||||
else if(IsSymbol(">>="))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(RightShiftExpression,true,{ node,ParseAssignment()})});
|
||||
}
|
||||
else if(IsSymbol("|="))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(BitwiseOrExpression,true,{ node,ParseAssignment()})});
|
||||
}
|
||||
else if(IsSymbol("&="))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(BitwiseAndExpression,true,{ node,ParseAssignment()})});
|
||||
}
|
||||
else if(IsSymbol("^="))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(XOrExpression,true,{ node,ParseAssignment()})});
|
||||
}
|
||||
return node;
|
||||
}
|
||||
SyntaxNode Parser::ParseNode(bool isRoot)
|
||||
{
|
||||
if(i < tokens.size() && !isRoot && tokens[i].type == Documentation)
|
||||
{
|
||||
auto txt = tokens[i].text;
|
||||
i++;
|
||||
return AdvancedSyntaxNode::Create(DocumentationStatement,false,{txt,ParseNode()});
|
||||
}
|
||||
if(IsSymbol("{") || isRoot)
|
||||
{
|
||||
AdvancedSyntaxNode aSN;
|
||||
aSN.isExpression=false;
|
||||
aSN.nodeName = isRoot ? NodeList : ScopeNode;
|
||||
|
||||
while(i < tokens.size() && (isRoot || !IsSymbol("}",false)))
|
||||
{
|
||||
aSN.nodes.push_back(ParseNode());
|
||||
IsSymbol(";");
|
||||
}
|
||||
|
||||
if(!isRoot) i++;
|
||||
return aSN;
|
||||
}
|
||||
|
||||
if(IsIdentifier("if"))
|
||||
{
|
||||
EnsureSymbol("(");
|
||||
SyntaxNode cond = ParseExpression();
|
||||
EnsureSymbol(")");
|
||||
SyntaxNode truthy = nullptr;
|
||||
SyntaxNode falsey = nullptr;
|
||||
|
||||
if(!IsIdentifier("else",false))
|
||||
{
|
||||
truthy = ParseNode();
|
||||
}
|
||||
|
||||
if(IsIdentifier("else"))
|
||||
{
|
||||
falsey = ParseNode();
|
||||
}
|
||||
|
||||
return AdvancedSyntaxNode::Create(IfStatement, false, {cond, truthy,falsey});
|
||||
}
|
||||
if(IsIdentifier("while"))
|
||||
{
|
||||
EnsureSymbol("(");
|
||||
SyntaxNode cond = ParseExpression();
|
||||
EnsureSymbol(")");
|
||||
|
||||
SyntaxNode body = nullptr;
|
||||
if(!IsSymbol(";"))
|
||||
{
|
||||
body = ParseNode();
|
||||
}
|
||||
return AdvancedSyntaxNode::Create(WhileStatement,false,{cond,body});
|
||||
}
|
||||
if(IsIdentifier("do"))
|
||||
{
|
||||
EnsureSymbol("(");
|
||||
SyntaxNode cond = ParseExpression();
|
||||
EnsureSymbol(")");
|
||||
|
||||
SyntaxNode body = nullptr;
|
||||
if(!IsSymbol(";"))
|
||||
{
|
||||
body = ParseNode();
|
||||
}
|
||||
return AdvancedSyntaxNode::Create(DoStatement,false,{cond,body});
|
||||
}
|
||||
if(IsIdentifier("for"))
|
||||
{
|
||||
SyntaxNode init = nullptr;
|
||||
SyntaxNode cond = true;
|
||||
SyntaxNode inc = nullptr;
|
||||
SyntaxNode body = nullptr;
|
||||
EnsureSymbol("(");
|
||||
if(!IsSymbol(";",false))
|
||||
{
|
||||
init = ParseExpression();
|
||||
}
|
||||
EnsureSymbol(";");
|
||||
if(!IsSymbol(";",false))
|
||||
{
|
||||
cond = ParseExpression();
|
||||
}
|
||||
EnsureSymbol(";");
|
||||
if(!IsSymbol(")",false))
|
||||
{
|
||||
inc = ParseExpression();
|
||||
}
|
||||
|
||||
EnsureSymbol(")");
|
||||
|
||||
|
||||
if(!IsSymbol(";"))
|
||||
{
|
||||
body = ParseNode();
|
||||
}
|
||||
return AdvancedSyntaxNode::Create(ForStatement,false,{init,cond,inc,body});
|
||||
}
|
||||
if(IsIdentifier("each"))
|
||||
{
|
||||
SyntaxNode item = nullptr;
|
||||
EnsureSymbol("(");
|
||||
SyntaxNode list = ParseExpression();
|
||||
SyntaxNode body = nullptr;
|
||||
if(IsSymbol(":"))
|
||||
{
|
||||
item = list;
|
||||
list = ParseExpression();
|
||||
}
|
||||
EnsureSymbol(")");
|
||||
|
||||
if(!IsSymbol(";"))
|
||||
{
|
||||
body = ParseNode();
|
||||
}
|
||||
return AdvancedSyntaxNode::Create(EachStatement,false,{item,list,body});
|
||||
}
|
||||
if(IsIdentifier("func"))
|
||||
{
|
||||
auto nameAndArgs = ParseExpression();
|
||||
|
||||
if(IsSymbol("{",false))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(FunctionStatement,false,{nameAndArgs,ParseNode()});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto v = ParseExpression();
|
||||
|
||||
EnsureSymbol(";");
|
||||
return AdvancedSyntaxNode::Create(FunctionStatement,false,{nameAndArgs,AdvancedSyntaxNode::Create(ReturnStatement,false,{v})});
|
||||
}
|
||||
}
|
||||
if(IsIdentifier("break"))
|
||||
{
|
||||
EnsureSymbol(";");
|
||||
return AdvancedSyntaxNode::Create(BreakStatement,false,{});
|
||||
}
|
||||
if(IsIdentifier("continue"))
|
||||
{
|
||||
EnsureSymbol(";");
|
||||
return AdvancedSyntaxNode::Create(ContinueStatement,false,{});
|
||||
}
|
||||
if(IsIdentifier("return"))
|
||||
{
|
||||
auto v = ParseExpression();
|
||||
EnsureSymbol(";");
|
||||
return AdvancedSyntaxNode::Create(ReturnStatement,false,{v});
|
||||
}
|
||||
if(IsIdentifier("throw"))
|
||||
{
|
||||
auto v = ParseExpression();
|
||||
EnsureSymbol(";");
|
||||
return AdvancedSyntaxNode::Create(ThrowStatement,false,{v});
|
||||
}
|
||||
if(IsIdentifier("try"))
|
||||
{
|
||||
auto tryBody = ParseNode();
|
||||
SyntaxNode catchNode = nullptr;
|
||||
SyntaxNode catchEx = nullptr;
|
||||
SyntaxNode finally=nullptr;
|
||||
if(IsIdentifier("catch"))
|
||||
{
|
||||
EnsureSymbol("(");
|
||||
catchEx = ParseExpression();
|
||||
EnsureSymbol(")");
|
||||
catchNode = ParseNode();
|
||||
}
|
||||
if(IsIdentifier("finally"))
|
||||
{
|
||||
finally = ParseNode();
|
||||
}
|
||||
|
||||
return AdvancedSyntaxNode::Create(TryStatement, false, {tryBody,catchNode,catchEx, finally});
|
||||
}
|
||||
if(IsIdentifier("defer"))
|
||||
{
|
||||
if(IsSymbol("{",false))
|
||||
{
|
||||
return AdvancedSyntaxNode::Create(DeferStatement,false,{ParseNode()});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto v = ParseExpression();
|
||||
|
||||
EnsureSymbol(";");
|
||||
return AdvancedSyntaxNode::Create(DeferStatement,false,{AdvancedSyntaxNode::Create(ReturnStatement,false,{v})});
|
||||
}
|
||||
}
|
||||
auto v = ParseExpression();
|
||||
EnsureSymbol(";");
|
||||
return v;
|
||||
}
|
||||
SyntaxNode Parser::ParseTernary()
|
||||
{
|
||||
SyntaxNode node = ParseLOr();
|
||||
if(IsSymbol("?"))
|
||||
{
|
||||
auto yes = ParseTernary();
|
||||
EnsureSymbol(":");
|
||||
auto no = ParseTernary();
|
||||
|
||||
return AdvancedSyntaxNode::Create(TernaryExpression,true,{node,yes,no});
|
||||
}
|
||||
return node;
|
||||
}
|
||||
SyntaxNode Parser::ParseShift()
|
||||
{
|
||||
SyntaxNode expr = ParseSum();
|
||||
while(IsAnySymbol({"<<",">>"},true))
|
||||
{
|
||||
if(tkn.text == "<<")
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(LeftShiftExpression, true, {expr,ParseSum()});
|
||||
}
|
||||
else if(tkn.text == ">>")
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(RightShiftExpression, true, {expr,ParseSum()});
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
SyntaxNode Parser::ParseRel()
|
||||
{
|
||||
SyntaxNode expr = ParseShift();
|
||||
while(IsAnySymbol({"<",">","<=",">="},true))
|
||||
{
|
||||
if(tkn.text == "<")
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(LessThanExpression, true, {expr,ParseShift()});
|
||||
}
|
||||
else if(tkn.text == ">")
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(GreaterThanExpression, true, {expr,ParseShift()});
|
||||
}
|
||||
else if(tkn.text == "<=")
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(LessThanEqualsExpression, true, {expr,ParseShift()});
|
||||
}
|
||||
else if(tkn.text == ">")
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(GreaterThanEqualsExpression, true, {expr,ParseShift()});
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
SyntaxNode Parser::ParseEq()
|
||||
{
|
||||
SyntaxNode expr = ParseRel();
|
||||
while(IsAnySymbol({"==","!="},true))
|
||||
{
|
||||
if(tkn.text == "==")
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(EqualsExpression, true, {expr,ParseRel()});
|
||||
}
|
||||
else if(tkn.text == "!=")
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(NotEqualsExpression, true, {expr,ParseRel()});
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
SyntaxNode Parser::ParseBAnd()
|
||||
{
|
||||
SyntaxNode expr = ParseEq();
|
||||
while(IsSymbol("&"))
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(BitwiseAndExpression,true,{expr,ParseEq()});
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
SyntaxNode Parser::ParseExpression()
|
||||
{
|
||||
SyntaxNode expr = ParseAssignment();
|
||||
while(IsSymbol(","))
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(CommaExpression,true,{expr,ParseAssignment()});
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
SyntaxNode Parser::ParseXOr()
|
||||
{
|
||||
SyntaxNode expr = ParseBAnd();
|
||||
while(IsSymbol("^"))
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(XOrExpression,true,{expr,ParseBAnd()});
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
SyntaxNode Parser::ParseBOr()
|
||||
{
|
||||
SyntaxNode expr = ParseXOr();
|
||||
while(IsSymbol("|"))
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(BitwiseOrExpression,true,{expr,ParseXOr()});
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
SyntaxNode Parser::ParseLAnd()
|
||||
{
|
||||
SyntaxNode expr = ParseBOr();
|
||||
while(IsSymbol("&&"))
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(LogicalAndExpression,true,{expr,ParseBOr()});
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
SyntaxNode Parser::ParseLOr()
|
||||
{
|
||||
SyntaxNode expr = ParseLAnd();
|
||||
while(IsSymbol("||"))
|
||||
{
|
||||
expr = AdvancedSyntaxNode::Create(LogicalOrExpression,true,{expr,ParseLAnd()});
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
#include "CrossLang.hpp"
|
||||
#include <iostream>
|
||||
#include "sago/platform_folders.h"
|
||||
#include <mbedtls/error.h>
|
||||
#include <string>
|
||||
using namespace Tesses::Framework;
|
||||
using namespace Tesses::CrossLang;
|
||||
using namespace Tesses::Framework::Http;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
TF_Init();
|
||||
|
||||
Tesses::Framework::Filesystem::VFSPath filename = sago::getConfigHome();
|
||||
filename = filename / "Tesses" / "CrossLang" / "Shell" / "Shell.crvm";
|
||||
|
||||
Tesses::Framework::Filesystem::LocalFilesystem fs;
|
||||
GC gc;
|
||||
gc.Start();
|
||||
GCList ls(gc);
|
||||
tryAgain:
|
||||
TRootEnvironment* env = TRootEnvironment::Create(ls, TDictionary::Create(ls));
|
||||
|
||||
|
||||
TStd::RegisterStd(&gc,env);
|
||||
|
||||
if(fs.RegularFileExists(filename))
|
||||
env->LoadFileWithDependencies(&gc, &fs, filename);
|
||||
else
|
||||
{
|
||||
tryAgainFast:
|
||||
std::cout << "File " << filename.ToString() << " not found, do you want to download the installer from: https://crosslang.tesseslanguage.com/crosslang-shell-install.tcross (this will install other stuff as well) (Y/n)? ";
|
||||
std::string line;
|
||||
std::getline(std::cin,line);
|
||||
if(line == "Y" || line == "y")
|
||||
{
|
||||
HttpRequest req;
|
||||
req.url = "https://crosslang.tesseslanguage.com/crosslang-shell-install.tcross";
|
||||
req.method = "GET";
|
||||
HttpResponse resp(req);
|
||||
if(resp.statusCode == StatusCode::OK)
|
||||
{
|
||||
std::string str = resp.ReadAsString();
|
||||
env->Eval(ls, str);
|
||||
goto tryAgain;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error when fetching the script error: " << std::to_string(resp.statusCode) << " " << HttpUtils::StatusCodeString(resp.statusCode) << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if(line == "N" || line == "n")
|
||||
{
|
||||
std::cout << "Looks like you will need to install manually" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Please use Y or N (case insensitive)" << std::endl;
|
||||
goto tryAgainFast;
|
||||
}
|
||||
}
|
||||
|
||||
TList* args = TList::Create(ls);
|
||||
|
||||
args->Add(filename.ToString());
|
||||
|
||||
for(int arg=1;arg<argc;arg++)
|
||||
args->Add(std::string(argv[arg]));
|
||||
|
||||
auto res = env->CallFunction(ls,"main",{args});
|
||||
int64_t iresult;
|
||||
if(GetObject(res,iresult))
|
||||
return (int)iresult;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
#include "CrossLang.hpp"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
using namespace Tesses::CrossLang;
|
||||
using namespace Tesses::Framework;
|
||||
using namespace Tesses::Framework::Filesystem;
|
||||
void Help(const char* filename)
|
||||
{
|
||||
printf("USAGE: %s [OPTIONS] source_file1 source_file2 source_file_n \n", filename);
|
||||
printf("OPTIONS:\n");
|
||||
printf(" -o: Output directory (OUTDIR, defaults to ./bin)\n");
|
||||
printf(" -i: Set info (ex {\"maintainer\": \"Mike Nolan\", \"repo\": \"https://example.com/\", \"homepage\": \"https://example.com/\",\"license\":\"MIT\"})\n");
|
||||
printf(" -v: Set version (1.0.0.0-prod defaults to 1.0.0.0-dev)\n");
|
||||
printf(" -d: Add dependency (DependencyName-1.0.0.0-prod)\n");
|
||||
printf(" -n: Set name (MyAppOrLibName defaults to out)\n");
|
||||
printf(" -r: Set resource directory (RESDIR defaults to res)\n");
|
||||
printf(" -h, --help: Prints help\n");
|
||||
printf("Options except for help have flag with arg like this: -F ARG\n");
|
||||
exit(1);
|
||||
}
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
/*std::ifstream strm(argv[1],std::ios_base::in|std::ios_base::binary);
|
||||
std::vector<LexToken> tokens;
|
||||
Lex(argv[1],strm,tokens);
|
||||
|
||||
Parser parser(tokens);
|
||||
|
||||
CodeGen gen;
|
||||
gen.GenRoot(parser.ParseRoot());
|
||||
std::vector<uint8_t> data;
|
||||
ByteCodeVectorWriter w(data);
|
||||
gen.Save(std::filesystem::current_path(),w);
|
||||
*/
|
||||
TF_Init();
|
||||
std::filesystem::path outputDir = std::filesystem::current_path() / "bin";
|
||||
std::vector<std::filesystem::path> source;
|
||||
std::filesystem::path resourceDir = std::filesystem::current_path() / "res";
|
||||
std::vector<std::pair<std::string, TVMVersion>> dependencies;
|
||||
std::string name="out";
|
||||
std::string info="{}";
|
||||
TVMVersion version;
|
||||
if(argc < 2)
|
||||
{
|
||||
Help(argv[0]);
|
||||
}
|
||||
|
||||
for(int i = 1; i < argc; i++)
|
||||
{
|
||||
if(strcmp(argv[i],"--help") == 0 || strcmp(argv[i],"-h")==0)
|
||||
{
|
||||
Help(argv[0]);
|
||||
}
|
||||
else if(strcmp(argv[i], "-o") == 0)
|
||||
{
|
||||
i++;
|
||||
if(i < argc)
|
||||
{
|
||||
outputDir = argv[i];
|
||||
}
|
||||
}
|
||||
else if(strcmp(argv[i], "-r") == 0)
|
||||
{
|
||||
i++;
|
||||
if(i < argc)
|
||||
{
|
||||
resourceDir = argv[i];
|
||||
}
|
||||
}
|
||||
else if(strcmp(argv[i], "-i") == 0)
|
||||
{
|
||||
i++;
|
||||
if(i < argc)
|
||||
{
|
||||
info = argv[i];
|
||||
}
|
||||
}
|
||||
else if(strcmp(argv[i], "-d") == 0)
|
||||
{
|
||||
i++;
|
||||
if(i < argc)
|
||||
{
|
||||
std::string str = argv[i];
|
||||
auto lastDash = str.find_last_of('-');
|
||||
if(lastDash < str.size())
|
||||
{
|
||||
std::string str2 = str.substr(lastDash+1);
|
||||
if(str2 == "dev" || str2 == "alpha" || str2 == "beta" || str2 == "prod")
|
||||
{
|
||||
lastDash = str.find_last_of('-',lastDash-1);
|
||||
}
|
||||
std::string str1 = str.substr(0,lastDash);
|
||||
str2 = str.substr(lastDash+1);
|
||||
|
||||
TVMVersion v2;
|
||||
if(!TVMVersion::TryParse(str2,v2))
|
||||
{
|
||||
printf("ERROR: Invalid syntax for version\n");
|
||||
printf("Expected MAJOR[.MINOR[.PATCH[.BUILD[-dev,-alpha,-beta,-prod]]]]\n");
|
||||
exit(1);
|
||||
}
|
||||
dependencies.push_back(std::pair<std::string,TVMVersion>(str1,v2));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("ERROR: Dependency must have version\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(strcmp(argv[i], "-n") == 0)
|
||||
{
|
||||
i++;
|
||||
if(i < argc)
|
||||
{
|
||||
name = argv[i];
|
||||
}
|
||||
}
|
||||
else if(strcmp(argv[i], "-v") == 0)
|
||||
{
|
||||
i++;
|
||||
if(i < argc)
|
||||
{
|
||||
|
||||
if(!TVMVersion::TryParse(argv[i],version))
|
||||
{
|
||||
printf("ERROR: Invalid syntax for version\n");
|
||||
printf("Expected MAJOR[.MINOR[.PATCH[.BUILD[-dev,-alpha,-beta,-prod]]]]\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
source.push_back(argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::vector<LexToken> tokens;
|
||||
|
||||
for(auto src : source)
|
||||
{
|
||||
std::ifstream strm(src,std::ios_base::in|std::ios_base::binary);
|
||||
int res = Lex(argv[1],strm,tokens);
|
||||
|
||||
}
|
||||
|
||||
Parser parser(tokens);
|
||||
|
||||
CodeGen gen;
|
||||
gen.GenRoot(parser.ParseRoot());
|
||||
gen.name = name;
|
||||
gen.version = version;
|
||||
gen.info = info;
|
||||
for(auto deps : dependencies)
|
||||
{
|
||||
gen.dependencies.push_back(deps);
|
||||
}
|
||||
std::filesystem::create_directory(outputDir);
|
||||
|
||||
{
|
||||
Tesses::Framework::Streams::FileStream strm(outputDir / (name + "-" + version.ToString() + ".crvm"),"wb");
|
||||
|
||||
LocalFilesystem fs;
|
||||
SubdirFilesystem sfs(&fs,fs.SystemToVFSPath(resourceDir.string()),false);
|
||||
|
||||
gen.Save(&sfs,&strm);
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
#include "CrossLang.hpp"
|
||||
#include <iostream>
|
||||
using namespace Tesses::CrossLang;
|
||||
void Ensure(Tesses::Framework::Streams::Stream& strm,uint8_t* buffer, size_t len)
|
||||
{
|
||||
if(strm.ReadBlock(buffer,len) != len)
|
||||
{
|
||||
throw VMException("Could not read " + std::to_string(len) + " byte(s).");
|
||||
}
|
||||
}
|
||||
uint32_t EnsureInt(Tesses::Framework::Streams::Stream& strm)
|
||||
{
|
||||
uint8_t buff[4];
|
||||
Ensure(strm,buff,sizeof(buff));
|
||||
return BitConverter::ToUint32BE(buff[0]);
|
||||
}
|
||||
std::string EnsureString(Tesses::Framework::Streams::Stream& strm)
|
||||
{
|
||||
size_t len = (size_t)EnsureInt(strm);
|
||||
std::string myStr={};
|
||||
myStr.resize(len);
|
||||
Ensure(strm,(uint8_t*)myStr.data(), len);
|
||||
return myStr;
|
||||
}
|
||||
void DumpFile(std::filesystem::path p)
|
||||
{
|
||||
if(std::filesystem::is_regular_file(p))
|
||||
{
|
||||
try
|
||||
{
|
||||
std::cout << "File: " << p.string() << std::endl;
|
||||
Tesses::Framework::Streams::FileStream strm(p,"rb");
|
||||
uint8_t main_header[18];
|
||||
Ensure(strm,main_header,sizeof(main_header));
|
||||
if(strncmp((const char*)main_header,"TCROSSVM",8) != 0) throw VMException("Invalid TCrossVM image.");
|
||||
TVMVersion version(main_header+8);
|
||||
if(version.CompareToRuntime() == 1)
|
||||
{
|
||||
throw VMException("Runtime is too old.");
|
||||
}
|
||||
TVMVersion v2(main_header+13);
|
||||
std::cout << "Version: " << v2.ToString() << std::endl;
|
||||
|
||||
size_t _len = (size_t)EnsureInt(strm);
|
||||
|
||||
std::cout << "SectionCount: " << _len << std::endl;
|
||||
|
||||
std::vector<std::string> strs;
|
||||
|
||||
std::unordered_map<uint32_t, std::vector<std::string>> funs;
|
||||
std::vector<std::vector<std::string>> closures;
|
||||
|
||||
|
||||
|
||||
char table_name[4];
|
||||
|
||||
for(size_t i = 0; i < _len; i++)
|
||||
{
|
||||
Ensure(strm,(uint8_t*)table_name,sizeof(table_name));
|
||||
size_t tableLen = (size_t)EnsureInt(strm);
|
||||
std::string tableName(table_name,4);
|
||||
if(tableName == "STRS")
|
||||
{
|
||||
size_t strsLen = (size_t)EnsureInt(strm);
|
||||
for(size_t j = 0;j < strsLen;j++)
|
||||
{
|
||||
strs.push_back(EnsureString(strm));
|
||||
}
|
||||
}
|
||||
else if(tableName == "DEPS")
|
||||
{
|
||||
|
||||
std::string name = strs.at((size_t)EnsureInt(strm));
|
||||
|
||||
uint8_t version_bytes[5];
|
||||
Ensure(strm,version_bytes,sizeof(version_bytes));
|
||||
TVMVersion depVersion(version_bytes);
|
||||
std::cout << "Dependency: " << name << "-" << depVersion.ToString() << std::endl;
|
||||
}
|
||||
else if(tableName == "NAME")
|
||||
{
|
||||
std::cout << "Name: " << strs.at((size_t)EnsureInt(strm)) << std::endl;
|
||||
}
|
||||
else if(tableName == "CHKS")
|
||||
{
|
||||
size_t chunkCount = (size_t)EnsureInt(strm);
|
||||
|
||||
for(size_t j = 0; j < chunkCount; j++)
|
||||
{
|
||||
std::vector<std::string> args;
|
||||
size_t argCount = (size_t)EnsureInt(strm);
|
||||
for(size_t k = 0; k < argCount; k++)
|
||||
{
|
||||
args.push_back(strs.at(EnsureInt(strm)));
|
||||
}
|
||||
auto len = EnsureInt(strm);
|
||||
|
||||
strm.Seek(len,Tesses::Framework::Streams::SeekOrigin::Current);
|
||||
|
||||
closures.push_back(args);
|
||||
}
|
||||
}
|
||||
else if(tableName == "FUNS")
|
||||
{
|
||||
size_t funLength = (size_t)EnsureInt(strm);
|
||||
|
||||
for(size_t j = 0; j < funLength;j++)
|
||||
{
|
||||
std::vector<std::string> fnParts;
|
||||
uint32_t fnPartsC = EnsureInt(strm);
|
||||
for(uint32_t k = 0; k < fnPartsC; k++)
|
||||
{
|
||||
fnParts.push_back(strs.at(EnsureInt(strm)));
|
||||
}
|
||||
|
||||
uint32_t fnNumber = EnsureInt(strm);
|
||||
funs[fnNumber] = fnParts;
|
||||
|
||||
}
|
||||
}
|
||||
else if(tableName == "INFO")
|
||||
{
|
||||
|
||||
std::cout << "Info: " << strs.at((size_t)EnsureInt(strm)) << std::endl;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
strm.Seek((int64_t)tableLen,Tesses::Framework::Streams::SeekOrigin::Current);
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i = 1; i < closures.size(); i++)
|
||||
{
|
||||
if(funs.contains((uint32_t)i))
|
||||
{
|
||||
std::cout << "Func: ";
|
||||
auto res = funs[(uint32_t)i];
|
||||
if(!res.empty()) {
|
||||
std::cout << "/^" << res[0] << "^/ ";
|
||||
}
|
||||
for(size_t i = 1; i < res.size(); i++)
|
||||
{
|
||||
if(i > 1) std::cout << ".";
|
||||
std::cout << res[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Closure: ";
|
||||
}
|
||||
std::cout << "(";
|
||||
bool first=true;
|
||||
for(auto arg : closures[i])
|
||||
{
|
||||
if(!first) std::cout << ", ";
|
||||
std::cout << arg;
|
||||
|
||||
if(first) first=false;
|
||||
}
|
||||
std::cout << ")" << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
std::cout << "String Table:" << std::endl;
|
||||
|
||||
for(auto str : strs) {
|
||||
std::cout << EscapeString(str, true) << std::endl;
|
||||
}
|
||||
}
|
||||
catch(std::exception& ex)
|
||||
{
|
||||
std::cout << "Error when reading file \"" << p.string() << "\" " << ex.what() << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "CrossVM file \"" << p.string() << "\" does not exist." << std::endl;
|
||||
}
|
||||
}
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
for(int i = 1; i < argc; i++)
|
||||
{
|
||||
DumpFile(argv[i]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
#include "CrossLang.hpp"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
using namespace Tesses::Framework;
|
||||
using namespace Tesses::CrossLang;
|
||||
using namespace Tesses::Framework::Filesystem;
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
TF_Init();
|
||||
GC gc;
|
||||
gc.Start();
|
||||
GCList ls(gc);
|
||||
TRootEnvironment* env = TRootEnvironment::Create(ls, TDictionary::Create(ls));
|
||||
TStd::RegisterStd(&gc,env);
|
||||
|
||||
if(argc > 1)
|
||||
{
|
||||
std::ifstream strm(argv[1],std::ios_base::in|std::ios_base::binary);
|
||||
std::vector<LexToken> tokens;
|
||||
Lex(argv[1],strm,tokens);
|
||||
|
||||
Parser parser(tokens);
|
||||
|
||||
CodeGen gen;
|
||||
gen.GenRoot(parser.ParseRoot());
|
||||
std::vector<uint8_t> data;
|
||||
LocalFilesystem fs;
|
||||
SubdirFilesystem sfs(&fs,VFSPath("."),false);
|
||||
|
||||
Tesses::Framework::Streams::MemoryStream strm2(true);
|
||||
gen.Save(&sfs,&strm2);
|
||||
|
||||
|
||||
{
|
||||
TFile* file = TFile::Create(ls);
|
||||
|
||||
strm2.Seek(0,Tesses::Framework::Streams::SeekOrigin::Begin);
|
||||
file->Load(&gc,&strm2);
|
||||
|
||||
env->LoadFile(&gc, file);
|
||||
|
||||
|
||||
}
|
||||
|
||||
TList* args = TList::Create(ls);
|
||||
for(int arg=1;arg<argc;arg++)
|
||||
args->Add(std::string(argv[arg]));
|
||||
|
||||
auto res = env->CallFunction(ls,"main",{args});
|
||||
int64_t iresult;
|
||||
if(GetObject(res,iresult))
|
||||
return (int)iresult;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
while(true)
|
||||
{
|
||||
std::cout << "> ";
|
||||
std::string source;
|
||||
std::getline(std::cin,source);
|
||||
|
||||
Tesses::Framework::Streams::MemoryStream strm2(true);
|
||||
|
||||
if(source.starts_with("loadfile "))
|
||||
{
|
||||
std::string filename = source.substr(9);
|
||||
|
||||
std::ifstream strm(filename,std::ios_base::in|std::ios_base::binary);
|
||||
std::vector<LexToken> tokens;
|
||||
Lex(filename,strm,tokens);
|
||||
|
||||
Parser parser(tokens);
|
||||
|
||||
CodeGen gen;
|
||||
gen.GenRoot(parser.ParseRoot());
|
||||
|
||||
LocalFilesystem fs;
|
||||
SubdirFilesystem sfs(&fs,VFSPath("."),false);
|
||||
|
||||
gen.Save(&sfs, &strm2);
|
||||
|
||||
}
|
||||
else if(source == "exit")
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<LexToken> tokens;
|
||||
std::stringstream strm(source,std::ios_base::in | std::ios_base::binary);
|
||||
Lex("lexed.tcross",strm,tokens);
|
||||
|
||||
Parser parser(tokens);
|
||||
|
||||
CodeGen gen;
|
||||
gen.GenRoot(parser.ParseRoot());
|
||||
|
||||
LocalFilesystem fs;
|
||||
SubdirFilesystem sfs(&fs,VFSPath("."),false);
|
||||
|
||||
gen.Save(&sfs,&strm2);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
TFile* file = TFile::Create(ls);
|
||||
|
||||
strm2.Seek(0,Tesses::Framework::Streams::SeekOrigin::Begin);
|
||||
file->Load(&gc,&strm2);
|
||||
|
||||
env->LoadFile(&gc, file);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
#include <mbedtls/error.h>
|
||||
using namespace Tesses::Framework;
|
||||
using namespace Tesses::CrossLang;
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
TF_Init();
|
||||
if(argc < 2)
|
||||
{
|
||||
printf("USAGE: %s <filename.crvm> <args...>\n",argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
GC gc;
|
||||
gc.Start();
|
||||
GCList ls(gc);
|
||||
TRootEnvironment* env = TRootEnvironment::Create(ls, TDictionary::Create(ls));
|
||||
Tesses::Framework::Filesystem::LocalFilesystem fs;
|
||||
env->LoadFileWithDependencies(&gc, &fs, fs.SystemToVFSPath(argv[1]));
|
||||
TStd::RegisterStd(&gc,env);
|
||||
TList* args = TList::Create(ls);
|
||||
for(int arg=1;arg<argc;arg++)
|
||||
args->Add(std::string(argv[arg]));
|
||||
|
||||
auto res = env->CallFunction(ls,"main",{args});
|
||||
int64_t iresult;
|
||||
if(GetObject(res,iresult))
|
||||
return (int)iresult;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
#include "CrossLang.hpp"
|
||||
#include <iostream>
|
||||
|
||||
#if defined(GEKKO)
|
||||
#undef CROSSLANG_ENABLE_TERMIOS
|
||||
#endif
|
||||
|
||||
#ifdef CROSSLANG_ENABLE_TERMIOS
|
||||
#include <termios.h>
|
||||
#endif
|
||||
namespace Tesses::CrossLang {
|
||||
#ifdef CROSSLANG_ENABLE_TERMIOS
|
||||
struct termios orig_termios;
|
||||
static void disableRawMode()
|
||||
{
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
|
||||
}
|
||||
#endif
|
||||
TObject Console_getEcho(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
#ifdef CROSSLANG_ENABLE_TERMIOS
|
||||
struct termios raw;
|
||||
tcgetattr(STDIN_FILENO, &raw);
|
||||
return (raw.c_lflag & ECHO) > 0;
|
||||
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
TObject Console_setEcho(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
if(args.size() == 1 && std::holds_alternative<bool>(args[0]))
|
||||
{
|
||||
bool cooked = std::get<bool>(args[0]);
|
||||
#ifdef CROSSLANG_ENABLE_TERMIOS
|
||||
struct termios raw;
|
||||
tcgetattr(STDIN_FILENO, &raw);
|
||||
if(cooked)
|
||||
{
|
||||
raw.c_lflag |= ECHO;
|
||||
}
|
||||
else
|
||||
{
|
||||
raw.c_lflag &= ~(ECHO);
|
||||
}
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
|
||||
|
||||
#endif
|
||||
return cooked;
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
TObject Console_getCanonical(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
#ifdef CROSSLANG_ENABLE_TERMIOS
|
||||
struct termios raw;
|
||||
tcgetattr(STDIN_FILENO, &raw);
|
||||
return (raw.c_lflag & ICANON) > 0;
|
||||
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
TObject Console_setCanonical(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
if(args.size() == 1 && std::holds_alternative<bool>(args[0]))
|
||||
{
|
||||
bool cooked = std::get<bool>(args[0]);
|
||||
#ifdef CROSSLANG_ENABLE_TERMIOS
|
||||
struct termios raw;
|
||||
tcgetattr(STDIN_FILENO, &raw);
|
||||
if(cooked)
|
||||
{
|
||||
raw.c_lflag |= ICANON;
|
||||
}
|
||||
else
|
||||
{
|
||||
raw.c_lflag &= ~(ICANON);
|
||||
}
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
|
||||
|
||||
#endif
|
||||
return cooked;
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
TObject Console_getSignals(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
#ifdef CROSSLANG_ENABLE_TERMIOS
|
||||
struct termios raw;
|
||||
tcgetattr(STDIN_FILENO, &raw);
|
||||
return (raw.c_lflag & ISIG) > 0;
|
||||
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
TObject Console_setSignals(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
if(args.size() == 1 && std::holds_alternative<bool>(args[0]))
|
||||
{
|
||||
bool cooked = std::get<bool>(args[0]);
|
||||
#ifdef CROSSLANG_ENABLE_TERMIOS
|
||||
struct termios raw;
|
||||
tcgetattr(STDIN_FILENO, &raw);
|
||||
if(cooked)
|
||||
{
|
||||
raw.c_lflag |= ISIG;
|
||||
}
|
||||
else
|
||||
{
|
||||
raw.c_lflag &= ~(ISIG);
|
||||
}
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
|
||||
|
||||
#endif
|
||||
return cooked;
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
TObject Console_Read(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
uint8_t byte;
|
||||
std::cin.read((char*)&byte,1);
|
||||
return std::cin.eof() ? (int64_t)-1 : (int64_t)byte;
|
||||
}
|
||||
TObject Console_ReadLine(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
std::string str;
|
||||
std::getline(std::cin,str);
|
||||
return str;
|
||||
}
|
||||
TObject Console_Write(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
if(args.size() < 1)
|
||||
{
|
||||
return Undefined();
|
||||
}
|
||||
std::cout << ToString(ls.GetGC(),args[0]);
|
||||
return Undefined();
|
||||
}
|
||||
TObject Console_Fatal(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
if(args.size() < 1)
|
||||
{
|
||||
std::cout << "FATAL: <NO MESSAGE>" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
std::cout << "FATAL: " << ToString(ls.GetGC(),args[0]) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
TObject Console_WriteLine(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
if(args.size() < 1)
|
||||
{
|
||||
std::cout << "\n";
|
||||
return Undefined();
|
||||
}
|
||||
std::cout << ToString(ls.GetGC(),args[0]) << "\n";
|
||||
return Undefined();
|
||||
}
|
||||
void TStd::RegisterConsole(GC* gc,TRootEnvironment* env)
|
||||
{
|
||||
env->permissions.canRegisterConsole=true;
|
||||
#ifdef CROSSLANG_ENABLE_TERMIOS
|
||||
tcgetattr(STDIN_FILENO, &orig_termios);
|
||||
atexit(disableRawMode);
|
||||
#endif
|
||||
GCList ls(gc);
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
dict->DeclareFunction(gc,"getEcho","Get whether terminal is echoing characters read",{},Console_getEcho);
|
||||
dict->DeclareFunction(gc,"setEcho","Set whether terminal is echoing characters read",{"flag"},Console_setEcho);
|
||||
dict->DeclareFunction(gc,"getCanonical","Get whether terminal is buffering line by line (true) or byte by byte (false)",{},Console_getCanonical);
|
||||
dict->DeclareFunction(gc,"setCanonical","Set whether terminal is buffering line by line (true) or byte by byte (false)",{"flag"},Console_setCanonical);
|
||||
dict->DeclareFunction(gc,"getSignals","Get whether terminal is sending signals for CTRL+C (true) or via read (false)",{},Console_getSignals);
|
||||
dict->DeclareFunction(gc,"setSignals","Set whether terminal is sending signals for CTRL+C (true) or via read (false)",{"flag"},Console_setSignals);
|
||||
|
||||
dict->DeclareFunction(gc,"Read", "Reads a byte from stdin",{},Console_Read);
|
||||
dict->DeclareFunction(gc,"ReadLine","Reads line from stdin",{},Console_ReadLine);
|
||||
dict->DeclareFunction(gc,"Write","Write text \"text\" to stdout",{"text"},Console_Write);
|
||||
dict->DeclareFunction(gc,"WriteLine","Write text \"text\" to stdout with new line",{"text"},Console_WriteLine);
|
||||
dict->DeclareFunction(gc,"Fatal","Stop the program with an optional error message",{"$text"},Console_Fatal);
|
||||
|
||||
gc->BarrierBegin();
|
||||
env->DeclareVariable("Console", dict);
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,811 @@
|
|||
#include "CrossLang.hpp"
|
||||
#if defined(CROSSLANG_ENABLE_MBED)
|
||||
#include <iostream>
|
||||
|
||||
#include <mbedtls/sha1.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <mbedtls/sha512.h>
|
||||
#include <mbedtls/base64.h>
|
||||
|
||||
#include <mbedtls/entropy.h>
|
||||
#include <mbedtls/ctr_drbg.h>
|
||||
#include <mbedtls/x509.h>
|
||||
#include <mbedtls/ssl.h>
|
||||
#include <mbedtls/net_sockets.h>
|
||||
#include <mbedtls/error.h>
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
#if defined(CROSSLANG_ENABLE_MBED)
|
||||
static void my_debug(void *ctx, int level,
|
||||
const char *file, int line,
|
||||
const char *str)
|
||||
{
|
||||
((void) level);
|
||||
|
||||
fprintf(stderr, "%s:%04d: %s", file, line, str);
|
||||
fflush(stderr);
|
||||
}
|
||||
class TlsClientStream {
|
||||
GCList* ls;
|
||||
TCallable* read;
|
||||
TCallable* write;
|
||||
TCallable* close;
|
||||
TByteArray* readBuffer;
|
||||
TByteArray* writeBuffer;
|
||||
mbedtls_entropy_context entropy;
|
||||
mbedtls_ctr_drbg_context ctr_drbg;
|
||||
mbedtls_ssl_context ssl;
|
||||
mbedtls_ssl_config conf;
|
||||
mbedtls_x509_crt cachain;
|
||||
|
||||
|
||||
static int strm_send(void* ctx,const unsigned char* buf,size_t len)
|
||||
{
|
||||
TlsClientStream* strm = static_cast<TlsClientStream*>(ctx);
|
||||
strm->writeBuffer->data.resize(len);
|
||||
memcpy(strm->writeBuffer->data.data(),buf,len);
|
||||
|
||||
auto res = strm->write->Call(*strm->ls,{strm->writeBuffer});
|
||||
if(std::holds_alternative<int64_t>(res))
|
||||
{
|
||||
auto num = std::get<int64_t>(res);
|
||||
return (int)num;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
static int strm_recv(void* ctx,unsigned char* buf,size_t len)
|
||||
{
|
||||
TlsClientStream* strm = static_cast<TlsClientStream*>(ctx);
|
||||
strm->readBuffer->data.resize(len);
|
||||
|
||||
auto res = strm->read->Call(*strm->ls,{strm->readBuffer});
|
||||
if(std::holds_alternative<int64_t>(res))
|
||||
{
|
||||
auto num = std::get<int64_t>(res);
|
||||
if(num < 0) return (int)num;
|
||||
size_t read = (size_t)num;
|
||||
if(read > len) read = len;
|
||||
memcpy(buf,strm->readBuffer->data.data(), read);
|
||||
return read;
|
||||
}
|
||||
return -1;
|
||||
|
||||
//return (int)strm->Read((uint8_t*)buf,len);
|
||||
}
|
||||
public:
|
||||
void Close()
|
||||
{
|
||||
close->Call(*ls,{});
|
||||
}
|
||||
bool success=false;
|
||||
bool isDoneReading = false;
|
||||
int64_t Read(uint8_t* buffer, size_t len)
|
||||
{
|
||||
if(isDoneReading) return 0;
|
||||
int64_t r = (int64_t)mbedtls_ssl_read(&ssl,buffer,len);
|
||||
if(r == 0) isDoneReading=true;
|
||||
if(r == -30848)
|
||||
{
|
||||
isDoneReading = true;
|
||||
return 0;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
int64_t Write(uint8_t* buffer, size_t len)
|
||||
{
|
||||
return (int64_t)mbedtls_ssl_write(&ssl,buffer,len);
|
||||
}
|
||||
|
||||
TlsClientStream(GC* gc,std::string domain,std::string chain,bool verify, TCallable* read, TCallable* write, TCallable* close)
|
||||
{
|
||||
ls = new GCList(gc);
|
||||
ls->Add(read);
|
||||
ls->Add(write);
|
||||
ls->Add(close);
|
||||
this->read = read;
|
||||
this->write = write;
|
||||
this->close = close;
|
||||
readBuffer = TByteArray::Create(ls);
|
||||
writeBuffer = TByteArray::Create(ls);
|
||||
|
||||
mbedtls_ssl_init(&ssl);
|
||||
mbedtls_ssl_config_init(&conf);
|
||||
mbedtls_x509_crt_init(&cachain);
|
||||
mbedtls_ctr_drbg_init(&ctr_drbg);
|
||||
mbedtls_entropy_init(&entropy);
|
||||
|
||||
const char* pers = "CrossLangTLS";
|
||||
|
||||
int ret=0;
|
||||
|
||||
|
||||
|
||||
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
||||
psa_status_t status = psa_crypto_init();
|
||||
if (status != PSA_SUCCESS) {
|
||||
mbedtls_fprintf(stderr, "Failed to initialize PSA Crypto implementation: %d\n",
|
||||
(int) status);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
|
||||
(const unsigned char *) pers,
|
||||
strlen(pers))) != 0)
|
||||
{
|
||||
printf("FAILED mbedtls_ctr_drbg_seed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(ret != 0) { printf("FAILED mbedtls_x509_crt_parse cert %i\n",ret); return;}
|
||||
ret = mbedtls_x509_crt_parse(&cachain, (const unsigned char *) chain.c_str(),
|
||||
chain.size()+1);
|
||||
|
||||
if(ret != 0) {printf("FAILED mbedtls_x509_crt_parse chain %i\n",ret); return;}
|
||||
|
||||
|
||||
|
||||
if((ret = mbedtls_ssl_config_defaults(&conf,
|
||||
MBEDTLS_SSL_IS_CLIENT,
|
||||
MBEDTLS_SSL_TRANSPORT_STREAM,
|
||||
MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
|
||||
{
|
||||
char buffer[100];
|
||||
mbedtls_strerror(ret,buffer,sizeof(buffer));
|
||||
printf("FAILED mbedtls_ssl_conf_defaults %s\n",buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
|
||||
mbedtls_ssl_conf_dbg(&conf,my_debug,stdout);
|
||||
/* #if defined(MBEDTLS_SSL_CACHE_C)
|
||||
mbedtls_ssl_conf_session_cache(&conf, &cache,
|
||||
mbedtls_ssl_cache_get,
|
||||
mbedtls_ssl_cache_set);
|
||||
#endif*/
|
||||
mbedtls_ssl_conf_authmode(&conf, verify ? MBEDTLS_SSL_VERIFY_REQUIRED: MBEDTLS_SSL_VERIFY_OPTIONAL);
|
||||
mbedtls_ssl_conf_ca_chain(&conf, &cachain, NULL);
|
||||
|
||||
|
||||
mbedtls_ssl_set_bio(&ssl, static_cast<void*>(this),strm_send,strm_recv, NULL);
|
||||
if((ret=mbedtls_ssl_setup(&ssl,&conf) != 0))
|
||||
{
|
||||
printf("FAILED mbedtls_ssl_setup %i\n",ret);
|
||||
return;
|
||||
}
|
||||
if((ret=mbedtls_ssl_set_hostname(&ssl,domain.c_str()) != 0))
|
||||
{
|
||||
printf("FAILED mbedtls_ssl_set_hostname %i\n",ret);
|
||||
return;
|
||||
}
|
||||
if((ret = mbedtls_ssl_handshake(&ssl)) != 0)
|
||||
{
|
||||
char buffer[100];
|
||||
mbedtls_strerror(ret,buffer,sizeof(buffer));
|
||||
printf("FAILED mbedtls_ssl_handshake %s\n",buffer);
|
||||
return;
|
||||
}
|
||||
uint32_t flags;
|
||||
if ((flags = mbedtls_ssl_get_verify_result(&ssl)) != 0) {
|
||||
#if !defined(MBEDTLS_X509_REMOVE_INFO)
|
||||
char vrfy_buf[512];
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if !defined(MBEDTLS_X509_REMOVE_INFO)
|
||||
mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), " ! ", flags);
|
||||
|
||||
|
||||
#endif
|
||||
if(verify)
|
||||
return;
|
||||
}
|
||||
|
||||
success=true;
|
||||
|
||||
}
|
||||
~TlsClientStream()
|
||||
{
|
||||
|
||||
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||||
mbedtls_entropy_free(&entropy);
|
||||
mbedtls_ssl_config_free(&conf);
|
||||
|
||||
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
||||
mbedtls_psa_crypto_free();
|
||||
#endif /* MBEDTLS_USE_PSA_CRYPTO */
|
||||
|
||||
delete ls;
|
||||
}
|
||||
};
|
||||
|
||||
class TlsServerStream {
|
||||
GCList* ls;
|
||||
TCallable* read;
|
||||
TCallable* write;
|
||||
TCallable* close;
|
||||
TByteArray* readBuffer;
|
||||
TByteArray* writeBuffer;
|
||||
mbedtls_entropy_context entropy;
|
||||
mbedtls_ctr_drbg_context ctr_drbg;
|
||||
mbedtls_ssl_context ssl;
|
||||
mbedtls_ssl_config conf;
|
||||
mbedtls_x509_crt srvcert;
|
||||
mbedtls_x509_crt cachain;
|
||||
mbedtls_pk_context pkey;
|
||||
|
||||
|
||||
|
||||
static int strm_send(void* ctx,const unsigned char* buf,size_t len)
|
||||
{
|
||||
TlsServerStream* strm = static_cast<TlsServerStream*>(ctx);
|
||||
strm->writeBuffer->data.resize(len);
|
||||
memcpy(strm->writeBuffer->data.data(),buf,len);
|
||||
|
||||
auto res = strm->write->Call(*strm->ls,{strm->writeBuffer});
|
||||
if(std::holds_alternative<int64_t>(res))
|
||||
{
|
||||
auto num = std::get<int64_t>(res);
|
||||
|
||||
return (int)num;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
static int strm_recv(void* ctx,unsigned char* buf,size_t len)
|
||||
{
|
||||
TlsServerStream* strm = static_cast<TlsServerStream*>(ctx);
|
||||
strm->readBuffer->data.resize(len);
|
||||
|
||||
auto res = strm->read->Call(*strm->ls,{strm->readBuffer});
|
||||
if(std::holds_alternative<int64_t>(res))
|
||||
{
|
||||
auto num = std::get<int64_t>(res);
|
||||
if(num < 0) return num;
|
||||
size_t read = (size_t)num;
|
||||
if(read > len) read = len;
|
||||
memcpy(buf,strm->readBuffer->data.data(), read);
|
||||
return read;
|
||||
}
|
||||
return -1;
|
||||
|
||||
//return (int)strm->Read((uint8_t*)buf,len);
|
||||
}
|
||||
public:
|
||||
void Close()
|
||||
{
|
||||
close->Call(*ls,{});
|
||||
}
|
||||
bool success=false;
|
||||
bool isDoneReading =false;
|
||||
int64_t Read(uint8_t* buffer, size_t len)
|
||||
{
|
||||
if(isDoneReading) return 0;
|
||||
int64_t r = (int64_t)mbedtls_ssl_read(&ssl,buffer,len);
|
||||
if(r == 0) isDoneReading=true;
|
||||
if(r == -30848)
|
||||
{
|
||||
isDoneReading = true;
|
||||
return 0;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
int64_t Write(uint8_t* buffer, size_t len)
|
||||
{
|
||||
return mbedtls_ssl_write(&ssl,buffer,len);
|
||||
}
|
||||
|
||||
TlsServerStream(GC* gc,std::string cert, std::string chain, std::string privkey, TCallable* read, TCallable* write, TCallable* close)
|
||||
{
|
||||
ls = new GCList(gc);
|
||||
ls->Add(read);
|
||||
ls->Add(write);
|
||||
ls->Add(close);
|
||||
readBuffer = TByteArray::Create(ls);
|
||||
writeBuffer = TByteArray::Create(ls);
|
||||
this->read = read;
|
||||
this->write = write;
|
||||
this->close = close;
|
||||
|
||||
mbedtls_ssl_init(&ssl);
|
||||
mbedtls_ssl_config_init(&conf);
|
||||
mbedtls_x509_crt_init(&srvcert);
|
||||
mbedtls_x509_crt_init(&cachain);
|
||||
mbedtls_ctr_drbg_init(&ctr_drbg);
|
||||
mbedtls_entropy_init(&entropy);
|
||||
mbedtls_pk_init(&pkey);
|
||||
|
||||
const char* pers = "CrossLangTLS";
|
||||
int ret=0;
|
||||
|
||||
|
||||
|
||||
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
||||
psa_status_t status = psa_crypto_init();
|
||||
if (status != PSA_SUCCESS) {
|
||||
mbedtls_fprintf(stderr, "Failed to initialize PSA Crypto implementation: %d\n",
|
||||
(int) status);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
|
||||
(const unsigned char *) pers,
|
||||
strlen(pers))) != 0)
|
||||
{
|
||||
printf("FAILED mbedtls_ctr_drbg_seed\n");
|
||||
return;
|
||||
}
|
||||
ret = mbedtls_x509_crt_parse(&srvcert, (const unsigned char *) cert.c_str(),
|
||||
cert.size()+1);
|
||||
if(ret != 0) { printf("FAILED mbedtls_x509_crt_parse cert %i\n",ret); return;}
|
||||
ret = mbedtls_x509_crt_parse(&cachain, (const unsigned char *) chain.c_str(),
|
||||
chain.size()+1);
|
||||
|
||||
if(ret != 0) {printf("FAILED mbedtls_x509_crt_parse chain %i\n",ret); return;}
|
||||
|
||||
|
||||
ret = mbedtls_pk_parse_key(&pkey, (const unsigned char *) privkey.c_str(),
|
||||
privkey.size()+1, NULL, 0);
|
||||
if(ret != 0) {printf("FAILED mbedtls_pk_parse_key %i\n",ret); return;}
|
||||
if((ret = mbedtls_ssl_config_defaults(&conf,
|
||||
MBEDTLS_SSL_IS_SERVER,
|
||||
MBEDTLS_SSL_TRANSPORT_STREAM,
|
||||
MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
|
||||
{
|
||||
char buffer[100];
|
||||
mbedtls_strerror(ret,buffer,sizeof(buffer));
|
||||
printf("FAILED mbedtls_ssl_conf_defaults %s\n",buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
|
||||
|
||||
/* #if defined(MBEDTLS_SSL_CACHE_C)
|
||||
mbedtls_ssl_conf_session_cache(&conf, &cache,
|
||||
mbedtls_ssl_cache_get,
|
||||
mbedtls_ssl_cache_set);
|
||||
#endif*/
|
||||
mbedtls_ssl_conf_own_cert(&conf,&srvcert,&pkey);
|
||||
mbedtls_ssl_conf_ca_chain(&conf, &cachain, NULL);
|
||||
|
||||
mbedtls_ssl_set_bio(&ssl, static_cast<void*>(this),strm_send,strm_recv, NULL);
|
||||
|
||||
if((ret=mbedtls_ssl_setup(&ssl,&conf) != 0))
|
||||
{
|
||||
printf("FAILED mbedtls_ssl_setup %i\n",ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if((ret = mbedtls_ssl_handshake(&ssl)) != 0)
|
||||
{
|
||||
char buffer[100];
|
||||
mbedtls_strerror(ret,buffer,sizeof(buffer));
|
||||
printf("FAILED mbedtls_ssl_handshake %s\n",buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
success=true;
|
||||
|
||||
}
|
||||
~TlsServerStream()
|
||||
{
|
||||
mbedtls_x509_crt_free(&srvcert);
|
||||
mbedtls_pk_free(&pkey);
|
||||
|
||||
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||||
mbedtls_entropy_free(&entropy);
|
||||
mbedtls_ssl_config_free(&conf);
|
||||
|
||||
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
||||
mbedtls_psa_crypto_free();
|
||||
#endif /* MBEDTLS_USE_PSA_CRYPTO */
|
||||
|
||||
delete ls;
|
||||
}
|
||||
};
|
||||
static TObject Crypto_TlsClient(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
//TlsServerStream(GC* gc,std::string domain, std::string chain, bool verify, TCallable* read, TCallable* write, TCallable* close)
|
||||
if(args.size() == 6 && std::holds_alternative<std::string>(args[0]) && std::holds_alternative<std::string>(args[1]) && std::holds_alternative<bool>(args[2]) && std::holds_alternative<THeapObjectHolder>(args[3]) && std::holds_alternative<THeapObjectHolder>(args[4]) && std::holds_alternative<THeapObjectHolder>(args[5]))
|
||||
{
|
||||
std::string domain = std::get<std::string>(args[0]);
|
||||
std::string chain = std::get<std::string>(args[1]);
|
||||
bool verify = std::get<bool>(args[2]);
|
||||
TCallable* read = dynamic_cast<TCallable*>(std::get<THeapObjectHolder>(args[3]).obj);
|
||||
|
||||
TCallable* write = dynamic_cast<TCallable*>(std::get<THeapObjectHolder>(args[4]).obj);
|
||||
|
||||
TCallable* close = dynamic_cast<TCallable*>(std::get<THeapObjectHolder>(args[5]).obj);
|
||||
|
||||
if(read != nullptr && write != nullptr && close != nullptr)
|
||||
{
|
||||
auto serverStream = new TlsClientStream(ls.GetGC(),domain,chain,verify,read,write,close);
|
||||
if(!serverStream->success)
|
||||
{
|
||||
serverStream->Close();
|
||||
delete serverStream;
|
||||
return nullptr;
|
||||
}
|
||||
TNative* native = TNative::Create(ls,static_cast<void*>(serverStream),[](void* ptr)->void {
|
||||
if(ptr == nullptr) return;
|
||||
TlsClientStream* strm = static_cast<TlsClientStream*>(ptr);
|
||||
strm->Close();
|
||||
delete strm;
|
||||
});
|
||||
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
ls.GetGC()->BarrierBegin();
|
||||
dict->SetValue("_native",native);
|
||||
ls.GetGC()->BarrierEnd();
|
||||
dict->DeclareFunction(ls.GetGC(),"Read","Read data from Tls Connection",{"buffer","offset","length"},[native](GCList& ls2, std::vector<TObject> args2)->TObject {
|
||||
if(args2.size() == 3 && std::holds_alternative<THeapObjectHolder>(args2[0]) && std::holds_alternative<int64_t>(args2[1]) && std::holds_alternative<int64_t>(args2[2]))
|
||||
{
|
||||
auto byteArray = dynamic_cast<TByteArray*>(std::get<THeapObjectHolder>(args2[0]).obj);
|
||||
size_t offset = (size_t)std::get<int64_t>(args2[1]);
|
||||
size_t length = (size_t)std::get<int64_t>(args2[2]);
|
||||
|
||||
if(!native->GetDestroyed() && byteArray != nullptr && ((offset + length) <= byteArray->data.size()))
|
||||
{
|
||||
auto strm = static_cast<TlsClientStream*>(native->GetPointer());
|
||||
return strm->Read(byteArray->data.data()+offset, length);
|
||||
}
|
||||
}
|
||||
return Undefined();
|
||||
});
|
||||
dict->DeclareFunction(ls.GetGC(),"Write","Write data to Tls Connection",{"buffer","offset","length"},[native](GCList& ls2, std::vector<TObject> args2)->TObject {
|
||||
if(args2.size() == 3 && std::holds_alternative<THeapObjectHolder>(args2[0]) && std::holds_alternative<int64_t>(args2[1]) && std::holds_alternative<int64_t>(args2[2]))
|
||||
{
|
||||
auto byteArray = dynamic_cast<TByteArray*>(std::get<THeapObjectHolder>(args2[0]).obj);
|
||||
size_t offset = (size_t)std::get<int64_t>(args2[1]);
|
||||
size_t length = (size_t)std::get<int64_t>(args2[2]);
|
||||
|
||||
if(!native->GetDestroyed() && byteArray != nullptr && ((offset + length) <= byteArray->data.size()))
|
||||
{
|
||||
auto strm = static_cast<TlsClientStream*>(native->GetPointer());
|
||||
return strm->Write(byteArray->data.data()+offset, length);
|
||||
}
|
||||
}
|
||||
return Undefined();
|
||||
});
|
||||
dict->DeclareFunction(ls.GetGC(),"Close","Close Tls Connection",{},[native](GCList& ls2, std::vector<TObject> args2)->TObject {
|
||||
|
||||
if(!native->GetDestroyed())
|
||||
{
|
||||
native->Destroy();
|
||||
}
|
||||
|
||||
return Undefined();
|
||||
});
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
static TObject Crypto_TlsServer(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
//TlsServerStream(GC* gc,std::string cert, std::string chain, std::string privkey, TCallable* read, TCallable* write, TCallable* close)
|
||||
if(args.size() == 6 && std::holds_alternative<std::string>(args[0]) && std::holds_alternative<std::string>(args[1]) && std::holds_alternative<std::string>(args[2]) && std::holds_alternative<THeapObjectHolder>(args[3]) && std::holds_alternative<THeapObjectHolder>(args[4]) && std::holds_alternative<THeapObjectHolder>(args[5]))
|
||||
{
|
||||
std::string cert = std::get<std::string>(args[0]);
|
||||
std::string chain = std::get<std::string>(args[1]);
|
||||
std::string privkey = std::get<std::string>(args[2]);
|
||||
TCallable* read = dynamic_cast<TCallable*>(std::get<THeapObjectHolder>(args[3]).obj);
|
||||
|
||||
TCallable* write = dynamic_cast<TCallable*>(std::get<THeapObjectHolder>(args[4]).obj);
|
||||
|
||||
TCallable* close = dynamic_cast<TCallable*>(std::get<THeapObjectHolder>(args[5]).obj);
|
||||
|
||||
if(read != nullptr && write != nullptr && close != nullptr)
|
||||
{
|
||||
auto serverStream = new TlsServerStream(ls.GetGC(),cert,chain,privkey,read,write,close);
|
||||
if(!serverStream->success)
|
||||
{
|
||||
serverStream->Close();
|
||||
delete serverStream;
|
||||
return nullptr;
|
||||
}
|
||||
TNative* native = TNative::Create(ls,static_cast<void*>(serverStream),[](void* ptr)->void {
|
||||
if(ptr == nullptr) return;
|
||||
TlsServerStream* strm = static_cast<TlsServerStream*>(ptr);
|
||||
strm->Close();
|
||||
delete strm;
|
||||
});
|
||||
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
ls.GetGC()->BarrierBegin();
|
||||
dict->SetValue("_native",native);
|
||||
ls.GetGC()->BarrierEnd();
|
||||
dict->DeclareFunction(ls.GetGC(),"Read","Read data from Tls Connection",{"buffer","offset","length"},[native](GCList& ls2, std::vector<TObject> args2)->TObject {
|
||||
if(args2.size() == 3 && std::holds_alternative<THeapObjectHolder>(args2[0]) && std::holds_alternative<int64_t>(args2[1]) && std::holds_alternative<int64_t>(args2[2]))
|
||||
{
|
||||
auto byteArray = dynamic_cast<TByteArray*>(std::get<THeapObjectHolder>(args2[0]).obj);
|
||||
size_t offset = (size_t)std::get<int64_t>(args2[1]);
|
||||
size_t length = (size_t)std::get<int64_t>(args2[2]);
|
||||
if(!native->GetDestroyed() && byteArray != nullptr && ((offset + length) <= byteArray->data.size()))
|
||||
{
|
||||
auto strm = static_cast<TlsServerStream*>(native->GetPointer());
|
||||
return strm->Read(byteArray->data.data()+offset, length);
|
||||
}
|
||||
}
|
||||
return Undefined();
|
||||
});
|
||||
dict->DeclareFunction(ls.GetGC(),"Write","Write data to Tls Connection",{"buffer","offset","length"},[native](GCList& ls2, std::vector<TObject> args2)->TObject {
|
||||
if(args2.size() == 3 && std::holds_alternative<THeapObjectHolder>(args2[0]) && std::holds_alternative<int64_t>(args2[1]) && std::holds_alternative<int64_t>(args2[2]))
|
||||
{
|
||||
auto byteArray = dynamic_cast<TByteArray*>(std::get<THeapObjectHolder>(args2[0]).obj);
|
||||
size_t offset = (size_t)std::get<int64_t>(args2[1]);
|
||||
size_t length = (size_t)std::get<int64_t>(args2[2]);
|
||||
|
||||
|
||||
if(!native->GetDestroyed() && byteArray != nullptr && ((offset + length) <= byteArray->data.size()))
|
||||
{
|
||||
auto strm = static_cast<TlsServerStream*>(native->GetPointer());
|
||||
return strm->Write(byteArray->data.data()+offset, length);
|
||||
}
|
||||
}
|
||||
return Undefined();
|
||||
});
|
||||
dict->DeclareFunction(ls.GetGC(),"Close","Close Tls Connection",{},[native](GCList& ls2, std::vector<TObject> args2)->TObject {
|
||||
|
||||
if(!native->GetDestroyed())
|
||||
{
|
||||
native->Destroy();
|
||||
}
|
||||
|
||||
return Undefined();
|
||||
});
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
static TObject Crypto_Sha1(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
mbedtls_sha1_context* ctx = new mbedtls_sha1_context();
|
||||
mbedtls_sha1_init(ctx);
|
||||
mbedtls_sha1_starts(ctx);
|
||||
|
||||
TNative* native = TNative::Create(ls,ctx,[](void* _ctx)->void{
|
||||
if(_ctx == nullptr) return;
|
||||
mbedtls_sha1_context* ctx = (mbedtls_sha1_context*)_ctx;
|
||||
mbedtls_sha1_free(ctx);
|
||||
delete ctx;
|
||||
});
|
||||
ls.GetGC()->BarrierBegin();
|
||||
dict->SetValue("_native",native);
|
||||
ls.GetGC()->BarrierEnd();
|
||||
|
||||
dict->DeclareFunction(ls.GetGC(),"Update","Write more bytes",{"buffer","offset","size"},[native](GCList& ls2,std::vector<TObject> args2)->TObject{
|
||||
if(!native->GetDestroyed() && args2.size() == 3 && std::holds_alternative<THeapObjectHolder>(args2[0]) && std::holds_alternative<int64_t>(args2[1]) && std::holds_alternative<int64_t>(args2[2]))
|
||||
{
|
||||
auto buffer = dynamic_cast<TByteArray*>(std::get<THeapObjectHolder>(args2[0]).obj);
|
||||
auto offset = (size_t)std::get<int64_t>(args2[1]);
|
||||
auto len = (size_t)std::get<int64_t>(args2[2]);
|
||||
if(buffer != nullptr && (len+offset) < buffer->data.size())
|
||||
{
|
||||
|
||||
mbedtls_sha1_update((mbedtls_sha1_context*)native->GetPointer(),buffer->data.data() + offset, len);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
dict->DeclareFunction(ls.GetGC(),"Finish","Get hash",{},[native](GCList& ls2,std::vector<TObject> args2)->TObject{
|
||||
if(!native->GetDestroyed())
|
||||
{
|
||||
TByteArray* ba = TByteArray::Create(ls2);
|
||||
ba->data.resize(20);
|
||||
|
||||
mbedtls_sha1_finish((mbedtls_sha1_context*)native->GetPointer(),ba->data.data());
|
||||
|
||||
return ba;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
|
||||
|
||||
mbedtls_sha1_update(&ctx, (const unsigned char*)"Demi Lovato", 11);
|
||||
unsigned char sha1[20];
|
||||
mbedtls_sha1_finish(&ctx,sha1);
|
||||
|
||||
for(int i = 0;i<20;i++)
|
||||
{
|
||||
printf("%02x", sha1[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
mbedtls_sha1_free(&ctx);*/
|
||||
|
||||
return dict;
|
||||
}
|
||||
static TObject Crypto_Sha256(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
|
||||
bool is224=false;
|
||||
|
||||
if(args.size() == 1 && std::holds_alternative<bool>(args[0]))
|
||||
{
|
||||
is224 = std::get<bool>(args[1]);
|
||||
}
|
||||
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
mbedtls_sha256_context* ctx = new mbedtls_sha256_context();
|
||||
mbedtls_sha256_init(ctx);
|
||||
mbedtls_sha256_starts(ctx,is224 ? 1 : 0);
|
||||
|
||||
|
||||
TNative* native = TNative::Create(ls,ctx,[](void* _ctx)->void{
|
||||
if(_ctx == nullptr) return;
|
||||
mbedtls_sha256_context* ctx = (mbedtls_sha256_context*)_ctx;
|
||||
mbedtls_sha256_free(ctx);
|
||||
delete ctx;
|
||||
});
|
||||
ls.GetGC()->BarrierBegin();
|
||||
dict->SetValue("_native",native);
|
||||
ls.GetGC()->BarrierEnd();
|
||||
|
||||
dict->DeclareFunction(ls.GetGC(),"Update","Write more bytes",{"buffer","offset","size"},[native](GCList& ls2,std::vector<TObject> args2)->TObject{
|
||||
if(!native->GetDestroyed() && args2.size() == 3 && std::holds_alternative<THeapObjectHolder>(args2[0]) && std::holds_alternative<int64_t>(args2[1]) && std::holds_alternative<int64_t>(args2[2]))
|
||||
{
|
||||
auto buffer = dynamic_cast<TByteArray*>(std::get<THeapObjectHolder>(args2[0]).obj);
|
||||
auto offset = (size_t)std::get<int64_t>(args2[1]);
|
||||
auto len = (size_t)std::get<int64_t>(args2[2]);
|
||||
if(buffer != nullptr && (len+offset) < buffer->data.size())
|
||||
{
|
||||
|
||||
mbedtls_sha256_update((mbedtls_sha256_context*)native->GetPointer(),buffer->data.data() + offset, len);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
dict->DeclareFunction(ls.GetGC(),"Finish","Get hash",{},[native](GCList& ls2,std::vector<TObject> args2)->TObject{
|
||||
if(!native->GetDestroyed())
|
||||
{
|
||||
TByteArray* ba = TByteArray::Create(ls2);
|
||||
ba->data.resize(32);
|
||||
|
||||
mbedtls_sha256_finish((mbedtls_sha256_context*)native->GetPointer(),ba->data.data());
|
||||
|
||||
return ba;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
|
||||
return dict;
|
||||
}
|
||||
static TObject Crypto_Sha512(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
|
||||
bool is384=false;
|
||||
|
||||
if(args.size() == 1 && std::holds_alternative<bool>(args[0]))
|
||||
{
|
||||
is384 = std::get<bool>(args[1]);
|
||||
}
|
||||
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
mbedtls_sha512_context* ctx = new mbedtls_sha512_context();
|
||||
mbedtls_sha512_init(ctx);
|
||||
mbedtls_sha512_starts(ctx,is384 ? 1 : 0);
|
||||
|
||||
TNative* native = TNative::Create(ls,ctx,[](void* _ctx)->void{
|
||||
if(_ctx == nullptr) return;
|
||||
mbedtls_sha512_context* ctx = (mbedtls_sha512_context*)_ctx;
|
||||
mbedtls_sha512_free(ctx);
|
||||
delete ctx;
|
||||
});
|
||||
ls.GetGC()->BarrierBegin();
|
||||
dict->SetValue("_native",native);
|
||||
ls.GetGC()->BarrierEnd();
|
||||
|
||||
dict->DeclareFunction(ls.GetGC(),"Update","Write more bytes",{"buffer","offset","size"},[native](GCList& ls2,std::vector<TObject> args2)->TObject{
|
||||
if(!native->GetDestroyed() && args2.size() == 3 && std::holds_alternative<THeapObjectHolder>(args2[0]) && std::holds_alternative<int64_t>(args2[1]) && std::holds_alternative<int64_t>(args2[2]))
|
||||
{
|
||||
auto buffer = dynamic_cast<TByteArray*>(std::get<THeapObjectHolder>(args2[0]).obj);
|
||||
auto offset = (size_t)std::get<int64_t>(args2[1]);
|
||||
auto len = (size_t)std::get<int64_t>(args2[2]);
|
||||
if(buffer != nullptr && (len+offset) < buffer->data.size())
|
||||
{
|
||||
|
||||
mbedtls_sha512_update((mbedtls_sha512_context*)native->GetPointer(),buffer->data.data() + offset, len);
|
||||
}
|
||||
}
|
||||
return Undefined();
|
||||
});
|
||||
|
||||
dict->DeclareFunction(ls.GetGC(),"Finish","Get hash",{},[native](GCList& ls2,std::vector<TObject> args2)->TObject{
|
||||
if(!native->GetDestroyed())
|
||||
{
|
||||
TByteArray* ba = TByteArray::Create(ls2);
|
||||
ba->data.resize(64);
|
||||
|
||||
mbedtls_sha512_finish((mbedtls_sha512_context*)native->GetPointer(),ba->data.data());
|
||||
|
||||
return ba;
|
||||
}
|
||||
return Undefined();
|
||||
});
|
||||
|
||||
|
||||
|
||||
return dict;
|
||||
}
|
||||
#endif
|
||||
void TStd::RegisterCrypto(GC* gc,TRootEnvironment* env)
|
||||
{
|
||||
|
||||
env->permissions.canRegisterCrypto=true;
|
||||
#if defined(CROSSLANG_ENABLE_MBED)
|
||||
|
||||
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, "Sha1","Sha1 Algorithm (needed for WebSocket handshake/BitTorrent etc) (don't use unless you have no other choice)",{},Crypto_Sha1);
|
||||
dict->DeclareFunction(gc, "Sha256","Sha256 Algorithm",{"$is224"},Crypto_Sha256);
|
||||
dict->DeclareFunction(gc, "Sha512","Sha512 Algorithm",{"$is384"},Crypto_Sha512);
|
||||
dict->DeclareFunction(gc, "TlsClient", "Create TLS client for HTTPS client",{"domain","chain","verify","read","write","close"},Crypto_TlsClient);
|
||||
|
||||
dict->DeclareFunction(gc, "TlsServer", "Create TLS server for HTTPS server",{"cert","chain","privkey","read","write","close"},Crypto_TlsServer);
|
||||
gc->BarrierBegin();
|
||||
env->DeclareVariable("Crypto", dict);
|
||||
gc->BarrierEnd();
|
||||
dict = TDictionary::Create(ls);
|
||||
dict->DeclareFunction(gc, "Encode","Encode Base64",{"buffer","offset","count"},[](GCList& ls,std::vector<TObject> args)->TObject{
|
||||
TByteArray* bArray;
|
||||
int64_t offset;
|
||||
int64_t count;
|
||||
|
||||
if(!GetArgumentHeap<TByteArray*>(args,0,bArray))
|
||||
return nullptr;
|
||||
if(!GetArgument<int64_t>(args,1, offset))
|
||||
return nullptr;
|
||||
if(!GetArgument<int64_t>(args,2, count))
|
||||
return nullptr;
|
||||
|
||||
size_t off = (size_t)offset;
|
||||
size_t len = (size_t)count;
|
||||
|
||||
off = std::min(off, bArray->data.size());
|
||||
|
||||
len = std::min(len, bArray->data.size()-off);
|
||||
|
||||
size_t outLen = ((4 * len / 3) + 3) & ~3;
|
||||
|
||||
std::string str(outLen,'\0');
|
||||
|
||||
if(mbedtls_base64_encode((unsigned char*)str.data(),str.size(),&outLen,bArray->data.data(),bArray->data.size()) != 0)
|
||||
return nullptr;
|
||||
|
||||
str.resize(outLen);
|
||||
|
||||
return str;
|
||||
|
||||
//bArray->data.size();
|
||||
|
||||
//
|
||||
});
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
|
||||
TObject Dictionary_Items(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
|
||||
TDictionary* dict;
|
||||
|
||||
dict->items.begin();
|
||||
if(args.size() == 1 && std::holds_alternative<THeapObjectHolder>(args[0]))
|
||||
{
|
||||
auto item = dynamic_cast<TDictionary*>(std::get<THeapObjectHolder>(args[0]).obj);
|
||||
if(item != nullptr)
|
||||
{
|
||||
TDictionary* enumerableItem = TDictionary::Create(ls);
|
||||
ls.GetGC()->BarrierBegin();
|
||||
|
||||
auto fn = TExternalMethod::Create(ls,"Get Enumerator for Dictionary",{"dict"},[item](GCList& ls2, std::vector<TObject> args)->TObject {
|
||||
return TDictionaryEnumerator::Create(ls2,item);
|
||||
});
|
||||
fn->watch.push_back(item);
|
||||
|
||||
enumerableItem->SetValue("GetEnumerator", fn);
|
||||
|
||||
ls.GetGC()->BarrierEnd();
|
||||
|
||||
return enumerableItem;
|
||||
}
|
||||
}
|
||||
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
void TStd::RegisterDictionary(GC* gc,TRootEnvironment* env)
|
||||
{
|
||||
|
||||
env->permissions.canRegisterDictionary=true;
|
||||
GCList ls(gc);
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
|
||||
|
||||
gc->BarrierBegin();
|
||||
|
||||
dict->DeclareFunction(gc, "Items","Get Dictionary Item Enumerable, for the each(item : Dictionary.Items(myDict)){item.Key; item.Value;}",{"dictionary"},Dictionary_Items);
|
||||
|
||||
env->DeclareVariable("Dictionary", dict);
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
#include "../sago/platform_folders.h"
|
||||
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
static TObject Env_getPlatform(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
#if defined(__SWITCH__)
|
||||
return "Nintendo Switch";
|
||||
#endif
|
||||
#if defined(GEKKO)
|
||||
#if defined(HW_RVL)
|
||||
return "Nintendo Wii";
|
||||
#endif
|
||||
return "Nintendo Gamecube";
|
||||
#endif
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
return "Windows";
|
||||
#endif
|
||||
#if defined(linux)
|
||||
return "Linux";
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "TargetConditionals.h"
|
||||
#if TARGET_OS_MAC
|
||||
return "MacOS";
|
||||
#endif
|
||||
#if TARGET_OS_IOS
|
||||
return "iOS";
|
||||
#endif
|
||||
#if TARGET_OS_TV
|
||||
return "Apple TV";
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_WATCH
|
||||
return "Apple Watch";
|
||||
#endif
|
||||
|
||||
#if __EMSCRIPTEN__
|
||||
return "WebAssembly";
|
||||
#endif
|
||||
|
||||
return "Unknown Apple Device";
|
||||
#endif
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
static TObject Env_GetAt(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
std::string key;
|
||||
if(GetArgument(args,0,key))
|
||||
{
|
||||
auto res = getenv(key.c_str());
|
||||
if(res == nullptr) return nullptr;
|
||||
std::string value = res;
|
||||
return value;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static TObject Env_SetAt(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
std::string key;
|
||||
std::string value;
|
||||
if(GetArgument(args,0,key))
|
||||
{
|
||||
if(GetArgument(args,1,value))
|
||||
setenv(key.c_str(), value.c_str(),1);
|
||||
else
|
||||
unsetenv(key.c_str());
|
||||
return value;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static TObject Env_getDownloads(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
return sago::getDownloadFolder();
|
||||
}
|
||||
static TObject Env_getMusic(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
return sago::getMusicFolder();
|
||||
}
|
||||
static TObject Env_getPictures(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
return sago::getMusicFolder();
|
||||
}
|
||||
static TObject Env_getVideos(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
return sago::getVideoFolder();
|
||||
}
|
||||
static TObject Env_getDocuments(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
return sago::getDocumentsFolder();
|
||||
}
|
||||
static TObject Env_getConfig(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
return sago::getConfigHome();
|
||||
}
|
||||
|
||||
static TObject Env_getDesktop(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
return sago::getDesktopFolder();
|
||||
}
|
||||
static TObject Env_getState(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
return sago::getStateDir();
|
||||
}
|
||||
static TObject Env_getCache(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
return sago::getCacheDir();
|
||||
}
|
||||
static TObject Env_getData(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
return sago::getDataHome();
|
||||
}
|
||||
static TObject Env_getUser(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
return sago::getHomeDir();
|
||||
}
|
||||
void TStd::RegisterEnv(GC* gc, TRootEnvironment* env)
|
||||
{
|
||||
|
||||
env->permissions.canRegisterEnv=true;
|
||||
GCList ls(gc);
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
dict->DeclareFunction(gc,"GetAt","Get environment variable", {"key"}, Env_GetAt);
|
||||
dict->DeclareFunction(gc,"SetAt","Set environment variable", {"key","value"}, Env_SetAt);
|
||||
|
||||
dict->DeclareFunction(gc,"getDesktop","Get downloads folder",{},Env_getDownloads);
|
||||
dict->DeclareFunction(gc,"getDownloads","Get downloads folder",{},Env_getDownloads);
|
||||
dict->DeclareFunction(gc,"getDocuments","Get documents folder",{},Env_getDocuments);
|
||||
dict->DeclareFunction(gc,"getMusic","Get music folder",{},Env_getMusic);
|
||||
dict->DeclareFunction(gc,"getPictures","Get pictures folder",{},Env_getPictures);
|
||||
dict->DeclareFunction(gc,"getVideos","Get videos folder",{},Env_getVideos);
|
||||
dict->DeclareFunction(gc,"getState","Get state folder",{},Env_getState);
|
||||
dict->DeclareFunction(gc,"getCache","Get cache folder",{},Env_getCache);
|
||||
dict->DeclareFunction(gc,"getConfig","Get config folder",{},Env_getConfig);
|
||||
dict->DeclareFunction(gc,"getData","Get data folder",{},Env_getData);
|
||||
dict->DeclareFunction(gc,"getUser","Get user folder",{},Env_getUser);
|
||||
dict->DeclareFunction(gc,"getPlatform","Get platform name",{},Env_getPlatform);
|
||||
|
||||
gc->BarrierBegin();
|
||||
env->SetVariable("Env", dict);
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
|
||||
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
static TObject FS_SubdirFilesystem(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
TVFSHeapObject* vfsho;
|
||||
|
||||
Tesses::Framework::Filesystem::VFSPath path;
|
||||
|
||||
if(GetArgumentHeap(args,0,vfsho) && GetArgumentAsPath(args,1,path))
|
||||
{
|
||||
return TVFSHeapObject::Create(ls,new Tesses::Framework::Filesystem::SubdirFilesystem(new TObjectVFS(ls.GetGC(),vfsho),path,true));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static TObject FS_MountableFilesystem(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
TVFSHeapObject* vfsho;
|
||||
|
||||
if(GetArgumentHeap(args,0,vfsho))
|
||||
{
|
||||
return TVFSHeapObject::Create(ls,new Tesses::Framework::Filesystem::MountableFilesystem(new TObjectVFS(ls.GetGC(),vfsho),true));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static TObject FS_MemoryStream(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
bool writable;
|
||||
if(GetArgument(args,0,writable))
|
||||
{
|
||||
return TStreamHeapObject::Create(ls,new Tesses::Framework::Streams::MemoryStream(writable));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static TObject FS_CreateFilesystem(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
TDictionary* dict;
|
||||
if(GetArgumentHeap(args,0,dict))
|
||||
{
|
||||
return TVFSHeapObject::Create(ls, new TObjectVFS(ls.GetGC(),dict));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static TObject FS_CreateStream(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
TDictionary* dict;
|
||||
if(GetArgumentHeap(args,0,dict))
|
||||
{
|
||||
return TStreamHeapObject::Create(ls, new TObjectStream(ls.GetGC(),dict));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void TStd::RegisterIO(GC* gc,TRootEnvironment* env,bool enableLocalFilesystem)
|
||||
{
|
||||
|
||||
env->permissions.canRegisterIO=true;
|
||||
env->permissions.canRegisterLocalFS = enableLocalFilesystem;
|
||||
GCList ls(gc);
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
|
||||
gc->BarrierBegin();
|
||||
if(enableLocalFilesystem)
|
||||
{
|
||||
TVFSHeapObject* vfs = TVFSHeapObject::Create(ls, new Tesses::Framework::Filesystem::LocalFilesystem());
|
||||
|
||||
dict->SetValue("Local", vfs);
|
||||
}
|
||||
|
||||
dict->DeclareFunction(gc, "MountableFilesystem","Create a mountable filesystem",{"root"}, FS_MountableFilesystem);
|
||||
dict->DeclareFunction(gc, "SubdirFilesystem","Create a subdir filesystem",{"fs","subdir"}, FS_SubdirFilesystem);
|
||||
dict->DeclareFunction(gc, "MemoryStream","Create a memory stream",{"writable"}, FS_MemoryStream);
|
||||
dict->DeclareFunction(gc, "CreateStream","Create stream", {"strm"},FS_CreateStream);
|
||||
dict->DeclareFunction(gc, "CreateFilesystem","Create filesystem", {"fs"},FS_CreateFilesystem);
|
||||
env->DeclareVariable("FS", dict);
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
|
||||
*/
|
||||
|
||||
#include "CrossLang.hpp"
|
||||
|
||||
#if defined(CROSSLANG_ENABLE_JSON)
|
||||
#include <jansson.h>
|
||||
#endif
|
||||
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
|
||||
#if defined(CROSSLANG_ENABLE_JSON)
|
||||
static json_t* JsonSerialize(TObject v)
|
||||
{
|
||||
if(std::holds_alternative<std::nullptr_t>(v)) return json_null();
|
||||
if(std::holds_alternative<int64_t>(v)) return json_integer(std::get<int64_t>(v));
|
||||
if(std::holds_alternative<double>(v)) return json_real(std::get<double>(v));
|
||||
if(std::holds_alternative<bool>(v)) return json_boolean(std::get<bool>(v));
|
||||
if(std::holds_alternative<std::string>(v))
|
||||
{
|
||||
std::string txt = std::get<std::string>(v);
|
||||
return json_stringn(txt.c_str(),txt.size());
|
||||
}
|
||||
if(std::holds_alternative<THeapObjectHolder>(v))
|
||||
{
|
||||
auto obj = std::get<THeapObjectHolder>(v).obj;
|
||||
auto ls = dynamic_cast<TList*>(obj);
|
||||
auto dict = dynamic_cast<TDictionary*>(obj);
|
||||
|
||||
if(ls != nullptr)
|
||||
{
|
||||
json_t* items=json_array();
|
||||
for(int64_t i = 0; i < ls->Count(); i++)
|
||||
{
|
||||
json_array_append_new(items,JsonSerialize(ls->Get(i)));
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
if(dict != nullptr)
|
||||
{
|
||||
json_t* obj = json_object();
|
||||
for(auto item : dict->items)
|
||||
{
|
||||
json_object_setn_new(obj, item.first.c_str(), item.first.size(),JsonSerialize(item.second));
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
return json_null();
|
||||
}
|
||||
static TObject Json_Encode(GCList& ls2, std::vector<TObject> args)
|
||||
{
|
||||
if(args.size() >= 1)
|
||||
{
|
||||
bool indent = (args.size() == 2 && std::holds_alternative<bool>(args[1]) && std::get<bool>(args[1]));
|
||||
|
||||
auto json = JsonSerialize(args[0]);
|
||||
char* txt = json_dumps(json, indent ? JSON_INDENT(4) : 0);
|
||||
std::string str = txt;
|
||||
free(txt);
|
||||
json_decref(json);
|
||||
return str;
|
||||
}
|
||||
return "null";
|
||||
}
|
||||
static TObject JsonDeserialize(GCList& ls2,json_t* json)
|
||||
{
|
||||
if(json == nullptr) return nullptr;
|
||||
if(json_is_null(json)) return nullptr;
|
||||
if(json_is_true(json)) return true;
|
||||
if(json_is_false(json)) return false;
|
||||
if(json_is_integer(json)) return (int64_t)json_integer_value(json);
|
||||
if(json_is_real(json)) return json_real_value(json);
|
||||
if(json_is_string(json))
|
||||
{
|
||||
return std::string(json_string_value(json),json_string_length(json));
|
||||
}
|
||||
if(json_is_array(json))
|
||||
{
|
||||
TList* ls = TList::Create(ls2);
|
||||
json_t* item;
|
||||
size_t index;
|
||||
json_array_foreach(json,index, item)
|
||||
{
|
||||
auto itemRes = JsonDeserialize(ls2,item);
|
||||
ls2.GetGC()->BarrierBegin();
|
||||
ls->Add(itemRes);
|
||||
ls2.GetGC()->BarrierEnd();
|
||||
}
|
||||
return ls;
|
||||
}
|
||||
if(json_is_object(json))
|
||||
{
|
||||
|
||||
TDictionary* dict = TDictionary::Create(ls2);
|
||||
void* n;
|
||||
const char* key;
|
||||
size_t len;
|
||||
json_t* value;
|
||||
|
||||
json_object_keylen_foreach_safe(json,n,key,len,value)
|
||||
{
|
||||
auto itemRes = JsonDeserialize(ls2,value);
|
||||
ls2.GetGC()->BarrierBegin();
|
||||
dict->SetValue(std::string(key,len),itemRes);
|
||||
ls2.GetGC()->BarrierEnd();
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
static TObject Json_Decode(GCList& ls2,std::vector<TObject> args)
|
||||
{
|
||||
if(args.size() > 0 && std::holds_alternative<std::string>(args[0]))
|
||||
{
|
||||
std::string jsonText = std::get<std::string>(args[0]);
|
||||
json_t* json = json_loadb(jsonText.c_str(), jsonText.size(),0,NULL);
|
||||
|
||||
auto res = JsonDeserialize(ls2, json);
|
||||
json_decref(json);
|
||||
return res;
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
#endif
|
||||
void TStd::RegisterJson(GC* gc,TRootEnvironment* env)
|
||||
{
|
||||
|
||||
env->permissions.canRegisterJSON=true;
|
||||
#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);
|
||||
|
||||
|
||||
gc->BarrierBegin();
|
||||
env->DeclareVariable("Json", dict);
|
||||
gc->BarrierEnd();
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,555 @@
|
|||
#include "CrossLang.hpp"
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <csignal>
|
||||
#include <arpa/inet.h>
|
||||
#include <iostream>
|
||||
#include <netinet/tcp.h>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
using namespace Tesses::Framework::Streams;
|
||||
using namespace Tesses::Framework::Http;
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
TServerHeapObject* TServerHeapObject::Create(GCList& ls, Tesses::Framework::Http::IHttpServer* svr)
|
||||
{
|
||||
TServerHeapObject* ho = new TServerHeapObject();
|
||||
ls.Add(ho);
|
||||
auto gc = ls.GetGC();
|
||||
gc->Watch(ho);
|
||||
ho->server = svr;
|
||||
return ho;
|
||||
}
|
||||
TServerHeapObject* TServerHeapObject::Create(GCList* ls, Tesses::Framework::Http::IHttpServer* svr)
|
||||
{
|
||||
TServerHeapObject* ho = new TServerHeapObject();
|
||||
ls->Add(ho);
|
||||
auto gc = ls->GetGC();
|
||||
gc->Watch(ho);
|
||||
ho->server = svr;
|
||||
return ho;
|
||||
}
|
||||
|
||||
void TServerHeapObject::Close()
|
||||
{
|
||||
if(this->server != nullptr)
|
||||
{
|
||||
delete this->server;
|
||||
this->server = nullptr;
|
||||
}
|
||||
}
|
||||
TServerHeapObject::~TServerHeapObject()
|
||||
{
|
||||
if(this->server != nullptr)
|
||||
{
|
||||
delete this->server;
|
||||
}
|
||||
}
|
||||
static TDictionary* CreateDictionaryFromHttpDictionary(GCList& ls, Tesses::Framework::Http::HttpDictionary* dict0)
|
||||
{
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
|
||||
auto gc =ls.GetGC();
|
||||
//gc->BarrierBegin();
|
||||
|
||||
|
||||
dict->DeclareFunction(gc,"AddValue","Add item",{"key","value"},[dict0](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {
|
||||
std::string key;
|
||||
std::string value;
|
||||
if(GetArgument(args2,0,key) && GetArgument(args2,1,value))
|
||||
{
|
||||
dict0->AddValue(key,value);
|
||||
}
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
dict->DeclareFunction(gc,"SetValue","Set item",{"key","value"},[dict0](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {
|
||||
std::string key;
|
||||
std::string value;
|
||||
if(GetArgument(args2,0,key) && GetArgument(args2,1,value))
|
||||
{
|
||||
dict0->SetValue(key,value);
|
||||
}
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
dict->DeclareFunction(gc,"Clear","Clear items",{},[dict0](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {
|
||||
dict0->Clear();
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
dict->DeclareFunction(gc,"GetFirstBoolean","Get First boolean",{"key"},[dict0](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {
|
||||
std::string key;
|
||||
if(GetArgument(args2,0,key))
|
||||
{
|
||||
return dict0->GetFirstBoolean(key);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
dict->DeclareFunction(gc,"TryGetFirst","Try Get first string",{"key"},[dict0](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {
|
||||
std::string key;
|
||||
std::string value;
|
||||
if(GetArgument(args2,0,key) && dict0->TryGetFirst(key,value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return nullptr;
|
||||
});
|
||||
dict->DeclareFunction(gc,"TryGetFirstDouble","Try Get first double",{"key"},[dict0](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {
|
||||
std::string key;
|
||||
double value;
|
||||
if(GetArgument(args2,0,key) && dict0->TryGetFirstDouble(key,value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return nullptr;
|
||||
});
|
||||
dict->DeclareFunction(gc,"TryGetFirstInt","Try Get first integer",{"key"},[dict0](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {
|
||||
std::string key;
|
||||
int64_t value;
|
||||
if(GetArgument(args2,0,key) && dict0->TryGetFirstInt(key,value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
dict->DeclareFunction(gc, "ToList","To List",{}, [dict0](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {
|
||||
TList* ls = TList::Create(ls2);
|
||||
for(auto item : dict0->kvp)
|
||||
{
|
||||
for(auto i : item.second)
|
||||
{
|
||||
auto d = TDictionary::Create(ls2);
|
||||
ls2.GetGC()->BarrierBegin();
|
||||
d->SetValue("Key", item.first);
|
||||
d->SetValue("Value", i);
|
||||
ls->Add(d);
|
||||
ls2.GetGC()->BarrierEnd();
|
||||
}
|
||||
}
|
||||
return ls;
|
||||
});
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
static TDictionary* CreateDictionaryFromServerContext(GCList& ls,ServerContext* ctx)
|
||||
{
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
|
||||
auto gc =ls.GetGC();
|
||||
gc->BarrierBegin();
|
||||
dict->SetValue("native",TNative::Create(ls,ctx,[](void*)->void {}));
|
||||
dict->SetValue("Encrypted",ctx->encrypted);
|
||||
dict->SetValue("Method",ctx->method);
|
||||
dict->SetValue("IP",ctx->ip);
|
||||
dict->SetValue("Port",(int64_t)ctx->port);
|
||||
dict->SetValue("OriginalPath",ctx->originalPath);
|
||||
dict->SetValue("QueryParams",CreateDictionaryFromHttpDictionary(ls,&ctx->queryParams));
|
||||
dict->SetValue("RequestHeaders",CreateDictionaryFromHttpDictionary(ls,&ctx->requestHeaders));
|
||||
dict->SetValue("ResponseHeaders",CreateDictionaryFromHttpDictionary(ls,&ctx->responseHeaders));
|
||||
gc->BarrierEnd();
|
||||
|
||||
dict->DeclareFunction(gc,"GetStream","Get streams",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {
|
||||
return TStreamHeapObject::Create(ls2, &ctx->GetStream());
|
||||
});
|
||||
dict->DeclareFunction(gc,"OpenRequestStream","Open Request Stream",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {
|
||||
return TStreamHeapObject::Create(ls2, ctx->OpenRequestStream());
|
||||
});
|
||||
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,"getNeedToParseFormData","Check if Need to parse form data",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject{
|
||||
return ctx->NeedToParseFormData();
|
||||
});
|
||||
|
||||
/*dict->DeclareFunction(gc,"ReadString","Read string from request",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject{
|
||||
return ctx->ReadString();
|
||||
});*/
|
||||
|
||||
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);
|
||||
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);
|
||||
return dict;
|
||||
});
|
||||
dict->DeclareFunction(gc,"WithContentDisposition","Set content disposition",{"filename","inline"},[ctx,dict](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject{
|
||||
std::string filename;
|
||||
bool isInline;
|
||||
if(GetArgument(args2,0,filename) && GetArgument(args2,1,isInline))
|
||||
ctx->WithContentDisposition(filename,isInline);
|
||||
return dict;
|
||||
});
|
||||
dict->DeclareFunction(gc,"WithHeader","Add header",{"key","value"},[ctx,dict](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject{
|
||||
std::string key;
|
||||
std::string value;
|
||||
if(GetArgument(args2,0,key) && GetArgument(args2,1,value))
|
||||
ctx->WithHeader(key,value);
|
||||
return dict;
|
||||
});
|
||||
dict->DeclareFunction(gc,"WithSingleHeader","Set header",{"key","value"},[ctx,dict](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject{
|
||||
std::string key;
|
||||
std::string value;
|
||||
if(GetArgument(args2,0,key) && GetArgument(args2,1,value))
|
||||
ctx->WithSingleHeader(key,value);
|
||||
return dict;
|
||||
});
|
||||
dict->DeclareFunction(gc,"SendBytes","Send bytes",{"ba"},[ctx,dict](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject{
|
||||
TByteArray* ba;
|
||||
if(GetArgumentHeap(args2,0,ba))
|
||||
ctx->SendBytes(ba->data);
|
||||
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,"getOriginalPathWithQuery","Get original path with query parameters",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {return ctx->GetOriginalPathWithQuery();});
|
||||
dict->DeclareFunction(gc,"getPath","Get path",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {return ctx->path;});
|
||||
dict->DeclareFunction(gc,"setPath","Set path",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {
|
||||
std::string str;
|
||||
if(GetArgument(args2,0,str))
|
||||
{
|
||||
ctx->path = str;
|
||||
return str;
|
||||
}
|
||||
return nullptr;
|
||||
});
|
||||
dict->DeclareFunction(gc,"getVersion","Get version",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {return ctx->version;});
|
||||
dict->DeclareFunction(gc,"setVersion","Set version",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {
|
||||
std::string str;
|
||||
if(GetArgument(args2,0,str))
|
||||
{
|
||||
ctx->version = str;
|
||||
return str;
|
||||
}
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
dict->DeclareFunction(gc,"getStatusCode","Get status code",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {return (int64_t)ctx->statusCode;});
|
||||
dict->DeclareFunction(gc,"setStatusCode","Set status code",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {
|
||||
int64_t sc;
|
||||
if(GetArgument(args2,0,sc))
|
||||
{
|
||||
ctx->statusCode = (StatusCode)sc;
|
||||
return sc;
|
||||
}
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
TObjectHttpServer::TObjectHttpServer(GC* gc,TObject obj)
|
||||
{
|
||||
this->ls=new GCList(gc);
|
||||
this->obj = obj;
|
||||
}
|
||||
|
||||
class TDictionaryHttpRequestBody : public HttpRequestBody
|
||||
{
|
||||
GC* gc;
|
||||
TDictionary* req;
|
||||
public:
|
||||
TDictionaryHttpRequestBody(GC* gc,TDictionary* req)
|
||||
{
|
||||
this->gc = gc;
|
||||
this->req = req;
|
||||
}
|
||||
|
||||
void HandleHeaders(HttpDictionary& dict)
|
||||
{
|
||||
GCList ls(gc);
|
||||
auto res=CreateDictionaryFromHttpDictionary(ls,&dict);
|
||||
req->CallMethod(ls,"HandleHeaders",{res});
|
||||
}
|
||||
void Write(Tesses::Framework::Streams::Stream* strm)
|
||||
{
|
||||
GCList ls(gc);
|
||||
auto res=TStreamHeapObject::Create(ls,strm);
|
||||
req->CallMethod(ls,"Write",{res});
|
||||
res->stream=nullptr;
|
||||
}
|
||||
|
||||
~TDictionaryHttpRequestBody()
|
||||
{
|
||||
GCList ls(gc);
|
||||
req->CallMethod(ls,"Close",{});
|
||||
}
|
||||
};
|
||||
|
||||
bool TObjectHttpServer::Handle(ServerContext& ctx)
|
||||
{
|
||||
TCallable* callable;
|
||||
TDictionary* dict;
|
||||
TServerHeapObject* server;
|
||||
if(GetObjectHeap<TCallable*>(this->obj,callable))
|
||||
{
|
||||
GCList ls2(this->ls->GetGC());
|
||||
auto res = CreateDictionaryFromServerContext(ls2,&ctx);
|
||||
bool result;
|
||||
auto out = callable->Call(ls2,{res});
|
||||
if(GetObject(out,result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else if(GetObjectHeap<TDictionary*>(this->obj,dict))
|
||||
{
|
||||
GCList ls2(this->ls->GetGC());
|
||||
auto res = CreateDictionaryFromServerContext(ls2,&ctx);
|
||||
bool result;
|
||||
auto out = dict->CallMethod(ls2,"Handle",{res});
|
||||
if(GetObject(out,result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else if(GetObjectHeap<TServerHeapObject*>(this->obj,server))
|
||||
{
|
||||
return server->server->Handle(ctx);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
TObjectHttpServer::~TObjectHttpServer()
|
||||
{
|
||||
delete ls;
|
||||
}
|
||||
|
||||
static TObject Net_UrlDecode(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
if(args.size() == 1 && std::holds_alternative<std::string>(args[0]))
|
||||
{
|
||||
return HttpUtils::UrlDecode(std::get<std::string>(args[0]));
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
static TObject Net_UrlEncode(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
if(args.size() == 1 && std::holds_alternative<std::string>(args[0]))
|
||||
{
|
||||
return HttpUtils::UrlEncode(std::get<std::string>(args[0]));
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
static TObject Net_UrlPathDecode(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
if(args.size() == 1 && std::holds_alternative<std::string>(args[0]))
|
||||
{
|
||||
return HttpUtils::UrlPathDecode(std::get<std::string>(args[0]));
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
static TObject Net_UrlPathEncode(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
if(args.size() == 1 && std::holds_alternative<std::string>(args[0]))
|
||||
{
|
||||
return HttpUtils::UrlPathEncode(std::get<std::string>(args[0]));
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
static TObject Net_NetworkStream(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
bool ipv6;
|
||||
bool datagram;
|
||||
if(GetArgument(args,0,ipv6) && GetArgument(args,1,datagram))
|
||||
{
|
||||
return TStreamHeapObject::Create(ls,new NetworkStream(ipv6,datagram));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static TObject Net_Http_ListenSimpleWithLoop(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
int64_t port;
|
||||
if(GetArgument(args,1,port))
|
||||
{
|
||||
TObjectHttpServer httpServer(ls.GetGC(),args[0]);
|
||||
uint16_t p = (uint16_t)port;
|
||||
HttpServer server(p,httpServer);
|
||||
server.StartAccepting();
|
||||
Tesses::Framework::TF_RunEventLoop();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static TObject Net_Http_MimeType(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
Tesses::Framework::Filesystem::VFSPath p;
|
||||
if(GetArgumentAsPath(args,0,p))
|
||||
{
|
||||
std::filesystem::path p2 = p.GetFileName();
|
||||
return HttpUtils::MimeType(p2);
|
||||
}
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
static TObject Net_Http_MakeRequest(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
auto gc = ls.GetGC();
|
||||
std::string url;
|
||||
|
||||
if(GetArgument(args,0,url))
|
||||
{
|
||||
TDictionary* body1=nullptr;
|
||||
TNative* body2=nullptr;
|
||||
HttpRequest req;
|
||||
req.method = "GET";
|
||||
req.ignoreSSLErrors=false;
|
||||
req.followRedirects=false;
|
||||
req.trusted_root_cert_bundle="";
|
||||
req.url = url;
|
||||
req.body = nullptr;
|
||||
|
||||
TDictionary* options;
|
||||
if(GetArgumentHeap(args,1,options))
|
||||
{
|
||||
gc->BarrierBegin();
|
||||
auto _obj = options->GetValue("Method");
|
||||
GetObject(_obj,req.method);
|
||||
_obj = options->GetValue("IgnoreSSLErrors");
|
||||
GetObject(_obj,req.ignoreSSLErrors);
|
||||
_obj = options->GetValue("FollowRedirects");
|
||||
GetObject(_obj,req.followRedirects);
|
||||
_obj = options->GetValue("TrustedRootCertBundle");
|
||||
GetObject(_obj,req.trusted_root_cert_bundle);
|
||||
|
||||
_obj = options->GetValue("RequestHeaders");
|
||||
|
||||
TList* headers;
|
||||
|
||||
if(GetObjectHeap(_obj, headers))
|
||||
{
|
||||
for(int64_t index = 0; index < headers->Count();index ++)
|
||||
{
|
||||
_obj = headers->Get(index);
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(_obj,dict))
|
||||
{
|
||||
std::string key={};
|
||||
std::string value={};
|
||||
_obj = dict->GetValue("Key");
|
||||
GetObject(_obj,key);
|
||||
_obj = dict->GetValue("Value");
|
||||
|
||||
req.requestHeaders.AddValue(key,value);
|
||||
}
|
||||
}
|
||||
}
|
||||
_obj = options->GetValue("Body");
|
||||
|
||||
|
||||
|
||||
if(GetObjectHeap(_obj, body1))
|
||||
{
|
||||
req.body = new TDictionaryHttpRequestBody(gc,body1);
|
||||
}
|
||||
else if(GetObjectHeap(_obj,body2) && !body2->GetDestroyed())
|
||||
{
|
||||
req.body = static_cast<HttpRequestBody*>(body2->GetPointer());
|
||||
}
|
||||
|
||||
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
|
||||
|
||||
HttpResponse* resp = new HttpResponse(req);
|
||||
|
||||
if(req.body != nullptr)
|
||||
{
|
||||
if(body2 != nullptr)
|
||||
{
|
||||
body2->Destroy();
|
||||
}
|
||||
else if(body1 != nullptr)
|
||||
{
|
||||
delete req.body;
|
||||
}
|
||||
}
|
||||
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
gc->BarrierBegin();
|
||||
|
||||
TNative* nat = TNative::Create(ls,resp,[](void* ptr)->void{
|
||||
HttpResponse* resp0 = static_cast<HttpResponse*>(ptr);
|
||||
delete resp0;
|
||||
});
|
||||
|
||||
dict->SetValue("native", nat);
|
||||
|
||||
auto copyToStream = TExternalMethod::Create(ls,"Copy To a stream",{"stream"},[resp](GCList& ls, std::vector<TObject> args)->TObject{
|
||||
TStreamHeapObject* strm;
|
||||
if(GetArgumentHeap(args,0,strm))
|
||||
{
|
||||
resp->CopyToStream(strm->stream);
|
||||
}
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
copyToStream->watch.push_back(dict);
|
||||
dict->SetValue("CopyToStream",copyToStream);
|
||||
|
||||
|
||||
auto readAsString = TExternalMethod::Create(ls,"Read as string",{},[resp](GCList& ls, std::vector<TObject> args)->TObject{
|
||||
return resp->ReadAsString();
|
||||
});
|
||||
|
||||
readAsString->watch.push_back(dict);
|
||||
dict->SetValue("ReadAsString",readAsString);
|
||||
|
||||
auto readAsStream = TExternalMethod::Create(ls,"Read as stream",{},[resp](GCList& ls, std::vector<TObject> args)->TObject{
|
||||
auto res = resp->ReadAsStream();
|
||||
return TStreamHeapObject::Create(ls, res);
|
||||
});
|
||||
|
||||
readAsStream->watch.push_back(dict);
|
||||
dict->SetValue("ReadAsStream",readAsStream);
|
||||
dict->SetValue("StatusCode",(int64_t)resp->statusCode);
|
||||
|
||||
dict->SetValue("Version",resp->version);
|
||||
|
||||
dict->SetValue("ResponseHeaders",CreateDictionaryFromHttpDictionary(ls,&resp->responseHeaders));
|
||||
|
||||
gc->BarrierEnd();
|
||||
return dict;
|
||||
}
|
||||
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TStd::RegisterNet(GC* gc, TRootEnvironment* env)
|
||||
{
|
||||
|
||||
env->permissions.canRegisterNet=true;
|
||||
GCList ls(gc);
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
|
||||
TDictionary* http = TDictionary::Create(ls);
|
||||
http->DeclareFunction(gc, "UrlEncode","Url encode query param",{"param"}, Net_UrlEncode);
|
||||
http->DeclareFunction(gc, "UrlDecode","Url decode query param",{"param"}, Net_UrlDecode);
|
||||
http->DeclareFunction(gc, "UrlPathEncode","Url encode path",{"path"}, Net_UrlPathEncode);
|
||||
http->DeclareFunction(gc, "UrlPathDecode","Url decode path",{"path"}, Net_UrlPathDecode);
|
||||
http->DeclareFunction(gc, "MimeType","Get mimetype from extension",{"ext"},Net_Http_MimeType);
|
||||
|
||||
|
||||
//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);
|
||||
|
||||
dict->DeclareFunction(gc, "NetworkStream","Create a network stream",{"ipv6","datagram"},Net_NetworkStream);
|
||||
gc->BarrierBegin();
|
||||
dict->SetValue("Http", http);
|
||||
env->DeclareVariable("Net", dict);
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#include "CrossLang.hpp"
|
||||
#if defined(GEKKO)
|
||||
#include <ogcsys.h>
|
||||
#include <gccore.h>
|
||||
#include <ogc/pad.h>
|
||||
#if defined(HW_RVL)
|
||||
#include <wiiuse/wpad.h>
|
||||
#endif
|
||||
#endif
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
void TStd::RegisterOGC(GC* gc, TRootEnvironment* env)
|
||||
{
|
||||
|
||||
env->permissions.canRegisterOGC=true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
|
||||
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
bool GetObjectAsPath(TObject& obj, Tesses::Framework::Filesystem::VFSPath& path, bool allowString)
|
||||
{
|
||||
if(GetObject(obj,path)) return true;
|
||||
std::string str;
|
||||
if(allowString && GetObject<std::string>(obj,str))
|
||||
{
|
||||
path = Tesses::Framework::Filesystem::VFSPath(str);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool GetArgumentAsPath(std::vector<TObject>& args, size_t index, Tesses::Framework::Filesystem::VFSPath& path,bool allowString)
|
||||
{
|
||||
if(GetArgument(args,index,path)) return true;
|
||||
std::string str;
|
||||
if(allowString && GetArgument<std::string>(args,index,str))
|
||||
{
|
||||
path = Tesses::Framework::Filesystem::VFSPath(str);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static TObject Path_Root(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
auto res = Tesses::Framework::Filesystem::VFSPath();\
|
||||
res.relative=false;
|
||||
return res;
|
||||
}
|
||||
static TObject Path_FromString(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
std::string str;
|
||||
if(GetArgument(args,0,str))
|
||||
{
|
||||
return Tesses::Framework::Filesystem::VFSPath(str);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static TObject Path_Create(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
TList* myls;
|
||||
bool relative;
|
||||
if(GetArgument(args,0,relative) && GetArgumentHeap(args,1,myls))
|
||||
{
|
||||
std::vector<std::string> items;
|
||||
for(int64_t i = 0; i < myls->Count(); i++)
|
||||
{
|
||||
std::string str;
|
||||
TObject o =myls->Get(i);
|
||||
if(GetObject<std::string>(o,str))
|
||||
{
|
||||
items.push_back(str);
|
||||
}
|
||||
}
|
||||
auto res = Tesses::Framework::Filesystem::VFSPath(items);
|
||||
res.relative = relative;
|
||||
return res;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
void TStd::RegisterPath(GC* gc,TRootEnvironment* env)
|
||||
{
|
||||
|
||||
env->permissions.canRegisterPath=true;
|
||||
GCList ls(gc);
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
|
||||
gc->BarrierBegin();
|
||||
dict->DeclareFunction(gc,"FromString","Create a Path from string",{"path"},Path_FromString);
|
||||
dict->DeclareFunction(gc,"Create","Create a Path from parts",{"relative","parts"},Path_Create);
|
||||
dict->DeclareFunction(gc,"Root","Create Absolute Root Path",{}, Path_Root);
|
||||
env->DeclareVariable("Path", dict);
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
#if defined(GEKKO)
|
||||
#undef CROSSLANG_ENABLE_PROCESS
|
||||
#endif
|
||||
|
||||
//#if defined(CROSSLANG_ENABLE_PROCESS)
|
||||
#include "subprocess.h"
|
||||
//#endif
|
||||
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
//#if defined(CROSSLANG_ENABLE_PROCESS)
|
||||
static TObject Process_Start(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
|
||||
//Process.Start({
|
||||
// FileName = "git",
|
||||
// Arguments = ["clone","https://gitea.site.tesses.net/tesses50/crosslang.git"],
|
||||
// Environment = []
|
||||
//})
|
||||
|
||||
|
||||
}
|
||||
//#endif
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
#include "CrossLang.hpp"
|
||||
#if defined(CROSSLANG_ENABLE_SDL2)
|
||||
#include <SDL2/SDL.h>
|
||||
#endif
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
#if defined(CROSSLANG_ENABLE_SDL2)
|
||||
static TObject SDL2_RenderPresent(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
TNative* renderer;
|
||||
if(GetArgumentHeap<TNative*>(args,0,renderer) && !renderer->GetDestroyed())
|
||||
{
|
||||
SDL_RenderPresent((SDL_Renderer*)renderer->GetPointer());
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
static TObject SDL2_RenderClear(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
TNative* renderer;
|
||||
if(GetArgumentHeap<TNative*>(args,0,renderer) && !renderer->GetDestroyed())
|
||||
{
|
||||
return SDL_RenderClear((SDL_Renderer*)renderer->GetPointer())==0;
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
static TObject SDL2_SetRenderDrawColor(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
TNative* renderer;
|
||||
int64_t r;
|
||||
int64_t g;
|
||||
int64_t b;
|
||||
int64_t a;
|
||||
if(GetArgumentHeap<TNative*>(args,0,renderer) && !renderer->GetDestroyed() && GetArgument<int64_t>(args,1,r) && GetArgument<int64_t>(args,2,g) && GetArgument<int64_t>(args,3,b) && GetArgument<int64_t>(args,4,a))
|
||||
{
|
||||
return SDL_SetRenderDrawColor((SDL_Renderer*)renderer->GetPointer(),(Uint8)r,(Uint8)g,(Uint8)b,(Uint8)a) == 0;
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
|
||||
static TObject SDL2_CreateRenderer(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
TNative* window;
|
||||
int64_t index;
|
||||
int64_t flags;
|
||||
if(GetArgumentHeap<TNative*>(args,0,window) && !window->GetDestroyed() && GetArgument<int64_t>(args,1,index) && GetArgument<int64_t>(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<TObject> args)
|
||||
{
|
||||
if(args.size() == 6 && std::holds_alternative<std::string>(args[0]) && std::holds_alternative<int64_t>(args[1]) && std::holds_alternative<int64_t>(args[2]) && std::holds_alternative<int64_t>(args[3]) && std::holds_alternative<int64_t>(args[4]) && std::holds_alternative<int64_t>(args[5]))
|
||||
{
|
||||
SDL_Window* window = SDL_CreateWindow(std::get<std::string>(args[0]).c_str(),(int)std::get<int64_t>(args[1]),(int)std::get<int64_t>(args[2]),(int)std::get<int64_t>(args[3]),(int)std::get<int64_t>(args[4]),(uint32_t)std::get<int64_t>(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, "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);
|
||||
|
||||
gc->BarrierBegin();
|
||||
env->DeclareVariable("SDL2", dict);
|
||||
gc->BarrierEnd();
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
#include "CrossLang.hpp"
|
||||
#if defined(CROSSLANG_ENABLE_SQLITE)
|
||||
#include "../sqlite/sqlite3.h"
|
||||
#endif
|
||||
#include <iostream>
|
||||
namespace Tesses::CrossLang {
|
||||
#if defined(CROSSLANG_ENABLE_SQLITE)
|
||||
static int sqlcollector(void* user, int count,char** vals, char** keys)
|
||||
{
|
||||
|
||||
std::pair<GCList*,TList*>* ls2 = static_cast<std::pair<GCList*,TList*>*>(user);
|
||||
TDictionary* dict = TDictionary::Create(ls2->first);
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
std::string key = keys[i] == nullptr ? "" : keys[i];
|
||||
|
||||
if(vals[i] == nullptr)
|
||||
{
|
||||
dict->SetValue(key,nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
dict->SetValue(key,vals[i]);
|
||||
}
|
||||
}
|
||||
ls2->second->Add(dict);
|
||||
|
||||
return 0;
|
||||
}
|
||||
TObject Sqlite_Open(GCList& ls, std::vector<TObject> args,TRootEnvironment* env)
|
||||
{
|
||||
Tesses::Framework::Filesystem::VFSPath p;
|
||||
if(GetArgumentAsPath(args,0,p))
|
||||
{
|
||||
if(env->permissions.sqlite3Scoped)
|
||||
{
|
||||
p = env->permissions.sqliteOffsetPath / p.CollapseRelativeParents();
|
||||
}
|
||||
std::string name = p.ToString();
|
||||
|
||||
sqlite3* sqlite;
|
||||
std::cout << name << std::endl;
|
||||
int rc =sqlite3_open(name.c_str(),&sqlite);
|
||||
if(rc)
|
||||
{
|
||||
std::string error = sqlite3_errmsg(sqlite);
|
||||
sqlite3_close(sqlite);
|
||||
return error;
|
||||
}
|
||||
return TNative::Create(ls,sqlite,[](void* a)->void {
|
||||
if(a != nullptr)
|
||||
sqlite3_close((sqlite3*)a);
|
||||
});
|
||||
}
|
||||
|
||||
return Undefined();
|
||||
|
||||
}
|
||||
TObject Sqlite_Exec(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
if(args.size() == 2 && std::holds_alternative<THeapObjectHolder>(args[0]) && std::holds_alternative<std::string>(args[1]))
|
||||
{
|
||||
TNative* native = dynamic_cast<TNative*>(std::get<THeapObjectHolder>(args[0]).obj);
|
||||
std::string sqlStatement = std::get<std::string>(args[1]);
|
||||
if(native != nullptr && !native->GetDestroyed())
|
||||
{
|
||||
sqlite3* sql = (sqlite3*)native->GetPointer();
|
||||
TList* myLs = TList::Create(ls);
|
||||
std::pair<GCList*,TList*> result(&ls, myLs);
|
||||
char* err;
|
||||
int res = sqlite3_exec(sql,sqlStatement.c_str(),sqlcollector,&result,&err);
|
||||
if(res != SQLITE_OK)
|
||||
{
|
||||
std::string errstr = err == nullptr ? "" : err;
|
||||
sqlite3_free(err);
|
||||
return errstr;
|
||||
}
|
||||
|
||||
return myLs;
|
||||
}
|
||||
}
|
||||
|
||||
return Undefined();
|
||||
|
||||
}
|
||||
|
||||
TObject Sqlite_Escape(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
if(args.size() == 1 && std::holds_alternative<std::string>(args[0]))
|
||||
{
|
||||
std::string srcStr = std::get<std::string>(args[0]);
|
||||
std::string myStr = "\'";
|
||||
for(auto c : srcStr)
|
||||
{
|
||||
if(c == '\'') myStr += "\'\'";
|
||||
else
|
||||
myStr += c;
|
||||
}
|
||||
myStr += '\'';
|
||||
return myStr;
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
TObject Sqlite_Close(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
if(args.size() == 1 && std::holds_alternative<THeapObjectHolder>(args[0]))
|
||||
{
|
||||
TNative* native = dynamic_cast<TNative*>(std::get<THeapObjectHolder>(args[0]).obj);
|
||||
|
||||
if(native != nullptr && !native->GetDestroyed())
|
||||
{
|
||||
native->Destroy();
|
||||
}
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
#endif
|
||||
void TStd::RegisterSqlite(GC* gc,TRootEnvironment* env)
|
||||
{
|
||||
|
||||
env->permissions.canRegisterSqlite=true;
|
||||
#if defined(CROSSLANG_ENABLE_SQLITE)
|
||||
GCList ls(gc);
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
dict->DeclareFunction(gc,"Open","Opens the database (returns database handle or an error message as string or undefined)",{"filename"},[env](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
return Sqlite_Open(ls,args,env);
|
||||
});
|
||||
dict->DeclareFunction(gc,"Exec","Execute sql (returns dictionary of columns key=value, an error message as string or undefined)",{"handle","sql"},Sqlite_Exec);
|
||||
dict->DeclareFunction(gc,"Close","Close sql database",{"handle"},Sqlite_Close);
|
||||
dict->DeclareFunction(gc,"Escape","Escape sql text",{"text"},Sqlite_Escape);
|
||||
|
||||
gc->BarrierBegin();
|
||||
env->DeclareVariable("Sqlite", dict);
|
||||
gc->BarrierEnd();
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
TObject TypeOf(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
if(args.size() < 1) return "Undefined";
|
||||
if(std::holds_alternative<Undefined>(args[0])) return "Undefined";
|
||||
if(std::holds_alternative<std::nullptr_t>(args[0])) return "Null";
|
||||
if(std::holds_alternative<bool>(args[0])) return "Boolean";
|
||||
if(std::holds_alternative<int64_t>(args[0])) return "Long";
|
||||
if(std::holds_alternative<double>(args[0])) return "Double";
|
||||
if(std::holds_alternative<char>(args[0])) return "Char";
|
||||
if(std::holds_alternative<MethodInvoker>(args[0])) return "MethodInvoker";
|
||||
if(std::holds_alternative<std::string>(args[0])) return "String";
|
||||
if(std::holds_alternative<Tesses::Framework::Filesystem::VFSPath>(args[0])) return "Path";
|
||||
if(std::holds_alternative<THeapObjectHolder>(args[0]))
|
||||
{
|
||||
auto obj = std::get<THeapObjectHolder>(args[0]).obj;
|
||||
auto dict = dynamic_cast<TDictionary*>(obj);
|
||||
auto list = dynamic_cast<TList*>(obj);
|
||||
auto argWrapper = dynamic_cast<TArgWrapper*>(obj);
|
||||
auto closure = dynamic_cast<TClosure*>(obj);
|
||||
auto externalMethod = dynamic_cast<TExternalMethod*>(obj);
|
||||
auto byteArray = dynamic_cast<TByteArray*>(obj);
|
||||
auto native = dynamic_cast<TNative*>(obj);
|
||||
auto vfs = dynamic_cast<TVFSHeapObject*>(obj);
|
||||
auto strm = dynamic_cast<TStreamHeapObject*>(obj);
|
||||
if(strm != nullptr)
|
||||
{
|
||||
auto netStrm = dynamic_cast<Tesses::Framework::Streams::NetworkStream*>(strm->stream);
|
||||
if(netStrm != nullptr)
|
||||
{
|
||||
return "NetworkStream";
|
||||
}
|
||||
|
||||
|
||||
return "Stream";
|
||||
}
|
||||
if(vfs != nullptr)
|
||||
{
|
||||
auto localVFS = dynamic_cast<Tesses::Framework::Filesystem::LocalFilesystem*>(vfs->vfs);
|
||||
|
||||
auto mountableVFS = dynamic_cast<Tesses::Framework::Filesystem::MountableFilesystem*>(vfs->vfs);
|
||||
|
||||
|
||||
auto subFS = dynamic_cast<Tesses::Framework::Filesystem::SubdirFilesystem*>(vfs->vfs);
|
||||
|
||||
if(localVFS != nullptr) return "LocalFilesystem";
|
||||
if(subFS != nullptr) return "SubdirFilesystem";
|
||||
if(mountableVFS != nullptr) return "MountableFilesystem";
|
||||
|
||||
return "VFS";
|
||||
}
|
||||
if(dict != nullptr) return "Dictionary";
|
||||
if(list != nullptr) return "List";
|
||||
if(argWrapper != nullptr) return "ArgWrapper";
|
||||
if(closure != nullptr) return "Closure";
|
||||
if(externalMethod != nullptr) return "ExternalMethod";
|
||||
if(byteArray != nullptr) return "ByteArray";
|
||||
if(native != nullptr) return "Native";
|
||||
|
||||
return "HeapObject";
|
||||
}
|
||||
|
||||
return "Object";
|
||||
}
|
||||
|
||||
TObject ByteArray(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
auto res= TByteArray::Create(ls);
|
||||
if(args.size() == 1){
|
||||
|
||||
if(std::holds_alternative<int64_t>(args[0]))
|
||||
res->data.resize((size_t)std::get<int64_t>(args[0]));
|
||||
else if(std::holds_alternative<std::string>(args[0]))
|
||||
{
|
||||
std::string& txt = std::get<std::string>(args[0]);
|
||||
res->data.insert(res->data.end(), txt.begin(),txt.end());
|
||||
}
|
||||
|
||||
}
|
||||
return res;
|
||||
|
||||
}
|
||||
TObject ParseLong(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
|
||||
if(args.size() >= 1)
|
||||
{
|
||||
int base = 10;
|
||||
|
||||
if(args.size() == 2 && std::holds_alternative<int64_t>(args[1]))
|
||||
{
|
||||
base = (int)std::get<int64_t>(args[1]);
|
||||
}
|
||||
|
||||
size_t pos;
|
||||
|
||||
|
||||
std::string str = ToString(ls.GetGC(),args[0]);
|
||||
|
||||
try {
|
||||
int64_t v = std::stoll(str,&pos,base);
|
||||
if(pos < str.size()) return nullptr;
|
||||
return v;
|
||||
}catch(std::exception& ex)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
}
|
||||
TObject ParseDouble(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
|
||||
if(args.size() == 1)
|
||||
{
|
||||
|
||||
|
||||
size_t pos;
|
||||
|
||||
|
||||
std::string str = ToString(ls.GetGC(),args[0]);
|
||||
|
||||
try {
|
||||
double v = std::stod(str,&pos);
|
||||
if(pos < str.size()) return nullptr;
|
||||
return v;
|
||||
}catch(std::exception& ex)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
static TObject Throw(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
if(!args.empty())
|
||||
{
|
||||
VMByteCodeException bce(ls.GetGC(),args[0]);
|
||||
throw bce;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static TObject Try(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
TCallable* tryPart;
|
||||
TCallable* catchPart;
|
||||
if(GetArgumentHeap(args,0,tryPart) && GetArgumentHeap(args,1,catchPart))
|
||||
{
|
||||
try {
|
||||
tryPart->Call(ls,{});
|
||||
}
|
||||
catch(VMByteCodeException& ex)
|
||||
{
|
||||
catchPart->Call(ls,{ex.exception});
|
||||
}
|
||||
catch(std::exception& ex)
|
||||
{
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
auto gc = ls.GetGC();
|
||||
gc->BarrierBegin();
|
||||
dict->SetValue("Type","NativeException");
|
||||
dict->SetValue("Text",ex.what());
|
||||
gc->BarrierEnd();
|
||||
catchPart->Call(ls,{dict});
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
EnvironmentPermissions::EnvironmentPermissions()
|
||||
{
|
||||
this->canRegisterConsole=false;
|
||||
this->canRegisterCrypto=false;
|
||||
this->canRegisterDictionary=false;
|
||||
this->canRegisterEnv=false;
|
||||
this->canRegisterIO=false;
|
||||
this->canRegisterJSON=false;
|
||||
this->canRegisterLocalFS=false;
|
||||
this->canRegisterNet=false;
|
||||
this->canRegisterOGC=false;
|
||||
this->canRegisterPath=false;
|
||||
this->canRegisterRoot=false;
|
||||
this->canRegisterSDL2=false;
|
||||
this->canRegisterSqlite=false;
|
||||
this->canRegisterVM = false;
|
||||
this->locked=false;
|
||||
this->sqlite3Scoped=false;
|
||||
|
||||
}
|
||||
|
||||
void TStd::RegisterRoot(GC* gc, TRootEnvironment* env)
|
||||
{
|
||||
|
||||
env->permissions.canRegisterRoot=true;
|
||||
env->DeclareFunction(gc, "ParseLong","Parse Long from String",{"arg","$base"},ParseLong);
|
||||
|
||||
env->DeclareFunction(gc, "ParseDouble","Parse Double from String",{"arg"},ParseDouble);
|
||||
env->DeclareFunction(gc, "TypeOf","Get type of object",{"object"},TypeOf);
|
||||
env->DeclareFunction(gc, "Throw", "Throw an exception",{"object"},Throw);
|
||||
env->DeclareFunction(gc, "Try", "Handle exceptions",{"callbackToTry","callbackToCatch"},Try);
|
||||
env->DeclareFunction(gc, "Thread","Create thread",{"callback"},[](GCList& ls, std::vector<TObject> args)-> TObject
|
||||
{
|
||||
if(args.size() == 1 && std::holds_alternative<THeapObjectHolder>(args[0]))
|
||||
{
|
||||
auto cb = dynamic_cast<TCallable*>(std::get<THeapObjectHolder>(args[0]).obj);
|
||||
if(cb != nullptr)
|
||||
{
|
||||
return CreateThread(ls,cb,false);
|
||||
}
|
||||
}
|
||||
return Undefined();
|
||||
});
|
||||
env->DeclareFunction(gc,"ByteArray","Create bytearray, with optional either size (to size it) or string argument (to fill byte array)",{"$data"},ByteArray);
|
||||
|
||||
}
|
||||
void TStd::RegisterStd(GC* gc, TRootEnvironment* env)
|
||||
{
|
||||
RegisterEnv(gc, env);
|
||||
RegisterRoot(gc,env);
|
||||
RegisterPath(gc,env);
|
||||
RegisterConsole(gc, env);
|
||||
RegisterIO(gc, env);
|
||||
RegisterNet(gc, env);
|
||||
RegisterSqlite(gc, env);
|
||||
RegisterVM(gc, env);
|
||||
RegisterJson(gc, env);
|
||||
RegisterDictionary(gc, env);
|
||||
RegisterCrypto(gc,env);
|
||||
RegisterSDL2(gc, env);
|
||||
RegisterOGC(gc, env);
|
||||
env->permissions.locked=true;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,211 @@
|
|||
|
||||
#include "CrossLang.hpp"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
static TObject VM_Eval(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
std::string str;
|
||||
if(GetArgument(args,0,str))
|
||||
{
|
||||
if(current_function != nullptr)
|
||||
{
|
||||
return current_function->env->Eval(ls,str);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static TObject Failure(GCList& ls, std::string reason)
|
||||
{
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
ls.GetGC()->BarrierBegin();
|
||||
dict->SetValue("Success",false);
|
||||
dict->SetValue("Reason", reason);
|
||||
ls.GetGC()->BarrierEnd();
|
||||
return dict;
|
||||
}
|
||||
static TObject Success(GCList& ls)
|
||||
{
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
ls.GetGC()->BarrierBegin();
|
||||
dict->SetValue("Success",true);
|
||||
ls.GetGC()->BarrierEnd();
|
||||
return dict;
|
||||
}
|
||||
static TObject VM_getCurrentEnvironment(GCList& ls2,std::vector<TObject> args)
|
||||
{
|
||||
if(current_function != nullptr) return current_function->env;
|
||||
return Undefined();
|
||||
}
|
||||
static TObject VM_Compile(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
try {
|
||||
TDictionary* dict;
|
||||
if(!GetArgumentHeap<TDictionary*>(args,0,dict)) return Undefined();
|
||||
/*
|
||||
VM.Compile
|
||||
{
|
||||
Name = "My name",
|
||||
Info = "",
|
||||
Sources = [],
|
||||
Version = "",
|
||||
Dependencies = [{Name="Name", Version}],
|
||||
ResourceDirectory = "",
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
std::string name = "Out";
|
||||
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::string info;
|
||||
TVFSHeapObject* vfsHO =nullptr;
|
||||
|
||||
ls.GetGC()->BarrierBegin();
|
||||
TObject _name = dict->GetValue("Name");
|
||||
TObject _version = dict->GetValue("Version");
|
||||
TObject _sources = dict->GetValue("Sources");
|
||||
TObject _dependencies = dict->GetValue("Dependencies");
|
||||
TObject _info = dict->GetValue("Info");
|
||||
TObject _resourceFileSystem = dict->GetValue("ResourceFileSystem");
|
||||
|
||||
TObject _out = dict->GetValue("Output");
|
||||
TList* _depList; TList* srcLst;
|
||||
GetObject<std::string>(_name,name);
|
||||
GetObject<std::string>(_info,info);
|
||||
GetObjectHeap(_resourceFileSystem, vfsHO);
|
||||
std::string v2;
|
||||
if(GetObject<std::string>(_version,v2))
|
||||
TVMVersion::TryParse(v2, version);
|
||||
|
||||
if(GetObjectHeap<TList*>(_dependencies,_depList))
|
||||
{
|
||||
for(int64_t i = 0; i < _depList->Count(); i++)
|
||||
{
|
||||
TObject _dep = _depList->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))
|
||||
{
|
||||
dependencies.push_back(std::pair<std::string,TVMVersion>(name2, version02));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if(GetObjectHeap<TList*>(_sources,srcLst))
|
||||
{
|
||||
for(int64_t i = 0; i < srcLst->Count(); i++)
|
||||
{
|
||||
TObject _src = srcLst->Get(i);
|
||||
TDictionary* _srcD;
|
||||
if(GetObjectHeap<TDictionary*>(_src, _srcD))
|
||||
{
|
||||
TObject _sourceTxt = _srcD->GetValue("Source");
|
||||
TObject _filename = _srcD->GetValue("FileName");
|
||||
|
||||
|
||||
std::string srctxt = "";
|
||||
std::string filename = "memory_" + std::to_string(i) + ".tcross";
|
||||
bool fromFile = false;
|
||||
|
||||
GetObject<std::string>(_sourceTxt,srctxt);
|
||||
GetObject<std::string>(_filename,filename);
|
||||
|
||||
|
||||
sources.push_back(std::pair<std::string,std::string>(srctxt,filename));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ls.GetGC()->BarrierEnd();
|
||||
|
||||
std::vector<LexToken> tokens;
|
||||
for(auto source : sources)
|
||||
{
|
||||
std::stringstream strm(source.first);
|
||||
|
||||
int res = Lex(source.second, strm, tokens);
|
||||
if(res != 0)
|
||||
{
|
||||
return Failure(ls, "Lex error in file \"" + source.second + "\":" + std::to_string(res));
|
||||
}
|
||||
}
|
||||
Parser parser(tokens);
|
||||
SyntaxNode n = parser.ParseRoot();
|
||||
CodeGen gen;
|
||||
gen.GenRoot(n);
|
||||
gen.dependencies = dependencies;
|
||||
gen.info = info;
|
||||
gen.name = name;
|
||||
gen.version = version;
|
||||
std::string outpath;
|
||||
TStreamHeapObject* stream;
|
||||
if(GetObjectHeap<TStreamHeapObject*>(_out, stream))
|
||||
{
|
||||
gen.Save(vfsHO != nullptr ? vfsHO->vfs : nullptr, stream->stream);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return Success(ls);
|
||||
} catch(std::exception& ex)
|
||||
{
|
||||
return Failure(ls, ex.what());
|
||||
}
|
||||
}
|
||||
void TStd::RegisterVM(GC* gc,TRootEnvironment* env)
|
||||
{
|
||||
|
||||
env->permissions.canRegisterVM=true;
|
||||
GCList ls(gc);
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
dict->DeclareFunction(gc, "getRootEnvironmentAsDictionary","Get root environment as a dictionary",{},[env](GCList& ls, std::vector<TObject> args)-> TObject{
|
||||
return env->GetDictionary();
|
||||
});
|
||||
dict->DeclareFunction(gc, "getRootEnvironment","Get root environment, for reflection purposes",{},[env](GCList& ls2,std::vector<TObject> args)->TObject {return env;});
|
||||
dict->DeclareFunction(gc, "getCurrentEnvironment","Get current environment, for reflection purposes",{},VM_getCurrentEnvironment);
|
||||
dict->DeclareFunction(gc, "CreateEnvironment","Create root environment",{"$dict"},[](GCList& ls,std::vector<TObject> args)->TObject{
|
||||
TDictionary* dict;
|
||||
if(GetArgumentHeap(args,0,dict))
|
||||
{
|
||||
return TRootEnvironment::Create(ls,dict);
|
||||
}
|
||||
else
|
||||
{
|
||||
return TRootEnvironment::Create(ls,TDictionary::Create(ls));
|
||||
}
|
||||
});
|
||||
dict->DeclareFunction(gc, "LoadExecutable", "Load a crossvm executable",{"stream"},[](GCList& ls,std::vector<TObject> args)->TObject{
|
||||
TStreamHeapObject* strm;
|
||||
if(GetArgumentHeap(args,0,strm))
|
||||
{
|
||||
TFile* f =TFile::Create(ls);
|
||||
f->Load(ls.GetGC(),strm->stream);
|
||||
return f;
|
||||
}
|
||||
return nullptr;
|
||||
});
|
||||
dict->DeclareFunction(gc, "Eval", "Eval source code",{"source"}, VM_Eval);
|
||||
dict->DeclareFunction(gc, "Compile", "Compile Source",{"dict"},VM_Compile);
|
||||
|
||||
|
||||
|
||||
gc->BarrierBegin();
|
||||
env->DeclareVariable("VM", dict);
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,460 @@
|
|||
/*
|
||||
Its is under the MIT license, to encourage reuse by cut-and-paste.
|
||||
|
||||
The original files are hosted here: https://github.com/sago007/PlatformFolders
|
||||
|
||||
Copyright (c) 2015-2016 Poul Sander
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "platform_folders.h"
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* Retrives the effective user's home dir.
|
||||
* If the user is running as root we ignore the HOME environment. It works badly with sudo.
|
||||
* Writing to $HOME as root implies security concerns that a multiplatform program cannot be assumed to handle.
|
||||
* @return The home directory. HOME environment is respected for non-root users if it exists.
|
||||
*/
|
||||
static std::string getHome() {
|
||||
std::string res;
|
||||
int uid = getuid();
|
||||
const char* homeEnv = std::getenv("HOME");
|
||||
if ( uid != 0 && homeEnv) {
|
||||
//We only acknowlegde HOME if not root.
|
||||
res = homeEnv;
|
||||
return res;
|
||||
}
|
||||
struct passwd* pw = nullptr;
|
||||
struct passwd pwd;
|
||||
long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
if (bufsize < 0) {
|
||||
bufsize = 16384;
|
||||
}
|
||||
std::vector<char> buffer;
|
||||
buffer.resize(bufsize);
|
||||
int error_code = getpwuid_r(uid, &pwd, buffer.data(), buffer.size(), &pw);
|
||||
if (error_code) {
|
||||
throw std::runtime_error("Unable to get passwd struct.");
|
||||
}
|
||||
const char* tempRes = pw->pw_dir;
|
||||
if (!tempRes) {
|
||||
throw std::runtime_error("User has no home directory");
|
||||
}
|
||||
res = tempRes;
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// Make sure we don't bring in all the extra junk with windows.h
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
// stringapiset.h depends on this
|
||||
#include <windows.h>
|
||||
// For SUCCEEDED macro
|
||||
#include <winerror.h>
|
||||
// For WideCharToMultiByte
|
||||
#include <stringapiset.h>
|
||||
// For SHGetFolderPathW and various CSIDL "magic numbers"
|
||||
#include <shlobj.h>
|
||||
|
||||
namespace sago {
|
||||
namespace internal {
|
||||
|
||||
std::string win32_utf16_to_utf8(const wchar_t* wstr) {
|
||||
std::string res;
|
||||
// If the 6th parameter is 0 then WideCharToMultiByte returns the number of bytes needed to store the result.
|
||||
int actualSize = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr);
|
||||
if (actualSize > 0) {
|
||||
//If the converted UTF-8 string could not be in the initial buffer. Allocate one that can hold it.
|
||||
std::vector<char> buffer(actualSize);
|
||||
actualSize = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, &buffer[0], static_cast<int>(buffer.size()), nullptr, nullptr);
|
||||
res = buffer.data();
|
||||
}
|
||||
if (actualSize == 0) {
|
||||
// WideCharToMultiByte return 0 for errors.
|
||||
throw std::runtime_error("UTF16 to UTF8 failed with error code: " + std::to_string(GetLastError()));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namesapce internal
|
||||
} // namespace sago
|
||||
|
||||
class FreeCoTaskMemory {
|
||||
LPWSTR pointer = NULL;
|
||||
public:
|
||||
explicit FreeCoTaskMemory(LPWSTR pointer) : pointer(pointer) {};
|
||||
~FreeCoTaskMemory() {
|
||||
CoTaskMemFree(pointer);
|
||||
}
|
||||
};
|
||||
|
||||
static std::string GetKnownWindowsFolder(REFKNOWNFOLDERID folderId, const char* errorMsg) {
|
||||
LPWSTR wszPath = NULL;
|
||||
HRESULT hr;
|
||||
hr = SHGetKnownFolderPath(folderId, KF_FLAG_CREATE, NULL, &wszPath);
|
||||
FreeCoTaskMemory scopeBoundMemory(wszPath);
|
||||
|
||||
if (!SUCCEEDED(hr)) {
|
||||
throw std::runtime_error(errorMsg);
|
||||
}
|
||||
return sago::internal::win32_utf16_to_utf8(wszPath);
|
||||
}
|
||||
|
||||
static std::string GetAppData() {
|
||||
return GetKnownWindowsFolder(FOLDERID_RoamingAppData, "RoamingAppData could not be found");
|
||||
}
|
||||
|
||||
static std::string GetAppDataCommon() {
|
||||
return GetKnownWindowsFolder(FOLDERID_ProgramData, "ProgramData could not be found");
|
||||
}
|
||||
|
||||
static std::string GetAppDataLocal() {
|
||||
return GetKnownWindowsFolder(FOLDERID_LocalAppData, "LocalAppData could not be found");
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
#else
|
||||
#include <map>
|
||||
#include <fstream>
|
||||
#include <sys/types.h>
|
||||
// For strlen and strtok
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
//Typically Linux. For easy reading the comments will just say Linux but should work with most *nixes
|
||||
|
||||
static void throwOnRelative(const char* envName, const char* envValue) {
|
||||
if (envValue[0] != '/') {
|
||||
char buffer[200];
|
||||
std::snprintf(buffer, sizeof(buffer), "Environment \"%s\" does not start with an '/'. XDG specifies that the value must be absolute. The current value is: \"%s\"", envName, envValue);
|
||||
throw std::runtime_error(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static std::string getLinuxFolderDefault(const char* envName, const char* defaultRelativePath) {
|
||||
std::string res;
|
||||
const char* tempRes = std::getenv(envName);
|
||||
if (tempRes) {
|
||||
throwOnRelative(envName, tempRes);
|
||||
res = tempRes;
|
||||
return res;
|
||||
}
|
||||
res = getHome() + "/" + defaultRelativePath;
|
||||
return res;
|
||||
}
|
||||
|
||||
static void appendExtraFolders(const char* envName, const char* defaultValue, std::vector<std::string>& folders) {
|
||||
const char* envValue = std::getenv(envName);
|
||||
if (!envValue) {
|
||||
envValue = defaultValue;
|
||||
}
|
||||
sago::internal::appendExtraFoldersTokenizer(envName, envValue, folders);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
namespace sago {
|
||||
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
namespace internal {
|
||||
void appendExtraFoldersTokenizer(const char* envName, const char* envValue, std::vector<std::string>& folders) {
|
||||
std::stringstream ss(envValue);
|
||||
std::string value;
|
||||
while (std::getline(ss, value, ':')) {
|
||||
if (value[0] == '/') {
|
||||
folders.push_back(value);
|
||||
}
|
||||
else {
|
||||
//Unless the system is wrongly configured this should never happen... But of course some systems will be incorectly configured.
|
||||
//The XDG documentation indicates that the folder should be ignored but that the program should continue.
|
||||
std::cerr << "Skipping path \"" << value << "\" in \"" << envName << "\" because it does not start with a \"/\"\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string getDataHome() {
|
||||
#ifdef _WIN32
|
||||
return GetAppData();
|
||||
#elif defined(__APPLE__)
|
||||
return getHome()+"/Library/Application Support";
|
||||
#else
|
||||
return getLinuxFolderDefault("XDG_DATA_HOME", ".local/share");
|
||||
#endif
|
||||
}
|
||||
std::string getHomeDir()
|
||||
{
|
||||
return getHome();
|
||||
}
|
||||
std::string getConfigHome() {
|
||||
#ifdef _WIN32
|
||||
return GetAppData();
|
||||
#elif defined(__APPLE__)
|
||||
return getHome()+"/Library/Application Support";
|
||||
#else
|
||||
return getLinuxFolderDefault("XDG_CONFIG_HOME", ".config");
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string getCacheDir() {
|
||||
#ifdef _WIN32
|
||||
return GetAppDataLocal();
|
||||
#elif defined(__APPLE__)
|
||||
return getHome()+"/Library/Caches";
|
||||
#else
|
||||
return getLinuxFolderDefault("XDG_CACHE_HOME", ".cache");
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string getStateDir() {
|
||||
#ifdef _WIN32
|
||||
return GetAppDataLocal();
|
||||
#elif defined(__APPLE__)
|
||||
return getHome()+"/Library/Application Support";
|
||||
#else
|
||||
return getLinuxFolderDefault("XDG_STATE_HOME", ".local/state");
|
||||
#endif
|
||||
}
|
||||
|
||||
void appendAdditionalDataDirectories(std::vector<std::string>& homes) {
|
||||
#ifdef _WIN32
|
||||
homes.push_back(GetAppDataCommon());
|
||||
#elif !defined(__APPLE__)
|
||||
appendExtraFolders("XDG_DATA_DIRS", "/usr/local/share/:/usr/share/", homes);
|
||||
#endif
|
||||
}
|
||||
|
||||
void appendAdditionalConfigDirectories(std::vector<std::string>& homes) {
|
||||
#ifdef _WIN32
|
||||
homes.push_back(GetAppDataCommon());
|
||||
#elif !defined(__APPLE__)
|
||||
appendExtraFolders("XDG_CONFIG_DIRS", "/etc/xdg", homes);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
struct PlatformFolders::PlatformFoldersData {
|
||||
std::map<std::string, std::string> folders;
|
||||
};
|
||||
|
||||
static void PlatformFoldersAddFromFile(const std::string& filename, std::map<std::string, std::string>& folders) {
|
||||
std::ifstream infile(filename.c_str());
|
||||
std::string line;
|
||||
while (std::getline(infile, line)) {
|
||||
if (line.length() == 0 || line.at(0) == '#' || line.substr(0, 4) != "XDG_" || line.find("_DIR") == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
std::size_t splitPos = line.find('=');
|
||||
std::string key = line.substr(0, splitPos);
|
||||
std::size_t valueStart = line.find('"', splitPos);
|
||||
std::size_t valueEnd = line.find('"', valueStart+1);
|
||||
std::string value = line.substr(valueStart+1, valueEnd - valueStart - 1);
|
||||
folders[key] = value;
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
std::cerr << "WARNING: Failed to process \"" << line << "\" from \"" << filename << "\". Error: "<< e.what() << "\n";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void PlatformFoldersFillData(std::map<std::string, std::string>& folders) {
|
||||
folders["XDG_DOCUMENTS_DIR"] = "$HOME/Documents";
|
||||
folders["XDG_DESKTOP_DIR"] = "$HOME/Desktop";
|
||||
folders["XDG_DOWNLOAD_DIR"] = "$HOME/Downloads";
|
||||
folders["XDG_MUSIC_DIR"] = "$HOME/Music";
|
||||
folders["XDG_PICTURES_DIR"] = "$HOME/Pictures";
|
||||
folders["XDG_PUBLICSHARE_DIR"] = "$HOME/Public";
|
||||
folders["XDG_TEMPLATES_DIR"] = "$HOME/.Templates";
|
||||
folders["XDG_VIDEOS_DIR"] = "$HOME/Videos";
|
||||
PlatformFoldersAddFromFile( getConfigHome()+"/user-dirs.dirs", folders);
|
||||
for (std::map<std::string, std::string>::iterator itr = folders.begin() ; itr != folders.end() ; ++itr ) {
|
||||
std::string& value = itr->second;
|
||||
if (value.compare(0, 5, "$HOME") == 0) {
|
||||
value = getHome() + value.substr(5, std::string::npos);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
PlatformFolders::PlatformFolders() {
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
this->data = new PlatformFolders::PlatformFoldersData();
|
||||
try {
|
||||
PlatformFoldersFillData(data->folders);
|
||||
}
|
||||
catch (...) {
|
||||
delete this->data;
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
PlatformFolders::~PlatformFolders() {
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
delete this->data;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string PlatformFolders::getDocumentsFolder() const {
|
||||
#ifdef _WIN32
|
||||
return GetKnownWindowsFolder(FOLDERID_Documents, "Failed to find My Documents folder");
|
||||
#elif defined(__APPLE__)
|
||||
return getHome()+"/Documents";
|
||||
#else
|
||||
return data->folders["XDG_DOCUMENTS_DIR"];
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string PlatformFolders::getDesktopFolder() const {
|
||||
#ifdef _WIN32
|
||||
return GetKnownWindowsFolder(FOLDERID_Desktop, "Failed to find Desktop folder");
|
||||
#elif defined(__APPLE__)
|
||||
return getHome()+"/Desktop";
|
||||
#else
|
||||
return data->folders["XDG_DESKTOP_DIR"];
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string PlatformFolders::getPicturesFolder() const {
|
||||
#ifdef _WIN32
|
||||
return GetKnownWindowsFolder(FOLDERID_Pictures, "Failed to find My Pictures folder");
|
||||
#elif defined(__APPLE__)
|
||||
return getHome()+"/Pictures";
|
||||
#else
|
||||
return data->folders["XDG_PICTURES_DIR"];
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string PlatformFolders::getPublicFolder() const {
|
||||
#ifdef _WIN32
|
||||
return GetKnownWindowsFolder(FOLDERID_Public, "Failed to find the Public folder");
|
||||
#elif defined(__APPLE__)
|
||||
return getHome()+"/Public";
|
||||
#else
|
||||
return data->folders["XDG_PUBLICSHARE_DIR"];
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string PlatformFolders::getDownloadFolder1() const {
|
||||
#ifdef _WIN32
|
||||
return GetKnownWindowsFolder(FOLDERID_Downloads, "Failed to find My Downloads folder");
|
||||
#elif defined(__APPLE__)
|
||||
return getHome()+"/Downloads";
|
||||
#else
|
||||
return data->folders["XDG_DOWNLOAD_DIR"];
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string PlatformFolders::getMusicFolder() const {
|
||||
#ifdef _WIN32
|
||||
return GetKnownWindowsFolder(FOLDERID_Music, "Failed to find My Music folder");
|
||||
#elif defined(__APPLE__)
|
||||
return getHome()+"/Music";
|
||||
#else
|
||||
return data->folders["XDG_MUSIC_DIR"];
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string PlatformFolders::getVideoFolder() const {
|
||||
#ifdef _WIN32
|
||||
return GetKnownWindowsFolder(FOLDERID_Videos, "Failed to find My Video folder");
|
||||
#elif defined(__APPLE__)
|
||||
return getHome()+"/Movies";
|
||||
#else
|
||||
return data->folders["XDG_VIDEOS_DIR"];
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string PlatformFolders::getSaveGamesFolder1() const {
|
||||
#ifdef _WIN32
|
||||
//A dedicated Save Games folder was not introduced until Vista. For XP and older save games are most often saved in a normal folder named "My Games".
|
||||
//Data that should not be user accessible should be placed under GetDataHome() instead
|
||||
return GetKnownWindowsFolder(FOLDERID_Documents, "Failed to find My Documents folder")+"\\My Games";
|
||||
#elif defined(__APPLE__)
|
||||
return getHome()+"/Library/Application Support";
|
||||
#else
|
||||
return getDataHome();
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string getDesktopFolder() {
|
||||
return PlatformFolders().getDesktopFolder();
|
||||
}
|
||||
|
||||
std::string getDocumentsFolder() {
|
||||
return PlatformFolders().getDocumentsFolder();
|
||||
}
|
||||
|
||||
std::string getDownloadFolder() {
|
||||
return PlatformFolders().getDownloadFolder1();
|
||||
}
|
||||
|
||||
std::string getDownloadFolder1() {
|
||||
return getDownloadFolder();
|
||||
}
|
||||
|
||||
std::string getPicturesFolder() {
|
||||
return PlatformFolders().getPicturesFolder();
|
||||
}
|
||||
|
||||
std::string getPublicFolder() {
|
||||
return PlatformFolders().getPublicFolder();
|
||||
}
|
||||
|
||||
std::string getMusicFolder() {
|
||||
return PlatformFolders().getMusicFolder();
|
||||
}
|
||||
|
||||
std::string getVideoFolder() {
|
||||
return PlatformFolders().getVideoFolder();
|
||||
}
|
||||
|
||||
std::string getSaveGamesFolder1() {
|
||||
return PlatformFolders().getSaveGamesFolder1();
|
||||
}
|
||||
|
||||
std::string getSaveGamesFolder2() {
|
||||
#ifdef _WIN32
|
||||
return GetKnownWindowsFolder(FOLDERID_SavedGames, "Failed to find Saved Games folder");
|
||||
#else
|
||||
return PlatformFolders().getSaveGamesFolder1();
|
||||
#endif
|
||||
}
|
||||
|
||||
} //namespace sago
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
Its is under the MIT license, to encourage reuse by cut-and-paste.
|
||||
|
||||
The original files are hosted here: https://github.com/sago007/PlatformFolders
|
||||
|
||||
Copyright (c) 2015 Poul Sander
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef SAGO_PLATFORM_FOLDERS_H
|
||||
#define SAGO_PLATFORM_FOLDERS_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* The namespace I use for common function. Nothing special about it.
|
||||
*/
|
||||
namespace sago {
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
||||
namespace internal {
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
void appendExtraFoldersTokenizer(const char* envName, const char* envValue, std::vector<std::string>& folders);
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
std::string win32_utf16_to_utf8(const wchar_t* wstr);
|
||||
#endif
|
||||
}
|
||||
#endif //DOXYGEN_SHOULD_SKIP_THIS
|
||||
|
||||
/**
|
||||
* Retrives the base folder for storing data files.
|
||||
* You must add the program name yourself like this:
|
||||
* @code{.cpp}
|
||||
* string data_home = getDataHome()+"/My Program Name/";
|
||||
* @endcode
|
||||
* On Windows this defaults to %APPDATA% (Roaming profile)
|
||||
* On Linux this defaults to ~/.local/share but can be configured by the user
|
||||
* @return The base folder for storing program data.
|
||||
*/
|
||||
std::string getDataHome();
|
||||
|
||||
std::string getHomeDir();
|
||||
|
||||
/**
|
||||
* Retrives the base folder for storing config files.
|
||||
* You must add the program name yourself like this:
|
||||
* @code{.cpp}
|
||||
* string data_home = getConfigHome()+"/My Program Name/";
|
||||
* @endcode
|
||||
* On Windows this defaults to %APPDATA% (Roaming profile)
|
||||
* On Linux this defaults to ~/.config but can be configured by the user
|
||||
* @return The base folder for storing config data.
|
||||
*/
|
||||
std::string getConfigHome();
|
||||
|
||||
/**
|
||||
* Retrives the base folder for storing cache files.
|
||||
* You must add the program name yourself like this:
|
||||
* @code{.cpp}
|
||||
* string data_home = getCacheDir()+"/My Program Name/cache/";
|
||||
* @endcode
|
||||
* On Windows this defaults to %APPDATALOCAL%
|
||||
* On Linux this defaults to ~/.cache but can be configured by the user
|
||||
* Note that it is recommended to append "cache" after the program name to prevent conflicting with "StateDir" under Windows
|
||||
* @return The base folder for storing data that do not need to be backed up and might be deleted.
|
||||
*/
|
||||
std::string getCacheDir();
|
||||
|
||||
/**
|
||||
* Retrives the base folder used for state files.
|
||||
* You must add the program name yourself like this:
|
||||
* @code{.cpp}
|
||||
* string data_home = getStateDir()+"/My Program Name/";
|
||||
* @endcode
|
||||
* On Windows this defaults to %APPDATALOCAL%
|
||||
* On Linux this defaults to ~/.local/state but can be configured by the user
|
||||
* On OS X this is the same as getDataHome()
|
||||
* @return The base folder for storing data that do not need to be backed up but should not be reguarly deleted either.
|
||||
*/
|
||||
std::string getStateDir();
|
||||
|
||||
/**
|
||||
* This will append extra folders that your program should be looking for data files in.
|
||||
* This does not normally include the path returned by GetDataHome().
|
||||
* If you want all the folders you should do something like:
|
||||
* @code{.cpp}
|
||||
* vector<string> folders;
|
||||
* folders.push_back(getDataHome());
|
||||
* appendAdditionalDataDirectories(folders);
|
||||
* for (string s& : folders) {
|
||||
* s+="/My Program Name/";
|
||||
* }
|
||||
* @endcode
|
||||
* You must apply "/My Program Name/" to all the strings.
|
||||
* The string at the lowest index has the highest priority.
|
||||
* @param homes A vector that extra folders will be appended to.
|
||||
*/
|
||||
void appendAdditionalDataDirectories(std::vector<std::string>& homes);
|
||||
|
||||
/**
|
||||
* This will append extra folders that your program should be looking for config files in.
|
||||
* This does not normally include the path returned by GetConfigHome().
|
||||
* If you want all the folders you should do something like:
|
||||
* @code{.cpp}
|
||||
* std::vector<std::string> folders;
|
||||
* folders.push_back(sago::getConfigHome());
|
||||
* sago::appendAdditionalConfigDirectories(folders);
|
||||
* for (std::string s& : folders) {
|
||||
* s+="/My Program Name/";
|
||||
* }
|
||||
* @endcode
|
||||
* You must apply "/My Program Name/" to all the strings.
|
||||
* The string at the lowest index has the highest priority.
|
||||
* @param homes A vector that extra folders will be appended to.
|
||||
*/
|
||||
void appendAdditionalConfigDirectories(std::vector<std::string>& homes);
|
||||
|
||||
/**
|
||||
* The folder that represents the desktop.
|
||||
* Normally you should try not to use this folder.
|
||||
* @return Absolute path to the user's desktop
|
||||
*/
|
||||
std::string getDesktopFolder();
|
||||
|
||||
/**
|
||||
* The folder to store user documents to
|
||||
* @return Absolute path to the "Documents" folder
|
||||
*/
|
||||
std::string getDocumentsFolder();
|
||||
|
||||
/**
|
||||
* The folder where files are downloaded.
|
||||
* @return Absolute path to the folder where files are downloaded to.
|
||||
*/
|
||||
std::string getDownloadFolder();
|
||||
|
||||
/**
|
||||
* The folder where files are downloaded.
|
||||
* @note This is provided for backward compatibility. Use getDownloadFolder instead.
|
||||
* @return Absolute path to the folder where files are downloaded to.
|
||||
*/
|
||||
std::string getDownloadFolder1();
|
||||
|
||||
/**
|
||||
* The folder for storing the user's pictures.
|
||||
* @return Absolute path to the "Picture" folder
|
||||
*/
|
||||
std::string getPicturesFolder();
|
||||
|
||||
/**
|
||||
* This returns the folder that can be used for sharing files with other users on the same system.
|
||||
* @return Absolute path to the "Public" folder
|
||||
*/
|
||||
std::string getPublicFolder();
|
||||
|
||||
/**
|
||||
* The folder where music is stored
|
||||
* @return Absolute path to the music folder
|
||||
*/
|
||||
std::string getMusicFolder();
|
||||
|
||||
/**
|
||||
* The folder where video is stored
|
||||
* @return Absolute path to the video folder
|
||||
*/
|
||||
std::string getVideoFolder();
|
||||
|
||||
/**
|
||||
* A base folder for storing saved games.
|
||||
* You must add the program name to it like this:
|
||||
* @code{.cpp}
|
||||
* string saved_games_folder = sago::getSaveGamesFolder1()+"/My Program Name/";
|
||||
* @endcode
|
||||
* @note Windows: This is an XP compatible version and returns the path to "My Games" in Documents. Vista and later has an official folder.
|
||||
* @note Linux: XDF does not define a folder for saved games. This will just return the same as GetDataHome()
|
||||
* @return The folder base folder for storing save games.
|
||||
*/
|
||||
std::string getSaveGamesFolder1();
|
||||
|
||||
/**
|
||||
* A base folder for storing saved games.
|
||||
* You must add the program name to it like this:
|
||||
* @code{.cpp}
|
||||
* string saved_games_folder = sago::getSaveGamesFolder2()+"/My Program Name/";
|
||||
* @endcode
|
||||
* @note PlatformFolders provide different folders to for saved games as not all operating systems has support for Saved Games yet.
|
||||
* It is recommended to pick the highest number (currently getSaveGamesFolder2) at the time your product enters production and stick with it
|
||||
* @note Windows: This returns the "Saved Games" folder. This folder exist in Vista and later
|
||||
* @note Linux: XDF does not define a folder for saved games. This will just return the same as GetDataHome()
|
||||
* @return The folder base folder for storing save games.
|
||||
*/
|
||||
std::string getSaveGamesFolder2();
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
||||
|
||||
/**
|
||||
* This class contains methods for finding the system depended special folders.
|
||||
* For Windows these folders are either by convention or given by CSIDL.
|
||||
* For Linux XDG convention is used.
|
||||
* The Linux version has very little error checking and assumes that the config is correct
|
||||
*/
|
||||
class PlatformFolders {
|
||||
public:
|
||||
PlatformFolders();
|
||||
~PlatformFolders();
|
||||
/**
|
||||
* The folder that represents the desktop.
|
||||
* Normally you should try not to use this folder.
|
||||
* @return Absolute path to the user's desktop
|
||||
*/
|
||||
std::string getDesktopFolder() const;
|
||||
/**
|
||||
* The folder to store user documents to
|
||||
* @return Absolute path to the "Documents" folder
|
||||
*/
|
||||
std::string getDocumentsFolder() const;
|
||||
/**
|
||||
* The folder for storing the user's pictures.
|
||||
* @return Absolute path to the "Picture" folder
|
||||
*/
|
||||
std::string getPicturesFolder() const;
|
||||
/**
|
||||
* Use sago::getPublicFolder() instead!
|
||||
*/
|
||||
std::string getPublicFolder() const;
|
||||
/**
|
||||
* The folder where files are downloaded.
|
||||
* @note Windows: This version is XP compatible and returns the Desktop. Vista and later has a dedicated folder.
|
||||
* @return Absolute path to the folder where files are downloaded to.
|
||||
*/
|
||||
std::string getDownloadFolder1() const;
|
||||
/**
|
||||
* The folder where music is stored
|
||||
* @return Absolute path to the music folder
|
||||
*/
|
||||
std::string getMusicFolder() const;
|
||||
/**
|
||||
* The folder where video is stored
|
||||
* @return Absolute path to the video folder
|
||||
*/
|
||||
std::string getVideoFolder() const;
|
||||
/**
|
||||
* The base folder for storing saved games.
|
||||
* You must add the program name to it like this:
|
||||
* @code{.cpp}
|
||||
* PlatformFolders pf;
|
||||
* string saved_games_folder = pf.getSaveGamesFolder1()+"/My Program Name/";
|
||||
* @endcode
|
||||
* @note Windows: This is an XP compatible version and returns the path to "My Games" in Documents. Vista and later has an official folder.
|
||||
* @note Linux: XDF does not define a folder for saved games. This will just return the same as GetDataHome()
|
||||
* @return The folder base folder for storing save games.
|
||||
*/
|
||||
std::string getSaveGamesFolder1() const;
|
||||
private:
|
||||
PlatformFolders(const PlatformFolders&);
|
||||
PlatformFolders& operator=(const PlatformFolders&);
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
struct PlatformFoldersData;
|
||||
PlatformFoldersData* data;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // skip doxygen
|
||||
|
||||
|
||||
} //namespace sago
|
||||
|
||||
#endif /* PLATFORM_FOLDERS_H */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,719 @@
|
|||
/*
|
||||
** 2006 June 7
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This header file defines the SQLite interface for use by
|
||||
** shared libraries that want to be imported as extensions into
|
||||
** an SQLite instance. Shared libraries that intend to be loaded
|
||||
** as extensions by SQLite should #include this file instead of
|
||||
** sqlite3.h.
|
||||
*/
|
||||
#ifndef SQLITE3EXT_H
|
||||
#define SQLITE3EXT_H
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** The following structure holds pointers to all of the SQLite API
|
||||
** routines.
|
||||
**
|
||||
** WARNING: In order to maintain backwards compatibility, add new
|
||||
** interfaces to the end of this structure only. If you insert new
|
||||
** interfaces in the middle of this structure, then older different
|
||||
** versions of SQLite will not be able to load each other's shared
|
||||
** libraries!
|
||||
*/
|
||||
struct sqlite3_api_routines {
|
||||
void * (*aggregate_context)(sqlite3_context*,int nBytes);
|
||||
int (*aggregate_count)(sqlite3_context*);
|
||||
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
|
||||
int (*bind_double)(sqlite3_stmt*,int,double);
|
||||
int (*bind_int)(sqlite3_stmt*,int,int);
|
||||
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
|
||||
int (*bind_null)(sqlite3_stmt*,int);
|
||||
int (*bind_parameter_count)(sqlite3_stmt*);
|
||||
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
|
||||
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
|
||||
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
|
||||
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
|
||||
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
|
||||
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
|
||||
int (*busy_timeout)(sqlite3*,int ms);
|
||||
int (*changes)(sqlite3*);
|
||||
int (*close)(sqlite3*);
|
||||
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const char*));
|
||||
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const void*));
|
||||
const void * (*column_blob)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_count)(sqlite3_stmt*pStmt);
|
||||
const char * (*column_database_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_database_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_decltype)(sqlite3_stmt*,int i);
|
||||
const void * (*column_decltype16)(sqlite3_stmt*,int);
|
||||
double (*column_double)(sqlite3_stmt*,int iCol);
|
||||
int (*column_int)(sqlite3_stmt*,int iCol);
|
||||
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
|
||||
const char * (*column_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_origin_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_origin_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_table_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_table_name16)(sqlite3_stmt*,int);
|
||||
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
|
||||
const void * (*column_text16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_type)(sqlite3_stmt*,int iCol);
|
||||
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
|
||||
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
|
||||
int (*complete)(const char*sql);
|
||||
int (*complete16)(const void*sql);
|
||||
int (*create_collation)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_collation16)(sqlite3*,const void*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_function)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_function16)(sqlite3*,const void*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
|
||||
int (*data_count)(sqlite3_stmt*pStmt);
|
||||
sqlite3 * (*db_handle)(sqlite3_stmt*);
|
||||
int (*declare_vtab)(sqlite3*,const char*);
|
||||
int (*enable_shared_cache)(int);
|
||||
int (*errcode)(sqlite3*db);
|
||||
const char * (*errmsg)(sqlite3*);
|
||||
const void * (*errmsg16)(sqlite3*);
|
||||
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
|
||||
int (*expired)(sqlite3_stmt*);
|
||||
int (*finalize)(sqlite3_stmt*pStmt);
|
||||
void (*free)(void*);
|
||||
void (*free_table)(char**result);
|
||||
int (*get_autocommit)(sqlite3*);
|
||||
void * (*get_auxdata)(sqlite3_context*,int);
|
||||
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
|
||||
int (*global_recover)(void);
|
||||
void (*interruptx)(sqlite3*);
|
||||
sqlite_int64 (*last_insert_rowid)(sqlite3*);
|
||||
const char * (*libversion)(void);
|
||||
int (*libversion_number)(void);
|
||||
void *(*malloc)(int);
|
||||
char * (*mprintf)(const char*,...);
|
||||
int (*open)(const char*,sqlite3**);
|
||||
int (*open16)(const void*,sqlite3**);
|
||||
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
|
||||
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
|
||||
void *(*realloc)(void*,int);
|
||||
int (*reset)(sqlite3_stmt*pStmt);
|
||||
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_double)(sqlite3_context*,double);
|
||||
void (*result_error)(sqlite3_context*,const char*,int);
|
||||
void (*result_error16)(sqlite3_context*,const void*,int);
|
||||
void (*result_int)(sqlite3_context*,int);
|
||||
void (*result_int64)(sqlite3_context*,sqlite_int64);
|
||||
void (*result_null)(sqlite3_context*);
|
||||
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
|
||||
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_value)(sqlite3_context*,sqlite3_value*);
|
||||
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
|
||||
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
||||
const char*,const char*),void*);
|
||||
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
||||
char * (*xsnprintf)(int,char*,const char*,...);
|
||||
int (*step)(sqlite3_stmt*);
|
||||
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
||||
char const**,char const**,int*,int*,int*);
|
||||
void (*thread_cleanup)(void);
|
||||
int (*total_changes)(sqlite3*);
|
||||
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
|
||||
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
|
||||
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
|
||||
sqlite_int64),void*);
|
||||
void * (*user_data)(sqlite3_context*);
|
||||
const void * (*value_blob)(sqlite3_value*);
|
||||
int (*value_bytes)(sqlite3_value*);
|
||||
int (*value_bytes16)(sqlite3_value*);
|
||||
double (*value_double)(sqlite3_value*);
|
||||
int (*value_int)(sqlite3_value*);
|
||||
sqlite_int64 (*value_int64)(sqlite3_value*);
|
||||
int (*value_numeric_type)(sqlite3_value*);
|
||||
const unsigned char * (*value_text)(sqlite3_value*);
|
||||
const void * (*value_text16)(sqlite3_value*);
|
||||
const void * (*value_text16be)(sqlite3_value*);
|
||||
const void * (*value_text16le)(sqlite3_value*);
|
||||
int (*value_type)(sqlite3_value*);
|
||||
char *(*vmprintf)(const char*,va_list);
|
||||
/* Added ??? */
|
||||
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
|
||||
/* Added by 3.3.13 */
|
||||
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
int (*clear_bindings)(sqlite3_stmt*);
|
||||
/* Added by 3.4.1 */
|
||||
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
|
||||
void (*xDestroy)(void *));
|
||||
/* Added by 3.5.0 */
|
||||
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
|
||||
int (*blob_bytes)(sqlite3_blob*);
|
||||
int (*blob_close)(sqlite3_blob*);
|
||||
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
|
||||
int,sqlite3_blob**);
|
||||
int (*blob_read)(sqlite3_blob*,void*,int,int);
|
||||
int (*blob_write)(sqlite3_blob*,const void*,int,int);
|
||||
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*),
|
||||
void(*)(void*));
|
||||
int (*file_control)(sqlite3*,const char*,int,void*);
|
||||
sqlite3_int64 (*memory_highwater)(int);
|
||||
sqlite3_int64 (*memory_used)(void);
|
||||
sqlite3_mutex *(*mutex_alloc)(int);
|
||||
void (*mutex_enter)(sqlite3_mutex*);
|
||||
void (*mutex_free)(sqlite3_mutex*);
|
||||
void (*mutex_leave)(sqlite3_mutex*);
|
||||
int (*mutex_try)(sqlite3_mutex*);
|
||||
int (*open_v2)(const char*,sqlite3**,int,const char*);
|
||||
int (*release_memory)(int);
|
||||
void (*result_error_nomem)(sqlite3_context*);
|
||||
void (*result_error_toobig)(sqlite3_context*);
|
||||
int (*sleep)(int);
|
||||
void (*soft_heap_limit)(int);
|
||||
sqlite3_vfs *(*vfs_find)(const char*);
|
||||
int (*vfs_register)(sqlite3_vfs*,int);
|
||||
int (*vfs_unregister)(sqlite3_vfs*);
|
||||
int (*xthreadsafe)(void);
|
||||
void (*result_zeroblob)(sqlite3_context*,int);
|
||||
void (*result_error_code)(sqlite3_context*,int);
|
||||
int (*test_control)(int, ...);
|
||||
void (*randomness)(int,void*);
|
||||
sqlite3 *(*context_db_handle)(sqlite3_context*);
|
||||
int (*extended_result_codes)(sqlite3*,int);
|
||||
int (*limit)(sqlite3*,int,int);
|
||||
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
|
||||
const char *(*sql)(sqlite3_stmt*);
|
||||
int (*status)(int,int*,int*,int);
|
||||
int (*backup_finish)(sqlite3_backup*);
|
||||
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
|
||||
int (*backup_pagecount)(sqlite3_backup*);
|
||||
int (*backup_remaining)(sqlite3_backup*);
|
||||
int (*backup_step)(sqlite3_backup*,int);
|
||||
const char *(*compileoption_get)(int);
|
||||
int (*compileoption_used)(const char*);
|
||||
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void(*xDestroy)(void*));
|
||||
int (*db_config)(sqlite3*,int,...);
|
||||
sqlite3_mutex *(*db_mutex)(sqlite3*);
|
||||
int (*db_status)(sqlite3*,int,int*,int*,int);
|
||||
int (*extended_errcode)(sqlite3*);
|
||||
void (*log)(int,const char*,...);
|
||||
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
|
||||
const char *(*sourceid)(void);
|
||||
int (*stmt_status)(sqlite3_stmt*,int,int);
|
||||
int (*strnicmp)(const char*,const char*,int);
|
||||
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
|
||||
int (*wal_autocheckpoint)(sqlite3*,int);
|
||||
int (*wal_checkpoint)(sqlite3*,const char*);
|
||||
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
|
||||
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
|
||||
int (*vtab_config)(sqlite3*,int op,...);
|
||||
int (*vtab_on_conflict)(sqlite3*);
|
||||
/* Version 3.7.16 and later */
|
||||
int (*close_v2)(sqlite3*);
|
||||
const char *(*db_filename)(sqlite3*,const char*);
|
||||
int (*db_readonly)(sqlite3*,const char*);
|
||||
int (*db_release_memory)(sqlite3*);
|
||||
const char *(*errstr)(int);
|
||||
int (*stmt_busy)(sqlite3_stmt*);
|
||||
int (*stmt_readonly)(sqlite3_stmt*);
|
||||
int (*stricmp)(const char*,const char*);
|
||||
int (*uri_boolean)(const char*,const char*,int);
|
||||
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
|
||||
const char *(*uri_parameter)(const char*,const char*);
|
||||
char *(*xvsnprintf)(int,char*,const char*,va_list);
|
||||
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
|
||||
/* Version 3.8.7 and later */
|
||||
int (*auto_extension)(void(*)(void));
|
||||
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
|
||||
void(*)(void*));
|
||||
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
|
||||
void(*)(void*),unsigned char);
|
||||
int (*cancel_auto_extension)(void(*)(void));
|
||||
int (*load_extension)(sqlite3*,const char*,const char*,char**);
|
||||
void *(*malloc64)(sqlite3_uint64);
|
||||
sqlite3_uint64 (*msize)(void*);
|
||||
void *(*realloc64)(void*,sqlite3_uint64);
|
||||
void (*reset_auto_extension)(void);
|
||||
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
|
||||
void(*)(void*));
|
||||
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
|
||||
void(*)(void*), unsigned char);
|
||||
int (*strglob)(const char*,const char*);
|
||||
/* Version 3.8.11 and later */
|
||||
sqlite3_value *(*value_dup)(const sqlite3_value*);
|
||||
void (*value_free)(sqlite3_value*);
|
||||
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
|
||||
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
|
||||
/* Version 3.9.0 and later */
|
||||
unsigned int (*value_subtype)(sqlite3_value*);
|
||||
void (*result_subtype)(sqlite3_context*,unsigned int);
|
||||
/* Version 3.10.0 and later */
|
||||
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
|
||||
int (*strlike)(const char*,const char*,unsigned int);
|
||||
int (*db_cacheflush)(sqlite3*);
|
||||
/* Version 3.12.0 and later */
|
||||
int (*system_errno)(sqlite3*);
|
||||
/* Version 3.14.0 and later */
|
||||
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
|
||||
char *(*expanded_sql)(sqlite3_stmt*);
|
||||
/* Version 3.18.0 and later */
|
||||
void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64);
|
||||
/* Version 3.20.0 and later */
|
||||
int (*prepare_v3)(sqlite3*,const char*,int,unsigned int,
|
||||
sqlite3_stmt**,const char**);
|
||||
int (*prepare16_v3)(sqlite3*,const void*,int,unsigned int,
|
||||
sqlite3_stmt**,const void**);
|
||||
int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*));
|
||||
void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*));
|
||||
void *(*value_pointer)(sqlite3_value*,const char*);
|
||||
int (*vtab_nochange)(sqlite3_context*);
|
||||
int (*value_nochange)(sqlite3_value*);
|
||||
const char *(*vtab_collation)(sqlite3_index_info*,int);
|
||||
/* Version 3.24.0 and later */
|
||||
int (*keyword_count)(void);
|
||||
int (*keyword_name)(int,const char**,int*);
|
||||
int (*keyword_check)(const char*,int);
|
||||
sqlite3_str *(*str_new)(sqlite3*);
|
||||
char *(*str_finish)(sqlite3_str*);
|
||||
void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
|
||||
void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
|
||||
void (*str_append)(sqlite3_str*, const char *zIn, int N);
|
||||
void (*str_appendall)(sqlite3_str*, const char *zIn);
|
||||
void (*str_appendchar)(sqlite3_str*, int N, char C);
|
||||
void (*str_reset)(sqlite3_str*);
|
||||
int (*str_errcode)(sqlite3_str*);
|
||||
int (*str_length)(sqlite3_str*);
|
||||
char *(*str_value)(sqlite3_str*);
|
||||
/* Version 3.25.0 and later */
|
||||
int (*create_window_function)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void (*xValue)(sqlite3_context*),
|
||||
void (*xInv)(sqlite3_context*,int,sqlite3_value**),
|
||||
void(*xDestroy)(void*));
|
||||
/* Version 3.26.0 and later */
|
||||
const char *(*normalized_sql)(sqlite3_stmt*);
|
||||
/* Version 3.28.0 and later */
|
||||
int (*stmt_isexplain)(sqlite3_stmt*);
|
||||
int (*value_frombind)(sqlite3_value*);
|
||||
/* Version 3.30.0 and later */
|
||||
int (*drop_modules)(sqlite3*,const char**);
|
||||
/* Version 3.31.0 and later */
|
||||
sqlite3_int64 (*hard_heap_limit64)(sqlite3_int64);
|
||||
const char *(*uri_key)(const char*,int);
|
||||
const char *(*filename_database)(const char*);
|
||||
const char *(*filename_journal)(const char*);
|
||||
const char *(*filename_wal)(const char*);
|
||||
/* Version 3.32.0 and later */
|
||||
const char *(*create_filename)(const char*,const char*,const char*,
|
||||
int,const char**);
|
||||
void (*free_filename)(const char*);
|
||||
sqlite3_file *(*database_file_object)(const char*);
|
||||
/* Version 3.34.0 and later */
|
||||
int (*txn_state)(sqlite3*,const char*);
|
||||
/* Version 3.36.1 and later */
|
||||
sqlite3_int64 (*changes64)(sqlite3*);
|
||||
sqlite3_int64 (*total_changes64)(sqlite3*);
|
||||
/* Version 3.37.0 and later */
|
||||
int (*autovacuum_pages)(sqlite3*,
|
||||
unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
|
||||
void*, void(*)(void*));
|
||||
/* Version 3.38.0 and later */
|
||||
int (*error_offset)(sqlite3*);
|
||||
int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**);
|
||||
int (*vtab_distinct)(sqlite3_index_info*);
|
||||
int (*vtab_in)(sqlite3_index_info*,int,int);
|
||||
int (*vtab_in_first)(sqlite3_value*,sqlite3_value**);
|
||||
int (*vtab_in_next)(sqlite3_value*,sqlite3_value**);
|
||||
/* Version 3.39.0 and later */
|
||||
int (*deserialize)(sqlite3*,const char*,unsigned char*,
|
||||
sqlite3_int64,sqlite3_int64,unsigned);
|
||||
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
|
||||
unsigned int);
|
||||
const char *(*db_name)(sqlite3*,int);
|
||||
/* Version 3.40.0 and later */
|
||||
int (*value_encoding)(sqlite3_value*);
|
||||
/* Version 3.41.0 and later */
|
||||
int (*is_interrupted)(sqlite3*);
|
||||
/* Version 3.43.0 and later */
|
||||
int (*stmt_explain)(sqlite3_stmt*,int);
|
||||
/* Version 3.44.0 and later */
|
||||
void *(*get_clientdata)(sqlite3*,const char*);
|
||||
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
|
||||
};
|
||||
|
||||
/*
|
||||
** This is the function signature used for all extension entry points. It
|
||||
** is also defined in the file "loadext.c".
|
||||
*/
|
||||
typedef int (*sqlite3_loadext_entry)(
|
||||
sqlite3 *db, /* Handle to the database. */
|
||||
char **pzErrMsg, /* Used to set error string on failure. */
|
||||
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
|
||||
);
|
||||
|
||||
/*
|
||||
** The following macros redefine the API routines so that they are
|
||||
** redirected through the global sqlite3_api structure.
|
||||
**
|
||||
** This header file is also used by the loadext.c source file
|
||||
** (part of the main SQLite library - not an extension) so that
|
||||
** it can get access to the sqlite3_api_routines structure
|
||||
** definition. But the main library does not want to redefine
|
||||
** the API. So the redefinition macros are only valid if the
|
||||
** SQLITE_CORE macros is undefined.
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
|
||||
#endif
|
||||
#define sqlite3_bind_blob sqlite3_api->bind_blob
|
||||
#define sqlite3_bind_double sqlite3_api->bind_double
|
||||
#define sqlite3_bind_int sqlite3_api->bind_int
|
||||
#define sqlite3_bind_int64 sqlite3_api->bind_int64
|
||||
#define sqlite3_bind_null sqlite3_api->bind_null
|
||||
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
|
||||
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
|
||||
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
|
||||
#define sqlite3_bind_text sqlite3_api->bind_text
|
||||
#define sqlite3_bind_text16 sqlite3_api->bind_text16
|
||||
#define sqlite3_bind_value sqlite3_api->bind_value
|
||||
#define sqlite3_busy_handler sqlite3_api->busy_handler
|
||||
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
|
||||
#define sqlite3_changes sqlite3_api->changes
|
||||
#define sqlite3_close sqlite3_api->close
|
||||
#define sqlite3_collation_needed sqlite3_api->collation_needed
|
||||
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
|
||||
#define sqlite3_column_blob sqlite3_api->column_blob
|
||||
#define sqlite3_column_bytes sqlite3_api->column_bytes
|
||||
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
|
||||
#define sqlite3_column_count sqlite3_api->column_count
|
||||
#define sqlite3_column_database_name sqlite3_api->column_database_name
|
||||
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
|
||||
#define sqlite3_column_decltype sqlite3_api->column_decltype
|
||||
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
|
||||
#define sqlite3_column_double sqlite3_api->column_double
|
||||
#define sqlite3_column_int sqlite3_api->column_int
|
||||
#define sqlite3_column_int64 sqlite3_api->column_int64
|
||||
#define sqlite3_column_name sqlite3_api->column_name
|
||||
#define sqlite3_column_name16 sqlite3_api->column_name16
|
||||
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
|
||||
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
|
||||
#define sqlite3_column_table_name sqlite3_api->column_table_name
|
||||
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
|
||||
#define sqlite3_column_text sqlite3_api->column_text
|
||||
#define sqlite3_column_text16 sqlite3_api->column_text16
|
||||
#define sqlite3_column_type sqlite3_api->column_type
|
||||
#define sqlite3_column_value sqlite3_api->column_value
|
||||
#define sqlite3_commit_hook sqlite3_api->commit_hook
|
||||
#define sqlite3_complete sqlite3_api->complete
|
||||
#define sqlite3_complete16 sqlite3_api->complete16
|
||||
#define sqlite3_create_collation sqlite3_api->create_collation
|
||||
#define sqlite3_create_collation16 sqlite3_api->create_collation16
|
||||
#define sqlite3_create_function sqlite3_api->create_function
|
||||
#define sqlite3_create_function16 sqlite3_api->create_function16
|
||||
#define sqlite3_create_module sqlite3_api->create_module
|
||||
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
|
||||
#define sqlite3_data_count sqlite3_api->data_count
|
||||
#define sqlite3_db_handle sqlite3_api->db_handle
|
||||
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
|
||||
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
|
||||
#define sqlite3_errcode sqlite3_api->errcode
|
||||
#define sqlite3_errmsg sqlite3_api->errmsg
|
||||
#define sqlite3_errmsg16 sqlite3_api->errmsg16
|
||||
#define sqlite3_exec sqlite3_api->exec
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_expired sqlite3_api->expired
|
||||
#endif
|
||||
#define sqlite3_finalize sqlite3_api->finalize
|
||||
#define sqlite3_free sqlite3_api->free
|
||||
#define sqlite3_free_table sqlite3_api->free_table
|
||||
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
|
||||
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
|
||||
#define sqlite3_get_table sqlite3_api->get_table
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_global_recover sqlite3_api->global_recover
|
||||
#endif
|
||||
#define sqlite3_interrupt sqlite3_api->interruptx
|
||||
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
|
||||
#define sqlite3_libversion sqlite3_api->libversion
|
||||
#define sqlite3_libversion_number sqlite3_api->libversion_number
|
||||
#define sqlite3_malloc sqlite3_api->malloc
|
||||
#define sqlite3_mprintf sqlite3_api->mprintf
|
||||
#define sqlite3_open sqlite3_api->open
|
||||
#define sqlite3_open16 sqlite3_api->open16
|
||||
#define sqlite3_prepare sqlite3_api->prepare
|
||||
#define sqlite3_prepare16 sqlite3_api->prepare16
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
#define sqlite3_profile sqlite3_api->profile
|
||||
#define sqlite3_progress_handler sqlite3_api->progress_handler
|
||||
#define sqlite3_realloc sqlite3_api->realloc
|
||||
#define sqlite3_reset sqlite3_api->reset
|
||||
#define sqlite3_result_blob sqlite3_api->result_blob
|
||||
#define sqlite3_result_double sqlite3_api->result_double
|
||||
#define sqlite3_result_error sqlite3_api->result_error
|
||||
#define sqlite3_result_error16 sqlite3_api->result_error16
|
||||
#define sqlite3_result_int sqlite3_api->result_int
|
||||
#define sqlite3_result_int64 sqlite3_api->result_int64
|
||||
#define sqlite3_result_null sqlite3_api->result_null
|
||||
#define sqlite3_result_text sqlite3_api->result_text
|
||||
#define sqlite3_result_text16 sqlite3_api->result_text16
|
||||
#define sqlite3_result_text16be sqlite3_api->result_text16be
|
||||
#define sqlite3_result_text16le sqlite3_api->result_text16le
|
||||
#define sqlite3_result_value sqlite3_api->result_value
|
||||
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
||||
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
||||
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
||||
#define sqlite3_snprintf sqlite3_api->xsnprintf
|
||||
#define sqlite3_step sqlite3_api->step
|
||||
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
||||
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
||||
#define sqlite3_total_changes sqlite3_api->total_changes
|
||||
#define sqlite3_trace sqlite3_api->trace
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
|
||||
#endif
|
||||
#define sqlite3_update_hook sqlite3_api->update_hook
|
||||
#define sqlite3_user_data sqlite3_api->user_data
|
||||
#define sqlite3_value_blob sqlite3_api->value_blob
|
||||
#define sqlite3_value_bytes sqlite3_api->value_bytes
|
||||
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
|
||||
#define sqlite3_value_double sqlite3_api->value_double
|
||||
#define sqlite3_value_int sqlite3_api->value_int
|
||||
#define sqlite3_value_int64 sqlite3_api->value_int64
|
||||
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
|
||||
#define sqlite3_value_text sqlite3_api->value_text
|
||||
#define sqlite3_value_text16 sqlite3_api->value_text16
|
||||
#define sqlite3_value_text16be sqlite3_api->value_text16be
|
||||
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
||||
#define sqlite3_value_type sqlite3_api->value_type
|
||||
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
||||
#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
|
||||
#define sqlite3_overload_function sqlite3_api->overload_function
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
|
||||
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
|
||||
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
|
||||
#define sqlite3_blob_close sqlite3_api->blob_close
|
||||
#define sqlite3_blob_open sqlite3_api->blob_open
|
||||
#define sqlite3_blob_read sqlite3_api->blob_read
|
||||
#define sqlite3_blob_write sqlite3_api->blob_write
|
||||
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
|
||||
#define sqlite3_file_control sqlite3_api->file_control
|
||||
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
|
||||
#define sqlite3_memory_used sqlite3_api->memory_used
|
||||
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
|
||||
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
|
||||
#define sqlite3_mutex_free sqlite3_api->mutex_free
|
||||
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
|
||||
#define sqlite3_mutex_try sqlite3_api->mutex_try
|
||||
#define sqlite3_open_v2 sqlite3_api->open_v2
|
||||
#define sqlite3_release_memory sqlite3_api->release_memory
|
||||
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
|
||||
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
|
||||
#define sqlite3_sleep sqlite3_api->sleep
|
||||
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
|
||||
#define sqlite3_vfs_find sqlite3_api->vfs_find
|
||||
#define sqlite3_vfs_register sqlite3_api->vfs_register
|
||||
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
|
||||
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
|
||||
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
|
||||
#define sqlite3_result_error_code sqlite3_api->result_error_code
|
||||
#define sqlite3_test_control sqlite3_api->test_control
|
||||
#define sqlite3_randomness sqlite3_api->randomness
|
||||
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
|
||||
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
|
||||
#define sqlite3_limit sqlite3_api->limit
|
||||
#define sqlite3_next_stmt sqlite3_api->next_stmt
|
||||
#define sqlite3_sql sqlite3_api->sql
|
||||
#define sqlite3_status sqlite3_api->status
|
||||
#define sqlite3_backup_finish sqlite3_api->backup_finish
|
||||
#define sqlite3_backup_init sqlite3_api->backup_init
|
||||
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
|
||||
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
|
||||
#define sqlite3_backup_step sqlite3_api->backup_step
|
||||
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
|
||||
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
|
||||
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
|
||||
#define sqlite3_db_config sqlite3_api->db_config
|
||||
#define sqlite3_db_mutex sqlite3_api->db_mutex
|
||||
#define sqlite3_db_status sqlite3_api->db_status
|
||||
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
|
||||
#define sqlite3_log sqlite3_api->log
|
||||
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
|
||||
#define sqlite3_sourceid sqlite3_api->sourceid
|
||||
#define sqlite3_stmt_status sqlite3_api->stmt_status
|
||||
#define sqlite3_strnicmp sqlite3_api->strnicmp
|
||||
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
|
||||
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
||||
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
||||
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
||||
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
|
||||
#define sqlite3_vtab_config sqlite3_api->vtab_config
|
||||
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
|
||||
/* Version 3.7.16 and later */
|
||||
#define sqlite3_close_v2 sqlite3_api->close_v2
|
||||
#define sqlite3_db_filename sqlite3_api->db_filename
|
||||
#define sqlite3_db_readonly sqlite3_api->db_readonly
|
||||
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
|
||||
#define sqlite3_errstr sqlite3_api->errstr
|
||||
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
|
||||
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
|
||||
#define sqlite3_stricmp sqlite3_api->stricmp
|
||||
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
|
||||
#define sqlite3_uri_int64 sqlite3_api->uri_int64
|
||||
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
|
||||
#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
|
||||
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
|
||||
/* Version 3.8.7 and later */
|
||||
#define sqlite3_auto_extension sqlite3_api->auto_extension
|
||||
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
|
||||
#define sqlite3_bind_text64 sqlite3_api->bind_text64
|
||||
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
|
||||
#define sqlite3_load_extension sqlite3_api->load_extension
|
||||
#define sqlite3_malloc64 sqlite3_api->malloc64
|
||||
#define sqlite3_msize sqlite3_api->msize
|
||||
#define sqlite3_realloc64 sqlite3_api->realloc64
|
||||
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
|
||||
#define sqlite3_result_blob64 sqlite3_api->result_blob64
|
||||
#define sqlite3_result_text64 sqlite3_api->result_text64
|
||||
#define sqlite3_strglob sqlite3_api->strglob
|
||||
/* Version 3.8.11 and later */
|
||||
#define sqlite3_value_dup sqlite3_api->value_dup
|
||||
#define sqlite3_value_free sqlite3_api->value_free
|
||||
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
|
||||
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
|
||||
/* Version 3.9.0 and later */
|
||||
#define sqlite3_value_subtype sqlite3_api->value_subtype
|
||||
#define sqlite3_result_subtype sqlite3_api->result_subtype
|
||||
/* Version 3.10.0 and later */
|
||||
#define sqlite3_status64 sqlite3_api->status64
|
||||
#define sqlite3_strlike sqlite3_api->strlike
|
||||
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
|
||||
/* Version 3.12.0 and later */
|
||||
#define sqlite3_system_errno sqlite3_api->system_errno
|
||||
/* Version 3.14.0 and later */
|
||||
#define sqlite3_trace_v2 sqlite3_api->trace_v2
|
||||
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
|
||||
/* Version 3.18.0 and later */
|
||||
#define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid
|
||||
/* Version 3.20.0 and later */
|
||||
#define sqlite3_prepare_v3 sqlite3_api->prepare_v3
|
||||
#define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3
|
||||
#define sqlite3_bind_pointer sqlite3_api->bind_pointer
|
||||
#define sqlite3_result_pointer sqlite3_api->result_pointer
|
||||
#define sqlite3_value_pointer sqlite3_api->value_pointer
|
||||
/* Version 3.22.0 and later */
|
||||
#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
|
||||
#define sqlite3_value_nochange sqlite3_api->value_nochange
|
||||
#define sqlite3_vtab_collation sqlite3_api->vtab_collation
|
||||
/* Version 3.24.0 and later */
|
||||
#define sqlite3_keyword_count sqlite3_api->keyword_count
|
||||
#define sqlite3_keyword_name sqlite3_api->keyword_name
|
||||
#define sqlite3_keyword_check sqlite3_api->keyword_check
|
||||
#define sqlite3_str_new sqlite3_api->str_new
|
||||
#define sqlite3_str_finish sqlite3_api->str_finish
|
||||
#define sqlite3_str_appendf sqlite3_api->str_appendf
|
||||
#define sqlite3_str_vappendf sqlite3_api->str_vappendf
|
||||
#define sqlite3_str_append sqlite3_api->str_append
|
||||
#define sqlite3_str_appendall sqlite3_api->str_appendall
|
||||
#define sqlite3_str_appendchar sqlite3_api->str_appendchar
|
||||
#define sqlite3_str_reset sqlite3_api->str_reset
|
||||
#define sqlite3_str_errcode sqlite3_api->str_errcode
|
||||
#define sqlite3_str_length sqlite3_api->str_length
|
||||
#define sqlite3_str_value sqlite3_api->str_value
|
||||
/* Version 3.25.0 and later */
|
||||
#define sqlite3_create_window_function sqlite3_api->create_window_function
|
||||
/* Version 3.26.0 and later */
|
||||
#define sqlite3_normalized_sql sqlite3_api->normalized_sql
|
||||
/* Version 3.28.0 and later */
|
||||
#define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain
|
||||
#define sqlite3_value_frombind sqlite3_api->value_frombind
|
||||
/* Version 3.30.0 and later */
|
||||
#define sqlite3_drop_modules sqlite3_api->drop_modules
|
||||
/* Version 3.31.0 and later */
|
||||
#define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64
|
||||
#define sqlite3_uri_key sqlite3_api->uri_key
|
||||
#define sqlite3_filename_database sqlite3_api->filename_database
|
||||
#define sqlite3_filename_journal sqlite3_api->filename_journal
|
||||
#define sqlite3_filename_wal sqlite3_api->filename_wal
|
||||
/* Version 3.32.0 and later */
|
||||
#define sqlite3_create_filename sqlite3_api->create_filename
|
||||
#define sqlite3_free_filename sqlite3_api->free_filename
|
||||
#define sqlite3_database_file_object sqlite3_api->database_file_object
|
||||
/* Version 3.34.0 and later */
|
||||
#define sqlite3_txn_state sqlite3_api->txn_state
|
||||
/* Version 3.36.1 and later */
|
||||
#define sqlite3_changes64 sqlite3_api->changes64
|
||||
#define sqlite3_total_changes64 sqlite3_api->total_changes64
|
||||
/* Version 3.37.0 and later */
|
||||
#define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages
|
||||
/* Version 3.38.0 and later */
|
||||
#define sqlite3_error_offset sqlite3_api->error_offset
|
||||
#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value
|
||||
#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct
|
||||
#define sqlite3_vtab_in sqlite3_api->vtab_in
|
||||
#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
|
||||
#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
|
||||
/* Version 3.39.0 and later */
|
||||
#ifndef SQLITE_OMIT_DESERIALIZE
|
||||
#define sqlite3_deserialize sqlite3_api->deserialize
|
||||
#define sqlite3_serialize sqlite3_api->serialize
|
||||
#endif
|
||||
#define sqlite3_db_name sqlite3_api->db_name
|
||||
/* Version 3.40.0 and later */
|
||||
#define sqlite3_value_encoding sqlite3_api->value_encoding
|
||||
/* Version 3.41.0 and later */
|
||||
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
|
||||
/* Version 3.43.0 and later */
|
||||
#define sqlite3_stmt_explain sqlite3_api->stmt_explain
|
||||
/* Version 3.44.0 and later */
|
||||
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
|
||||
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
/* This case when the file really is being compiled as a loadable
|
||||
** extension */
|
||||
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
|
||||
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
|
||||
# define SQLITE_EXTENSION_INIT3 \
|
||||
extern const sqlite3_api_routines *sqlite3_api;
|
||||
#else
|
||||
/* This case when the file is being statically linked into the
|
||||
** application */
|
||||
# define SQLITE_EXTENSION_INIT1 /*no-op*/
|
||||
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
|
||||
# define SQLITE_EXTENSION_INIT3 /*no-op*/
|
||||
#endif
|
||||
|
||||
#endif /* SQLITE3EXT_H */
|
|
@ -0,0 +1,655 @@
|
|||
/*
|
||||
** 2010 April 7
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file implements an example of a simple VFS implementation that
|
||||
** omits complex features often not required or not possible on embedded
|
||||
** platforms. Code is included to buffer writes to the journal file,
|
||||
** which can be a significant performance improvement on some embedded
|
||||
** platforms.
|
||||
**
|
||||
** OVERVIEW
|
||||
**
|
||||
** The code in this file implements a minimal SQLite VFS that can be
|
||||
** used on Linux and other posix-like operating systems. The following
|
||||
** system calls are used:
|
||||
**
|
||||
** File-system: access(), unlink(), getcwd()
|
||||
** File IO: open(), read(), write(), fsync(), close(), fstat()
|
||||
** Other: sleep(), usleep(), time()
|
||||
**
|
||||
** The following VFS features are omitted:
|
||||
**
|
||||
** 1. File locking. The user must ensure that there is at most one
|
||||
** connection to each database when using this VFS. Multiple
|
||||
** connections to a single shared-cache count as a single connection
|
||||
** for the purposes of the previous statement.
|
||||
**
|
||||
** 2. The loading of dynamic extensions (shared libraries).
|
||||
**
|
||||
** 3. Temporary files. The user must configure SQLite to use in-memory
|
||||
** temp files when using this VFS. The easiest way to do this is to
|
||||
** compile with:
|
||||
**
|
||||
** -DSQLITE_TEMP_STORE=3
|
||||
**
|
||||
** 4. File truncation. As of version 3.6.24, SQLite may run without
|
||||
** a working xTruncate() call, providing the user does not configure
|
||||
** SQLite to use "journal_mode=truncate", or use both
|
||||
** "journal_mode=persist" and ATTACHed databases.
|
||||
**
|
||||
** It is assumed that the system uses UNIX-like path-names. Specifically,
|
||||
** that '/' characters are used to separate path components and that
|
||||
** a path-name is a relative path unless it begins with a '/'. And that
|
||||
** no UTF-8 encoded paths are greater than 512 bytes in length.
|
||||
**
|
||||
** JOURNAL WRITE-BUFFERING
|
||||
**
|
||||
** To commit a transaction to the database, SQLite first writes rollback
|
||||
** information into the journal file. This usually consists of 4 steps:
|
||||
**
|
||||
** 1. The rollback information is sequentially written into the journal
|
||||
** file, starting at the start of the file.
|
||||
** 2. The journal file is synced to disk.
|
||||
** 3. A modification is made to the first few bytes of the journal file.
|
||||
** 4. The journal file is synced to disk again.
|
||||
**
|
||||
** Most of the data is written in step 1 using a series of calls to the
|
||||
** VFS xWrite() method. The buffers passed to the xWrite() calls are of
|
||||
** various sizes. For example, as of version 3.6.24, when committing a
|
||||
** transaction that modifies 3 pages of a database file that uses 4096
|
||||
** byte pages residing on a media with 512 byte sectors, SQLite makes
|
||||
** eleven calls to the xWrite() method to create the rollback journal,
|
||||
** as follows:
|
||||
**
|
||||
** Write offset | Bytes written
|
||||
** ----------------------------
|
||||
** 0 512
|
||||
** 512 4
|
||||
** 516 4096
|
||||
** 4612 4
|
||||
** 4616 4
|
||||
** 4620 4096
|
||||
** 8716 4
|
||||
** 8720 4
|
||||
** 8724 4096
|
||||
** 12820 4
|
||||
** ++++++++++++SYNC+++++++++++
|
||||
** 0 12
|
||||
** ++++++++++++SYNC+++++++++++
|
||||
**
|
||||
** On many operating systems, this is an efficient way to write to a file.
|
||||
** However, on some embedded systems that do not cache writes in OS
|
||||
** buffers it is much more efficient to write data in blocks that are
|
||||
** an integer multiple of the sector-size in size and aligned at the
|
||||
** start of a sector.
|
||||
**
|
||||
** To work around this, the code in this file allocates a fixed size
|
||||
** buffer of SQLITE_DEMOVFS_BUFFERSZ using sqlite3_malloc() whenever a
|
||||
** journal file is opened. It uses the buffer to coalesce sequential
|
||||
** writes into aligned SQLITE_DEMOVFS_BUFFERSZ blocks. When SQLite
|
||||
** invokes the xSync() method to sync the contents of the file to disk,
|
||||
** all accumulated data is written out, even if it does not constitute
|
||||
** a complete block. This means the actual IO to create the rollback
|
||||
** journal for the example transaction above is this:
|
||||
**
|
||||
** Write offset | Bytes written
|
||||
** ----------------------------
|
||||
** 0 8192
|
||||
** 8192 4632
|
||||
** ++++++++++++SYNC+++++++++++
|
||||
** 0 12
|
||||
** ++++++++++++SYNC+++++++++++
|
||||
**
|
||||
** Much more efficient if the underlying OS is not caching write
|
||||
** operations.
|
||||
*/
|
||||
|
||||
/*removed tests from https://www.sqlite.org/src/doc/trunk/src/test_demovfs.c*/
|
||||
|
||||
#if defined(GEKKO)
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/param.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
/*
|
||||
** Size of the write buffer used by journal files in bytes.
|
||||
*/
|
||||
#ifndef SQLITE_DEMOVFS_BUFFERSZ
|
||||
# define SQLITE_DEMOVFS_BUFFERSZ 8192
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The maximum pathname length supported by this VFS.
|
||||
*/
|
||||
#define MAXPATHNAME 512
|
||||
|
||||
/*
|
||||
** When using this VFS, the sqlite3_file* handles that SQLite uses are
|
||||
** actually pointers to instances of type DemoFile.
|
||||
*/
|
||||
typedef struct DemoFile DemoFile;
|
||||
struct DemoFile {
|
||||
sqlite3_file base; /* Base class. Must be first. */
|
||||
int fd; /* File descriptor */
|
||||
|
||||
char *aBuffer; /* Pointer to malloc'd buffer */
|
||||
int nBuffer; /* Valid bytes of data in zBuffer */
|
||||
sqlite3_int64 iBufferOfst; /* Offset in file of zBuffer[0] */
|
||||
};
|
||||
|
||||
/*
|
||||
** Write directly to the file passed as the first argument. Even if the
|
||||
** file has a write-buffer (DemoFile.aBuffer), ignore it.
|
||||
*/
|
||||
static int demoDirectWrite(
|
||||
DemoFile *p, /* File handle */
|
||||
const void *zBuf, /* Buffer containing data to write */
|
||||
int iAmt, /* Size of data to write in bytes */
|
||||
sqlite_int64 iOfst /* File offset to write to */
|
||||
){
|
||||
off_t ofst; /* Return value from lseek() */
|
||||
size_t nWrite; /* Return value from write() */
|
||||
|
||||
ofst = lseek(p->fd, iOfst, SEEK_SET);
|
||||
if( ofst!=iOfst ){
|
||||
return SQLITE_IOERR_WRITE;
|
||||
}
|
||||
|
||||
nWrite = write(p->fd, zBuf, iAmt);
|
||||
if( nWrite!=iAmt ){
|
||||
return SQLITE_IOERR_WRITE;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Flush the contents of the DemoFile.aBuffer buffer to disk. This is a
|
||||
** no-op if this particular file does not have a buffer (i.e. it is not
|
||||
** a journal file) or if the buffer is currently empty.
|
||||
*/
|
||||
static int demoFlushBuffer(DemoFile *p){
|
||||
int rc = SQLITE_OK;
|
||||
if( p->nBuffer ){
|
||||
rc = demoDirectWrite(p, p->aBuffer, p->nBuffer, p->iBufferOfst);
|
||||
p->nBuffer = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a file.
|
||||
*/
|
||||
static int demoClose(sqlite3_file *pFile){
|
||||
int rc;
|
||||
DemoFile *p = (DemoFile*)pFile;
|
||||
rc = demoFlushBuffer(p);
|
||||
sqlite3_free(p->aBuffer);
|
||||
close(p->fd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read data from a file.
|
||||
*/
|
||||
static int demoRead(
|
||||
sqlite3_file *pFile,
|
||||
void *zBuf,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
DemoFile *p = (DemoFile*)pFile;
|
||||
off_t ofst; /* Return value from lseek() */
|
||||
int nRead; /* Return value from read() */
|
||||
int rc; /* Return code from demoFlushBuffer() */
|
||||
|
||||
/* Flush any data in the write buffer to disk in case this operation
|
||||
** is trying to read data the file-region currently cached in the buffer.
|
||||
** It would be possible to detect this case and possibly save an
|
||||
** unnecessary write here, but in practice SQLite will rarely read from
|
||||
** a journal file when there is data cached in the write-buffer.
|
||||
*/
|
||||
rc = demoFlushBuffer(p);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
|
||||
ofst = lseek(p->fd, iOfst, SEEK_SET);
|
||||
if( ofst!=iOfst ){
|
||||
return SQLITE_IOERR_READ;
|
||||
}
|
||||
nRead = read(p->fd, zBuf, iAmt);
|
||||
|
||||
if( nRead==iAmt ){
|
||||
return SQLITE_OK;
|
||||
}else if( nRead>=0 ){
|
||||
if( nRead<iAmt ){
|
||||
memset(&((char*)zBuf)[nRead], 0, iAmt-nRead);
|
||||
}
|
||||
return SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
|
||||
return SQLITE_IOERR_READ;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write data to a crash-file.
|
||||
*/
|
||||
static int demoWrite(
|
||||
sqlite3_file *pFile,
|
||||
const void *zBuf,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
DemoFile *p = (DemoFile*)pFile;
|
||||
|
||||
if( p->aBuffer ){
|
||||
char *z = (char *)zBuf; /* Pointer to remaining data to write */
|
||||
int n = iAmt; /* Number of bytes at z */
|
||||
sqlite3_int64 i = iOfst; /* File offset to write to */
|
||||
|
||||
while( n>0 ){
|
||||
int nCopy; /* Number of bytes to copy into buffer */
|
||||
|
||||
/* If the buffer is full, or if this data is not being written directly
|
||||
** following the data already buffered, flush the buffer. Flushing
|
||||
** the buffer is a no-op if it is empty.
|
||||
*/
|
||||
if( p->nBuffer==SQLITE_DEMOVFS_BUFFERSZ || p->iBufferOfst+p->nBuffer!=i ){
|
||||
int rc = demoFlushBuffer(p);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
assert( p->nBuffer==0 || p->iBufferOfst+p->nBuffer==i );
|
||||
p->iBufferOfst = i - p->nBuffer;
|
||||
|
||||
/* Copy as much data as possible into the buffer. */
|
||||
nCopy = SQLITE_DEMOVFS_BUFFERSZ - p->nBuffer;
|
||||
if( nCopy>n ){
|
||||
nCopy = n;
|
||||
}
|
||||
memcpy(&p->aBuffer[p->nBuffer], z, nCopy);
|
||||
p->nBuffer += nCopy;
|
||||
|
||||
n -= nCopy;
|
||||
i += nCopy;
|
||||
z += nCopy;
|
||||
}
|
||||
}else{
|
||||
return demoDirectWrite(p, zBuf, iAmt, iOfst);
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Truncate a file. This is a no-op for this VFS (see header comments at
|
||||
** the top of the file).
|
||||
*/
|
||||
static int demoTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
||||
#if 0
|
||||
if( ftruncate(((DemoFile *)pFile)->fd, size) ) return SQLITE_IOERR_TRUNCATE;
|
||||
#endif
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sync the contents of the file to the persistent media.
|
||||
*/
|
||||
static int demoSync(sqlite3_file *pFile, int flags){
|
||||
DemoFile *p = (DemoFile*)pFile;
|
||||
int rc;
|
||||
|
||||
rc = demoFlushBuffer(p);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = fsync(p->fd);
|
||||
return (rc==0 ? SQLITE_OK : SQLITE_IOERR_FSYNC);
|
||||
}
|
||||
|
||||
/*
|
||||
** Write the size of the file in bytes to *pSize.
|
||||
*/
|
||||
static int demoFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
||||
DemoFile *p = (DemoFile*)pFile;
|
||||
int rc; /* Return code from fstat() call */
|
||||
struct stat sStat; /* Output of fstat() call */
|
||||
|
||||
/* Flush the contents of the buffer to disk. As with the flush in the
|
||||
** demoRead() method, it would be possible to avoid this and save a write
|
||||
** here and there. But in practice this comes up so infrequently it is
|
||||
** not worth the trouble.
|
||||
*/
|
||||
rc = demoFlushBuffer(p);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = fstat(p->fd, &sStat);
|
||||
if( rc!=0 ) return SQLITE_IOERR_FSTAT;
|
||||
*pSize = sStat.st_size;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Locking functions. The xLock() and xUnlock() methods are both no-ops.
|
||||
** The xCheckReservedLock() always indicates that no other process holds
|
||||
** a reserved lock on the database file. This ensures that if a hot-journal
|
||||
** file is found in the file-system it is rolled back.
|
||||
*/
|
||||
static int demoLock(sqlite3_file *pFile, int eLock){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int demoUnlock(sqlite3_file *pFile, int eLock){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int demoCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
||||
*pResOut = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** No xFileControl() verbs are implemented by this VFS.
|
||||
*/
|
||||
static int demoFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
return SQLITE_NOTFOUND;
|
||||
}
|
||||
|
||||
/*
|
||||
** The xSectorSize() and xDeviceCharacteristics() methods. These two
|
||||
** may return special values allowing SQLite to optimize file-system
|
||||
** access to some extent. But it is also safe to simply return 0.
|
||||
*/
|
||||
static int demoSectorSize(sqlite3_file *pFile){
|
||||
return 0;
|
||||
}
|
||||
static int demoDeviceCharacteristics(sqlite3_file *pFile){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a file handle.
|
||||
*/
|
||||
static int demoOpen(
|
||||
sqlite3_vfs *pVfs, /* VFS */
|
||||
const char *zName, /* File to open, or 0 for a temp file */
|
||||
sqlite3_file *pFile, /* Pointer to DemoFile struct to populate */
|
||||
int flags, /* Input SQLITE_OPEN_XXX flags */
|
||||
int *pOutFlags /* Output SQLITE_OPEN_XXX flags (or NULL) */
|
||||
){
|
||||
static const sqlite3_io_methods demoio = {
|
||||
1, /* iVersion */
|
||||
demoClose, /* xClose */
|
||||
demoRead, /* xRead */
|
||||
demoWrite, /* xWrite */
|
||||
demoTruncate, /* xTruncate */
|
||||
demoSync, /* xSync */
|
||||
demoFileSize, /* xFileSize */
|
||||
demoLock, /* xLock */
|
||||
demoUnlock, /* xUnlock */
|
||||
demoCheckReservedLock, /* xCheckReservedLock */
|
||||
demoFileControl, /* xFileControl */
|
||||
demoSectorSize, /* xSectorSize */
|
||||
demoDeviceCharacteristics /* xDeviceCharacteristics */
|
||||
};
|
||||
|
||||
DemoFile *p = (DemoFile*)pFile; /* Populate this structure */
|
||||
int oflags = 0; /* flags to pass to open() call */
|
||||
char *aBuf = 0;
|
||||
|
||||
if( zName==0 ){
|
||||
return SQLITE_IOERR;
|
||||
}
|
||||
|
||||
if( flags&SQLITE_OPEN_MAIN_JOURNAL ){
|
||||
aBuf = (char *)sqlite3_malloc(SQLITE_DEMOVFS_BUFFERSZ);
|
||||
if( !aBuf ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
if( flags&SQLITE_OPEN_EXCLUSIVE ) oflags |= O_EXCL;
|
||||
if( flags&SQLITE_OPEN_CREATE ) oflags |= O_CREAT;
|
||||
if( flags&SQLITE_OPEN_READONLY ) oflags |= O_RDONLY;
|
||||
if( flags&SQLITE_OPEN_READWRITE ) oflags |= O_RDWR;
|
||||
|
||||
memset(p, 0, sizeof(DemoFile));
|
||||
p->fd = open(zName, oflags);
|
||||
if( p->fd<0 ){
|
||||
sqlite3_free(aBuf);
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
p->aBuffer = aBuf;
|
||||
|
||||
if( pOutFlags ){
|
||||
*pOutFlags = flags;
|
||||
}
|
||||
p->base.pMethods = &demoio;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete the file identified by argument zPath. If the dirSync parameter
|
||||
** is non-zero, then ensure the file-system modification to delete the
|
||||
** file has been synced to disk before returning.
|
||||
*/
|
||||
static int demoDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
||||
int rc; /* Return code */
|
||||
|
||||
rc = unlink(zPath);
|
||||
if( rc!=0 && errno==ENOENT ) return SQLITE_OK;
|
||||
|
||||
if( rc==0 && dirSync ){
|
||||
int dfd; /* File descriptor open on directory */
|
||||
char *zSlash;
|
||||
char zDir[MAXPATHNAME+1]; /* Name of directory containing file zPath */
|
||||
|
||||
/* Figure out the directory name from the path of the file deleted. */
|
||||
sqlite3_snprintf(MAXPATHNAME, zDir, "%s", zPath);
|
||||
zDir[MAXPATHNAME] = '\0';
|
||||
zSlash = strrchr(zDir,'/');
|
||||
if( zSlash ){
|
||||
/* Open a file-descriptor on the directory. Sync. Close. */
|
||||
zSlash[0] = 0;
|
||||
dfd = open(zDir, O_RDONLY, 0);
|
||||
if( dfd<0 ){
|
||||
rc = -1;
|
||||
}else{
|
||||
rc = fsync(dfd);
|
||||
close(dfd);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (rc==0 ? SQLITE_OK : SQLITE_IOERR_DELETE);
|
||||
}
|
||||
|
||||
#ifndef F_OK
|
||||
# define F_OK 0
|
||||
#endif
|
||||
#ifndef R_OK
|
||||
# define R_OK 4
|
||||
#endif
|
||||
#ifndef W_OK
|
||||
# define W_OK 2
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Query the file-system to see if the named file exists, is readable or
|
||||
** is both readable and writable.
|
||||
*/
|
||||
static int demoAccess(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zPath,
|
||||
int flags,
|
||||
int *pResOut
|
||||
){
|
||||
int rc; /* access() return code */
|
||||
int eAccess = F_OK; /* Second argument to access() */
|
||||
|
||||
assert( flags==SQLITE_ACCESS_EXISTS /* access(zPath, F_OK) */
|
||||
|| flags==SQLITE_ACCESS_READ /* access(zPath, R_OK) */
|
||||
|| flags==SQLITE_ACCESS_READWRITE /* access(zPath, R_OK|W_OK) */
|
||||
);
|
||||
|
||||
if( flags==SQLITE_ACCESS_READWRITE ) eAccess = R_OK|W_OK;
|
||||
if( flags==SQLITE_ACCESS_READ ) eAccess = R_OK;
|
||||
|
||||
rc = access(zPath, eAccess);
|
||||
*pResOut = (rc==0);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument zPath points to a nul-terminated string containing a file path.
|
||||
** If zPath is an absolute path, then it is copied as is into the output
|
||||
** buffer. Otherwise, if it is a relative path, then the equivalent full
|
||||
** path is written to the output buffer.
|
||||
**
|
||||
** This function assumes that paths are UNIX style. Specifically, that:
|
||||
**
|
||||
** 1. Path components are separated by a '/'. and
|
||||
** 2. Full paths begin with a '/' character.
|
||||
*/
|
||||
static int demoFullPathname(
|
||||
sqlite3_vfs *pVfs, /* VFS */
|
||||
const char *zPath, /* Input path (possibly a relative path) */
|
||||
int nPathOut, /* Size of output buffer in bytes */
|
||||
char *zPathOut /* Pointer to output buffer */
|
||||
){
|
||||
char zDir[MAXPATHNAME+1];
|
||||
if( zPath[0]=='/' ){
|
||||
zDir[0] = '\0';
|
||||
}else{
|
||||
if( getcwd(zDir, sizeof(zDir))==0 ) return SQLITE_IOERR;
|
||||
}
|
||||
zDir[MAXPATHNAME] = '\0';
|
||||
|
||||
sqlite3_snprintf(nPathOut, zPathOut, "%s/%s", zDir, zPath);
|
||||
zPathOut[nPathOut-1] = '\0';
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** The following four VFS methods:
|
||||
**
|
||||
** xDlOpen
|
||||
** xDlError
|
||||
** xDlSym
|
||||
** xDlClose
|
||||
**
|
||||
** are supposed to implement the functionality needed by SQLite to load
|
||||
** extensions compiled as shared objects. This simple VFS does not support
|
||||
** this functionality, so the following functions are no-ops.
|
||||
*/
|
||||
static void *demoDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
||||
return 0;
|
||||
}
|
||||
static void demoDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
||||
sqlite3_snprintf(nByte, zErrMsg, "Loadable extensions are not supported");
|
||||
zErrMsg[nByte-1] = '\0';
|
||||
}
|
||||
static void (*demoDlSym(sqlite3_vfs *pVfs, void *pH, const char *z))(void){
|
||||
return 0;
|
||||
}
|
||||
static void demoDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** Parameter zByte points to a buffer nByte bytes in size. Populate this
|
||||
** buffer with pseudo-random data.
|
||||
*/
|
||||
static int demoRandomness(sqlite3_vfs *pVfs, int nByte, char *zByte){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sleep for at least nMicro microseconds. Return the (approximate) number
|
||||
** of microseconds slept for.
|
||||
*/
|
||||
static int demoSleep(sqlite3_vfs *pVfs, int nMicro){
|
||||
sleep(nMicro / 1000000);
|
||||
usleep(nMicro % 1000000);
|
||||
return nMicro;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set *pTime to the current UTC time expressed as a Julian day. Return
|
||||
** SQLITE_OK if successful, or an error code otherwise.
|
||||
**
|
||||
** http://en.wikipedia.org/wiki/Julian_day
|
||||
**
|
||||
** This implementation is not very good. The current time is rounded to
|
||||
** an integer number of seconds. Also, assuming time_t is a signed 32-bit
|
||||
** value, it will stop working some time in the year 2038 AD (the so-called
|
||||
** "year 2038" problem that afflicts systems that store time this way).
|
||||
*/
|
||||
static int demoCurrentTime(sqlite3_vfs *pVfs, double *pTime){
|
||||
time_t t = time(0);
|
||||
*pTime = t/86400.0 + 2440587.5;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function returns a pointer to the VFS implemented in this file.
|
||||
** To make the VFS available to SQLite:
|
||||
**
|
||||
** sqlite3_vfs_register(sqlite3_demovfs(), 0);
|
||||
*/
|
||||
uid_t geteuid(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fchown(int fildes, uid_t owner, gid_t group)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
sqlite3_vfs *sqlite3_demovfs(void){
|
||||
static sqlite3_vfs demovfs = {
|
||||
1, /* iVersion */
|
||||
sizeof(DemoFile), /* szOsFile */
|
||||
MAXPATHNAME, /* mxPathname */
|
||||
0, /* pNext */
|
||||
"demo", /* zName */
|
||||
0, /* pAppData */
|
||||
demoOpen, /* xOpen */
|
||||
demoDelete, /* xDelete */
|
||||
demoAccess, /* xAccess */
|
||||
demoFullPathname, /* xFullPathname */
|
||||
demoDlOpen, /* xDlOpen */
|
||||
demoDlError, /* xDlError */
|
||||
demoDlSym, /* xDlSym */
|
||||
demoDlClose, /* xDlClose */
|
||||
demoRandomness, /* xRandomness */
|
||||
demoSleep, /* xSleep */
|
||||
demoCurrentTime, /* xCurrentTime */
|
||||
};
|
||||
return &demovfs;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,90 @@
|
|||
#include "CrossLang.hpp"
|
||||
namespace Tesses::CrossLang {
|
||||
TArgWrapper* TArgWrapper::Create(GCList& ls, TCallable* callable)
|
||||
{
|
||||
TArgWrapper* argWrapper = new TArgWrapper();
|
||||
argWrapper->callable = callable;
|
||||
GC* gc = ls.GetGC();
|
||||
ls.Add(argWrapper);
|
||||
gc->Watch(argWrapper);
|
||||
return argWrapper;
|
||||
}
|
||||
TArgWrapper* TArgWrapper::Create(GCList* ls, TCallable* callable)
|
||||
{
|
||||
TArgWrapper* argWrapper = new TArgWrapper();
|
||||
argWrapper->callable = callable;
|
||||
GC* gc = ls->GetGC();
|
||||
ls->Add(argWrapper);
|
||||
gc->Watch(argWrapper);
|
||||
return argWrapper;
|
||||
}
|
||||
TObject TArgWrapper::Call(GCList& ls,std::vector<TObject> args)
|
||||
{
|
||||
auto cse = current_function;
|
||||
TList* argList = TList::Create(ls);
|
||||
argList->items = args;
|
||||
TObject v=this->callable->Call(ls,{argList});
|
||||
current_function = cse;
|
||||
return v;
|
||||
}
|
||||
void TArgWrapper::Mark()
|
||||
{
|
||||
if(this->marked) return;
|
||||
this->marked = true;
|
||||
this->callable->Mark();
|
||||
}
|
||||
void TClosure::Mark()
|
||||
{
|
||||
if(this->marked) return;
|
||||
this->marked=true;
|
||||
this->file->Mark();
|
||||
this->env->Mark();
|
||||
this->closure->Mark();
|
||||
|
||||
}
|
||||
TClosure* TClosure::Create(GCList& ls,TEnvironment* env,TFile* file,uint32_t chunkId,bool ownScope)
|
||||
{
|
||||
TClosure* closure = new TClosure();
|
||||
closure->ownScope=ownScope;
|
||||
GC* _gc = ls.GetGC();
|
||||
ls.Add(closure);
|
||||
_gc->Watch(closure);
|
||||
closure->chunkId = chunkId;
|
||||
if(chunkId < file->chunks.size())
|
||||
closure->closure = file->chunks[chunkId];
|
||||
else throw VMException("ChunkId out of bounds.");
|
||||
closure->env = env;
|
||||
closure->file = file;
|
||||
|
||||
return closure;
|
||||
}
|
||||
TClosure* TClosure::Create(GCList* ls,TEnvironment* env,TFile* file,uint32_t chunkId,bool ownScope)
|
||||
{
|
||||
TClosure* closure = new TClosure();
|
||||
closure->ownScope=ownScope;
|
||||
GC* _gc = ls->GetGC();
|
||||
ls->Add(closure);
|
||||
_gc->Watch(closure);
|
||||
closure->chunkId = chunkId;
|
||||
if(chunkId < file->chunks.size())
|
||||
closure->closure = file->chunks[chunkId];
|
||||
else throw VMException("ChunkId out of bounds.");
|
||||
closure->env = env;
|
||||
closure->file = file;
|
||||
|
||||
return closure;
|
||||
}
|
||||
|
||||
TObject TClosure::Call(GCList& ls,std::vector<TObject> args)
|
||||
{
|
||||
auto cse = current_function;
|
||||
InterperterThread* thrd=InterperterThread::Create(ls);
|
||||
thrd->AddCallStackEntry(ls,this, args);
|
||||
|
||||
thrd->Execute(ls.GetGC());
|
||||
|
||||
TObject v= thrd->call_stack_entries[0]->Pop(ls);
|
||||
current_function = cse;
|
||||
return v;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
namespace Tesses::CrossLang {
|
||||
TObject TDictionary::CallMethod(GCList& ls, std::string key, std::vector<TObject> args)
|
||||
{
|
||||
ls.GetGC()->BarrierBegin();
|
||||
auto res = this->GetValue(key);
|
||||
ls.GetGC()->BarrierEnd();
|
||||
TCallable* callable;
|
||||
|
||||
if(GetObjectHeap(res,callable))
|
||||
{
|
||||
auto closure = dynamic_cast<TClosure*>(callable);
|
||||
if(closure != nullptr && !closure->closure->args.empty() && closure->closure->args.front() == "this")
|
||||
{
|
||||
std::vector<TObject> args2;
|
||||
args2.push_back(this);
|
||||
args2.insert(args2.end(), args.begin(),args.end());
|
||||
return closure->Call(ls,args2);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return callable->Call(ls,args);
|
||||
|
||||
}
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
void TDictionary::DeclareFunction(GC* gc,std::string key,std::string documentation, std::vector<std::string> argNames, std::function<TObject(GCList& ls, std::vector<TObject> args)> cb)
|
||||
{
|
||||
gc->BarrierBegin();
|
||||
GCList ls(gc);
|
||||
this->SetValue(key, TExternalMethod::Create(ls,documentation,argNames,cb));
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
void TDictionary::DeclareFunction(GC& gc,std::string key,std::string documentation, std::vector<std::string> argNames, std::function<TObject(GCList& ls, std::vector<TObject> args)> cb)
|
||||
{
|
||||
gc.BarrierBegin();
|
||||
GCList ls(gc);
|
||||
this->SetValue(key, TExternalMethod::Create(ls,documentation,argNames,cb));
|
||||
gc.BarrierEnd();
|
||||
}
|
||||
void TDictionary::DeclareFunction(GC* gc,std::string key,std::string documentation, std::vector<std::string> argNames, std::function<TObject(GCList& ls, std::vector<TObject> args)> cb,std::function<void()> destroy)
|
||||
{
|
||||
gc->BarrierBegin();
|
||||
GCList ls(gc);
|
||||
this->SetValue(key, TExternalMethod::Create(ls,documentation,argNames,cb,destroy));
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
void TDictionary::DeclareFunction(GC& gc,std::string key,std::string documentation, std::vector<std::string> argNames, std::function<TObject(GCList& ls, std::vector<TObject> args)> cb,std::function<void()> destroy)
|
||||
{
|
||||
gc.BarrierBegin();
|
||||
GCList ls(gc);
|
||||
this->SetValue(key, TExternalMethod::Create(ls,documentation,argNames,cb,destroy));
|
||||
gc.BarrierEnd();
|
||||
}
|
||||
TObject TDictionary::GetValue(std::string key)
|
||||
{
|
||||
if(this->items.empty()) return Undefined();
|
||||
for(auto item : this->items)
|
||||
{
|
||||
if(item.first == key) return item.second;
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
void TDictionary::SetValue(std::string key, TObject value)
|
||||
{
|
||||
if(std::holds_alternative<Undefined>(value))
|
||||
{
|
||||
if(this->items.contains(key))
|
||||
this->items.erase(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->items[key] = value;
|
||||
}
|
||||
}
|
||||
bool TDictionary::HasValue(std::string key)
|
||||
{
|
||||
return this->items.contains(key);
|
||||
}
|
||||
void TDictionary::Mark()
|
||||
{
|
||||
if(this->marked) return;
|
||||
this->marked = true;
|
||||
for(auto item : this->items)
|
||||
{
|
||||
GC::Mark(item.second);
|
||||
}
|
||||
}
|
||||
TDictionary* TDictionary::Create(GCList* gc)
|
||||
{
|
||||
TDictionary* dict=new TDictionary();
|
||||
GC* _gc = gc->GetGC();
|
||||
gc->Add(dict);
|
||||
_gc->Watch(dict);
|
||||
return dict;
|
||||
}
|
||||
TDictionary* TDictionary::Create(GCList& gc)
|
||||
{
|
||||
TDictionary* dict=new TDictionary();
|
||||
GC* _gc = gc.GetGC();
|
||||
gc.Add(dict);
|
||||
_gc->Watch(dict);
|
||||
return dict;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,54 @@
|
|||
#include "CrossLang.hpp"
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
TExternalMethod::TExternalMethod(std::function<TObject(GCList& ls, std::vector<TObject> args)> cb,std::string documentation, std::vector<std::string> argNames,std::function<void()> destroy)
|
||||
{
|
||||
|
||||
this->cb = cb;
|
||||
this->args = argNames;
|
||||
this->documentation = documentation;
|
||||
this->destroy = destroy;
|
||||
}
|
||||
TExternalMethod* TExternalMethod::Create(GCList& ls,std::string documentation,std::vector<std::string> argNames,std::function<TObject(GCList& ls, std::vector<TObject> args)> cb,std::function<void()> destroy)
|
||||
{
|
||||
auto gc = ls.GetGC();
|
||||
TExternalMethod* method = new TExternalMethod(cb,documentation,argNames,destroy);
|
||||
ls.Add(method);
|
||||
gc->Watch(method);
|
||||
return method;
|
||||
}
|
||||
TExternalMethod* TExternalMethod::Create(GCList* ls,std::string documentation, std::vector<std::string> argNames,std::function<TObject(GCList& ls, std::vector<TObject> args)> cb,std::function<void()> destroy)
|
||||
{
|
||||
auto gc = ls->GetGC();
|
||||
TExternalMethod* method = new TExternalMethod(cb,documentation,argNames,destroy);
|
||||
ls->Add(method);
|
||||
gc->Watch(method);
|
||||
return method;
|
||||
}
|
||||
TExternalMethod* TExternalMethod::Create(GCList& ls,std::string documentation, std::vector<std::string> argNames,std::function<TObject(GCList& ls, std::vector<TObject> args)> cb)
|
||||
{
|
||||
auto gc = ls.GetGC();
|
||||
TExternalMethod* method = new TExternalMethod(cb,documentation,argNames,[]()->void{});
|
||||
ls.Add(method);
|
||||
gc->Watch(method);
|
||||
return method;
|
||||
}
|
||||
TExternalMethod* TExternalMethod::Create(GCList* ls,std::string documentation, std::vector<std::string> argNames,std::function<TObject(GCList& ls, std::vector<TObject> args)> cb)
|
||||
{
|
||||
auto gc = ls->GetGC();
|
||||
TExternalMethod* method = new TExternalMethod(cb,documentation,argNames,[]()->void{});
|
||||
ls->Add(method);
|
||||
gc->Watch(method);
|
||||
return method;
|
||||
}
|
||||
TObject TExternalMethod::Call(GCList& ls, std::vector<TObject> args)
|
||||
{
|
||||
if(cb == nullptr) return Undefined();
|
||||
return this->cb(ls,args);
|
||||
}
|
||||
TExternalMethod::~TExternalMethod()
|
||||
{
|
||||
if(this->destroy != nullptr)
|
||||
this->destroy();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,253 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
|
||||
bool TCustomEnumerator::MoveNext(GC* ls)
|
||||
{
|
||||
GCList ls2(ls);
|
||||
auto res = this->dict->CallMethod(ls2,"MoveNext",{});
|
||||
bool out;
|
||||
if(GetObject(res,out)) return out;
|
||||
return false;
|
||||
}
|
||||
TObject TCustomEnumerator::GetCurrent(GCList& ls)
|
||||
{
|
||||
TObject res=Undefined();
|
||||
ls.GetGC()->BarrierBegin();
|
||||
auto getCurrent = this->dict->GetValue("getCurrent");
|
||||
TCallable* call;
|
||||
if(GetObjectHeap(getCurrent,call))
|
||||
{
|
||||
ls.GetGC()->BarrierEnd();
|
||||
res=call->Call(ls,{});
|
||||
ls.GetGC()->BarrierBegin();
|
||||
}else{
|
||||
|
||||
res=this->dict->GetValue("Current");
|
||||
}
|
||||
ls.GetGC()->BarrierEnd();
|
||||
return res;
|
||||
}
|
||||
void TCustomEnumerator::Mark()
|
||||
{
|
||||
if(this->marked) return;
|
||||
this->dict->Mark();
|
||||
}
|
||||
TCustomEnumerator* TCustomEnumerator::Create(GCList* ls, TDictionary* dict)
|
||||
{
|
||||
TCustomEnumerator* customEnum = new TCustomEnumerator();
|
||||
customEnum->dict = dict;
|
||||
GC* _gc = ls->GetGC();
|
||||
ls->Add(customEnum);
|
||||
_gc->Watch(customEnum);
|
||||
return customEnum;
|
||||
}
|
||||
TCustomEnumerator* TCustomEnumerator::Create(GCList& ls, TDictionary* dict)
|
||||
{
|
||||
TCustomEnumerator* customEnum = new TCustomEnumerator();
|
||||
customEnum->dict = dict;
|
||||
GC* _gc = ls.GetGC();
|
||||
ls.Add(customEnum);
|
||||
_gc->Watch(customEnum);
|
||||
return customEnum;
|
||||
}
|
||||
TEnumerator* TEnumerator::CreateFromObject(GCList& ls, TObject obj)
|
||||
{
|
||||
std::string str;
|
||||
TList* mls;
|
||||
TDictionary* dict;
|
||||
TEnumerator* enumerator;
|
||||
if(GetObject(obj,str))
|
||||
{
|
||||
return TStringEnumerator::Create(ls, str);
|
||||
}
|
||||
else if(GetObjectHeap(obj,mls))
|
||||
{
|
||||
return TListEnumerator::Create(ls,mls);
|
||||
}
|
||||
else if(GetObjectHeap(obj,dict))
|
||||
{
|
||||
auto res=dict->CallMethod(ls,"GetEnumerator",{});
|
||||
if(GetObjectHeap(res,dict))
|
||||
{
|
||||
return TCustomEnumerator::Create(ls,dict);
|
||||
}
|
||||
else if(GetObjectHeap(res,enumerator))
|
||||
{
|
||||
return enumerator;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
TVFSPathEnumerator* TVFSPathEnumerator::Create(GCList& ls, Tesses::Framework::Filesystem::VFSPathEnumerator enumerator)
|
||||
{
|
||||
TVFSPathEnumerator* vfspathe = new TVFSPathEnumerator();
|
||||
vfspathe->enumerator = enumerator;
|
||||
GC* _gc = ls.GetGC();
|
||||
ls.Add(vfspathe);
|
||||
_gc->Watch(vfspathe);
|
||||
return vfspathe;
|
||||
}
|
||||
TVFSPathEnumerator* TVFSPathEnumerator::Create(GCList* ls, Tesses::Framework::Filesystem::VFSPathEnumerator enumerator)
|
||||
{
|
||||
TVFSPathEnumerator* vfspathe = new TVFSPathEnumerator();
|
||||
vfspathe->enumerator = enumerator;
|
||||
GC* _gc = ls->GetGC();
|
||||
ls->Add(vfspathe);
|
||||
_gc->Watch(vfspathe);
|
||||
return vfspathe;
|
||||
}
|
||||
bool TVFSPathEnumerator::MoveNext(GC* ls)
|
||||
{
|
||||
return enumerator.MoveNext();
|
||||
}
|
||||
TObject TVFSPathEnumerator::GetCurrent(GCList& ls)
|
||||
{
|
||||
return enumerator.Current;
|
||||
}
|
||||
TDictionaryEnumerator* TDictionaryEnumerator::Create(GCList& ls, TDictionary* dict)
|
||||
{
|
||||
TDictionaryEnumerator* dicte=new TDictionaryEnumerator();
|
||||
dicte->dict = dict;
|
||||
dicte->hasStarted=false;
|
||||
GC* _gc = ls.GetGC();
|
||||
ls.Add(dicte);
|
||||
_gc->Watch(dicte);
|
||||
return dicte;
|
||||
}
|
||||
TDictionaryEnumerator* TDictionaryEnumerator::Create(GCList* ls, TDictionary* dict)
|
||||
{
|
||||
TDictionaryEnumerator* dicte=new TDictionaryEnumerator();
|
||||
dicte->dict = dict;
|
||||
dicte->hasStarted=false;
|
||||
GC* _gc = ls->GetGC();
|
||||
ls->Add(dicte);
|
||||
_gc->Watch(dicte);
|
||||
return dicte;
|
||||
}
|
||||
|
||||
bool TDictionaryEnumerator::MoveNext(GC* ls)
|
||||
{
|
||||
if(!this->hasStarted)
|
||||
{
|
||||
this->hasStarted=true;
|
||||
this->ittr = this->dict->items.begin();
|
||||
return !this->dict->items.empty();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->ittr++;
|
||||
return this->ittr != this->dict->items.end();
|
||||
}
|
||||
}
|
||||
TObject TDictionaryEnumerator::GetCurrent(GCList& ls)
|
||||
{
|
||||
if(!this->hasStarted) return Undefined();
|
||||
if(this->ittr != this->dict->items.end())
|
||||
{
|
||||
ls.GetGC()->BarrierBegin();
|
||||
std::string key = this->ittr->first;
|
||||
TObject value = this->ittr->second;
|
||||
auto kvp = TDictionary::Create(ls);
|
||||
kvp->SetValue("Key",key);
|
||||
kvp->SetValue("Value",value);
|
||||
ls.GetGC()->BarrierEnd();
|
||||
return kvp;
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
void TDictionaryEnumerator::Mark()
|
||||
{
|
||||
if(this->marked) return;
|
||||
this->marked=true;
|
||||
this->dict->Mark();
|
||||
}
|
||||
|
||||
|
||||
TListEnumerator* TListEnumerator::Create(GCList& ls, TList* list)
|
||||
{
|
||||
TListEnumerator* liste=new TListEnumerator();
|
||||
liste->ls = list;
|
||||
liste->index = -1;
|
||||
GC* _gc = ls.GetGC();
|
||||
ls.Add(liste);
|
||||
_gc->Watch(liste);
|
||||
return liste;
|
||||
}
|
||||
TListEnumerator* TListEnumerator::Create(GCList* ls, TList* list)
|
||||
{
|
||||
TListEnumerator* liste=new TListEnumerator();
|
||||
liste->ls = list;
|
||||
liste->index = -1;
|
||||
GC* _gc = ls->GetGC();
|
||||
ls->Add(liste);
|
||||
_gc->Watch(liste);
|
||||
return liste;
|
||||
}
|
||||
bool TListEnumerator::MoveNext(GC* ls)
|
||||
{
|
||||
this->index++;
|
||||
return this->index >= 0 && this->index < this->ls->Count();
|
||||
}
|
||||
TObject TListEnumerator::GetCurrent(GCList& ls)
|
||||
{
|
||||
|
||||
if(this->index < -1) return nullptr;
|
||||
if(this->ls->Count() == 0) return nullptr;
|
||||
if(this->index >= this->ls->Count()) return nullptr;
|
||||
ls.GetGC()->BarrierBegin();
|
||||
TObject o = this->ls->Get(index);
|
||||
ls.GetGC()->BarrierEnd();
|
||||
return o;
|
||||
}
|
||||
void TListEnumerator::Mark()
|
||||
{
|
||||
if(this->marked) return;
|
||||
this->marked = true;
|
||||
this->ls->Mark();
|
||||
}
|
||||
|
||||
TStringEnumerator* TStringEnumerator::Create(GCList& ls,std::string str)
|
||||
{
|
||||
TStringEnumerator* stre=new TStringEnumerator();
|
||||
stre->str = str;
|
||||
stre->hasStarted=false;
|
||||
GC* _gc = ls.GetGC();
|
||||
ls.Add(stre);
|
||||
_gc->Watch(stre);
|
||||
return stre;
|
||||
}
|
||||
TStringEnumerator* TStringEnumerator::Create(GCList* ls,std::string str)
|
||||
{
|
||||
TStringEnumerator* stre=new TStringEnumerator();
|
||||
stre->str = str;
|
||||
stre->hasStarted=false;
|
||||
GC* _gc = ls->GetGC();
|
||||
ls->Add(stre);
|
||||
_gc->Watch(stre);
|
||||
return stre;
|
||||
}
|
||||
bool TStringEnumerator::MoveNext(GC* ls)
|
||||
{
|
||||
if(!this->hasStarted)
|
||||
{
|
||||
this->hasStarted=true;
|
||||
this->index = 0;
|
||||
return !this->str.empty();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(this->index >= this->str.size()) return false;
|
||||
this->index++;
|
||||
return this->index < this->str.size();
|
||||
}
|
||||
}
|
||||
TObject TStringEnumerator::GetCurrent(GCList& ls)
|
||||
{
|
||||
if(!this->hasStarted) return nullptr;
|
||||
if(this->index < this->str.size()) return this->str[this->index];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
};
|
|
@ -0,0 +1,86 @@
|
|||
#include "CrossLang.hpp"
|
||||
namespace Tesses::CrossLang {
|
||||
TByteArray* TByteArray::Create(GCList& ls)
|
||||
{
|
||||
TByteArray* arr=new TByteArray();
|
||||
GC* _gc = ls.GetGC();
|
||||
ls.Add(arr);
|
||||
_gc->Watch(arr);
|
||||
return arr;
|
||||
}
|
||||
|
||||
TByteArray* TByteArray::Create(GCList* ls)
|
||||
{
|
||||
TByteArray* arr=new TByteArray();
|
||||
GC* _gc = ls->GetGC();
|
||||
ls->Add(arr);
|
||||
_gc->Watch(arr);
|
||||
return arr;
|
||||
}
|
||||
TList* TList::Create(GCList* gc)
|
||||
{
|
||||
TList* list=new TList();
|
||||
GC* _gc = gc->GetGC();
|
||||
gc->Add(list);
|
||||
_gc->Watch(list);
|
||||
return list;
|
||||
}
|
||||
TList* TList::Create(GCList& gc)
|
||||
{
|
||||
TList* list=new TList();
|
||||
GC* _gc = gc.GetGC();
|
||||
gc.Add(list);
|
||||
_gc->Watch(list);
|
||||
return list;
|
||||
}
|
||||
void TList::Add(TObject value)
|
||||
{
|
||||
this->items.push_back(value);
|
||||
}
|
||||
void TList::Set(int64_t index, TObject value)
|
||||
{
|
||||
if(index >= 0 && index < this->Count())
|
||||
{
|
||||
this->items[index] = value;
|
||||
}
|
||||
}
|
||||
TObject TList::Get(int64_t index)
|
||||
{
|
||||
if(index >= 0 && index < this->Count())
|
||||
{
|
||||
return this->items[index];
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
int64_t TList::Count()
|
||||
{
|
||||
return (int64_t)this->items.size();
|
||||
}
|
||||
void TList::Insert(int64_t index, TObject value)
|
||||
{
|
||||
if(index >= 0 && index <= this->Count())
|
||||
{
|
||||
this->items.insert(this->items.begin()+index,value);
|
||||
}
|
||||
}
|
||||
void TList::RemoveAt(int64_t index)
|
||||
{
|
||||
if(index >= 0 && index < this->Count())
|
||||
{
|
||||
this->items.erase(this->items.begin()+index);
|
||||
}
|
||||
}
|
||||
void TList::Clear()
|
||||
{
|
||||
this->items.clear();
|
||||
}
|
||||
void TList::Mark()
|
||||
{
|
||||
if(this->marked) return;
|
||||
this->marked = true;
|
||||
for(auto item : this->items)
|
||||
{
|
||||
GC::Mark(item);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,58 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
TNative::TNative(void* ptr,std::function<void(void*)> destroy)
|
||||
{
|
||||
this->ptr = ptr;
|
||||
this->destroyed=false;
|
||||
this->destroy = destroy;
|
||||
|
||||
}
|
||||
bool TNative::GetDestroyed()
|
||||
{
|
||||
return this->destroyed;
|
||||
}
|
||||
void* TNative::GetPointer()
|
||||
{
|
||||
return this->ptr;
|
||||
}
|
||||
void TNative::Mark()
|
||||
{
|
||||
if(this->marked) return;
|
||||
this->marked=true;
|
||||
|
||||
GC::Mark(this->other);
|
||||
|
||||
}
|
||||
void TNative::Destroy()
|
||||
{
|
||||
if(this->destroyed) return;
|
||||
if(this->destroy != nullptr)
|
||||
{
|
||||
this->destroyed=true;
|
||||
this->destroy(this->ptr);
|
||||
}
|
||||
}
|
||||
TNative* TNative::Create(GCList& ls, void* ptr,std::function<void(void*)> destroy)
|
||||
{
|
||||
TNative* native = new TNative(ptr,destroy);
|
||||
GC* gc = ls.GetGC();
|
||||
ls.Add(native);
|
||||
gc->Watch(native);
|
||||
return native;
|
||||
}
|
||||
TNative* TNative::Create(GCList* ls, void* ptr,std::function<void(void*)> destroy)
|
||||
{
|
||||
TNative* native = new TNative(ptr,destroy);
|
||||
GC* gc = ls->GetGC();
|
||||
ls->Add(native);
|
||||
gc->Watch(native);
|
||||
return native;
|
||||
}
|
||||
TNative::~TNative()
|
||||
{
|
||||
this->Destroy();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
namespace Tesses::CrossLang {
|
||||
void TRootEnvironment::LoadDependency(GC* gc,Tesses::Framework::Filesystem::VFS* vfs, std::pair<std::string,TVMVersion> dep)
|
||||
{
|
||||
for(auto item : this->dependencies)
|
||||
if(item.first == dep.first && item.second.CompareTo(dep.second) >= 0) return;
|
||||
std::string name = {};
|
||||
name.append(dep.first);
|
||||
name.push_back('-');
|
||||
name.append(dep.second.ToString());
|
||||
name.append(".crvm");
|
||||
std::string filename="/" + name;
|
||||
|
||||
|
||||
Tesses::Framework::Streams::Stream* file;
|
||||
|
||||
if(vfs->RegularFileExists(filename) && (file = vfs->OpenFile(filename,"rb")) != nullptr)
|
||||
{
|
||||
|
||||
GCList ls(gc);
|
||||
TFile* f = TFile::Create(ls);
|
||||
f->Load(gc, file);
|
||||
LoadFileWithDependencies(gc, vfs, f);
|
||||
}
|
||||
else throw VMException("Could not open file: \"" + name + "\".");
|
||||
}
|
||||
TObject TEnvironment::Eval(GCList& ls,std::string code)
|
||||
{
|
||||
std::stringstream strm(code);
|
||||
std::vector<LexToken> tokens;
|
||||
int res =Lex("eval.tcross",strm,tokens);
|
||||
if(res != 0)
|
||||
{
|
||||
throw VMException("Lex error at line: " + std::to_string(res));
|
||||
}
|
||||
Parser parser(tokens);
|
||||
SyntaxNode n = parser.ParseRoot();
|
||||
CodeGen gen;
|
||||
gen.GenRoot(n);
|
||||
Tesses::Framework::Streams::MemoryStream ms(true);
|
||||
gen.Save(nullptr, &ms);
|
||||
ms.Seek(0,Tesses::Framework::Streams::SeekOrigin::Begin);
|
||||
TFile* f = TFile::Create(ls);
|
||||
f->Load(ls.GetGC(),&ms);
|
||||
return this->LoadFile(ls.GetGC(), f);
|
||||
}
|
||||
TObject TEnvironment::LoadFile(GC* gc, TFile* file)
|
||||
{
|
||||
for(auto fn : file->functions)
|
||||
{
|
||||
|
||||
std::string name;
|
||||
|
||||
|
||||
if(fn.first.size() < 2) throw VMException("No function name.");
|
||||
|
||||
std::vector<std::string> items(fn.first.begin()+1, fn.first.end());
|
||||
|
||||
if(fn.second >= file->chunks.size()) throw VMException("ChunkId out of bounds.");
|
||||
TFileChunk* chunk = file->chunks[fn.second];
|
||||
|
||||
|
||||
if(items.size() == 0)
|
||||
throw VMException("Function name can't be empty.");
|
||||
|
||||
else if(items.size() == 1)
|
||||
{
|
||||
GCList ls(gc);
|
||||
TClosure* closure=TClosure::Create(ls,this,file,fn.second);
|
||||
closure->documentation = fn.first[0];
|
||||
gc->BarrierBegin();
|
||||
this->DeclareVariable(items[0],closure);
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
else
|
||||
{
|
||||
GCList ls(gc);
|
||||
TClosure* closure=TClosure::Create(ls,this,file,fn.second);
|
||||
closure->documentation = fn.first[0];
|
||||
TObject v = this->GetVariable(items[0]);
|
||||
TDictionary* dict=nullptr;
|
||||
if(std::holds_alternative<THeapObjectHolder>(v))
|
||||
{
|
||||
dict=dynamic_cast<TDictionary*>(std::get<THeapObjectHolder>(v).obj);
|
||||
if(dict == nullptr)
|
||||
{
|
||||
dict = TDictionary::Create(ls);
|
||||
gc->BarrierBegin();
|
||||
this->SetVariable(items[0],dict);
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dict = TDictionary::Create(ls);
|
||||
gc->BarrierBegin();
|
||||
this->DeclareVariable(items[0],dict);
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
|
||||
for(size_t i = 1; i < items.size()-1; i++)
|
||||
{
|
||||
gc->BarrierBegin();
|
||||
auto v = dict->GetValue(items[i]);
|
||||
gc->BarrierEnd();
|
||||
if(std::holds_alternative<THeapObjectHolder>(v))
|
||||
{
|
||||
auto dict2=dynamic_cast<TDictionary*>(std::get<THeapObjectHolder>(v).obj);
|
||||
if(dict2 == nullptr)
|
||||
{
|
||||
dict2 = TDictionary::Create(ls);
|
||||
gc->BarrierBegin();
|
||||
dict->SetValue(items[i],dict2);
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
dict = dict2;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto dict2 = TDictionary::Create(ls);
|
||||
gc->BarrierBegin();
|
||||
dict->SetValue(items[i],dict2);
|
||||
gc->BarrierEnd();
|
||||
dict = dict2;
|
||||
}
|
||||
}
|
||||
gc->BarrierBegin();
|
||||
dict->SetValue(items[items.size()-1],closure);
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
}
|
||||
if(!file->chunks.empty())
|
||||
{
|
||||
GCList ls(gc);
|
||||
TClosure* closure=TClosure::Create(ls,this,file,0);
|
||||
return closure->Call(ls,{});
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void TRootEnvironment::LoadFileWithDependencies(GC* gc,Tesses::Framework::Filesystem::VFS* vfs, TFile* file)
|
||||
{
|
||||
this->dependencies.push_back(std::pair<std::string,TVMVersion>(file->name,file->version));
|
||||
for(auto item : file->dependencies)
|
||||
{
|
||||
LoadDependency(gc,vfs,item);
|
||||
}
|
||||
LoadFile(gc, file);
|
||||
|
||||
}
|
||||
void TRootEnvironment::LoadFileWithDependencies(GC* gc,Tesses::Framework::Filesystem::VFS* vfs, Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
Tesses::Framework::Streams::Stream* file;
|
||||
|
||||
if(vfs->RegularFileExists(path) && (file = vfs->OpenFile(path,"rb")) != nullptr)
|
||||
{
|
||||
|
||||
GCList ls(gc);
|
||||
TFile* f = TFile::Create(ls);
|
||||
f->Load(gc, file);
|
||||
Tesses::Framework::Filesystem::SubdirFilesystem dir(vfs,path.GetParent(),false);
|
||||
LoadFileWithDependencies(gc,vfs,f);
|
||||
}
|
||||
else throw VMException("Could not open file: \"" + path.GetFileName() + "\".");
|
||||
|
||||
}
|
||||
TDictionary* TRootEnvironment::GetDictionary()
|
||||
{
|
||||
return this->dict;
|
||||
}
|
||||
TObject TRootEnvironment::GetVariable(std::string key)
|
||||
{
|
||||
return this->dict->GetValue(key);
|
||||
}
|
||||
void TRootEnvironment::SetVariable(std::string key, TObject value)
|
||||
{
|
||||
return this->dict->SetValue(key,value);
|
||||
}
|
||||
void TRootEnvironment::DeclareVariable(std::string key, TObject value)
|
||||
{
|
||||
return this->dict->SetValue(key,value);
|
||||
}
|
||||
bool TRootEnvironment::HasVariable(std::string key)
|
||||
{
|
||||
return this->dict->HasValue(key);
|
||||
}
|
||||
bool TRootEnvironment::HasVariableRecurse(std::string key)
|
||||
{
|
||||
return this->dict->HasValue(key);
|
||||
}
|
||||
TEnvironment* TRootEnvironment::GetParentEnvironment()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
TRootEnvironment* TRootEnvironment::GetRootEnvironment()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
TRootEnvironment::TRootEnvironment(TDictionary* dict)
|
||||
{
|
||||
this->dict = dict;
|
||||
}
|
||||
|
||||
void TRootEnvironment::Mark()
|
||||
{
|
||||
if(this->marked) return;
|
||||
this->marked = true;
|
||||
this->dict->Mark();
|
||||
for(auto defer : this->defers) defer->Mark();
|
||||
}
|
||||
TRootEnvironment* TRootEnvironment::Create(GCList* gc,TDictionary* dict)
|
||||
{
|
||||
TRootEnvironment* env=new TRootEnvironment(dict);
|
||||
GC* _gc = gc->GetGC();
|
||||
gc->Add(env);
|
||||
_gc->Watch(env);
|
||||
return env;
|
||||
}
|
||||
TRootEnvironment* TRootEnvironment::Create(GCList& gc,TDictionary* dict)
|
||||
{
|
||||
TRootEnvironment* env=new TRootEnvironment(dict);
|
||||
GC* _gc = gc.GetGC();
|
||||
gc.Add(env);
|
||||
_gc->Watch(env);
|
||||
return env;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,281 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
int64_t TObjectStream::_GetPosInternal()
|
||||
{
|
||||
return Tesses::Framework::Streams::Stream::GetLength();
|
||||
}
|
||||
TObjectStream::TObjectStream(GC* gc, TObject obj)
|
||||
{
|
||||
this->ls = new GCList(gc);
|
||||
this->ls->Add(obj);
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(obj,dict))
|
||||
{
|
||||
gc->BarrierBegin();
|
||||
if(!dict->HasValue("Read"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"Read","Read from stream",{"buff","off","len"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
return (int64_t)0;
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("Write"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"Write","Write to stream",{"buff","off","len"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
return (int64_t)0;
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("getCanRead"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"getCanRead","Can read from stream",{}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
if(!dict->HasValue("getCanWrite"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"getCanWrite","Can write to stream",{}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("getCanSeek"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"getCanSeek","Can seek in stream",{}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("getPosition"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"getPosition","Can get position in stream",{}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
return (int64_t)0;
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("getLength"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"getPosition","Can get position in stream",{}, [this](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
return _GetPosInternal();
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("getEndOfStream"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"getEndOfStream","Is at end of stream",{}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
}
|
||||
bool TObjectStream::EndOfStream()
|
||||
{
|
||||
TDictionary* dict;
|
||||
TStreamHeapObject* strm;
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
auto res = dict->CallMethod(*ls, "getEndOfStream",{});
|
||||
bool r;
|
||||
if(GetObject(res,r)) return r;
|
||||
}
|
||||
else if(GetObjectHeap(this->obj, strm))
|
||||
{
|
||||
return strm->stream->EndOfStream();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
size_t TObjectStream::Read(uint8_t* buff, size_t sz)
|
||||
{
|
||||
TDictionary* dict;
|
||||
TStreamHeapObject* strm;
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls2(this->ls->GetGC());
|
||||
|
||||
TByteArray* arr=TByteArray::Create(ls2);
|
||||
arr->data.resize(sz);
|
||||
|
||||
auto res = dict->CallMethod(ls2, "Read",{arr, (int64_t)0L, (int64_t)sz});
|
||||
memcpy(buff,arr->data.data(),std::min(sz,arr->data.size()));
|
||||
int64_t r;
|
||||
if(GetObject(res,r)) return r;
|
||||
}
|
||||
else if(GetObjectHeap(this->obj, strm))
|
||||
{
|
||||
return strm->stream->Read(buff,sz);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
size_t TObjectStream::Write(const uint8_t* buff, size_t sz)
|
||||
{
|
||||
TDictionary* dict;
|
||||
TStreamHeapObject* strm;
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls2(this->ls->GetGC());
|
||||
|
||||
TByteArray* arr=TByteArray::Create(ls2);
|
||||
arr->data.resize(sz);
|
||||
memcpy(arr->data.data(), buff, sz);
|
||||
|
||||
auto res = dict->CallMethod(ls2, "Write",{arr, (int64_t)0L, (int64_t)sz});
|
||||
int64_t r;
|
||||
if(GetObject(res,r)) return r;
|
||||
}
|
||||
else if(GetObjectHeap(this->obj, strm))
|
||||
{
|
||||
return strm->stream->Write(buff,sz);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
bool TObjectStream::CanRead()
|
||||
{
|
||||
|
||||
TDictionary* dict;
|
||||
TStreamHeapObject* strm;
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
auto res = dict->CallMethod(*ls, "getCanRead",{});
|
||||
bool r;
|
||||
if(GetObject(res,r)) return r;
|
||||
}
|
||||
else if(GetObjectHeap(this->obj, strm))
|
||||
{
|
||||
return strm->stream->CanRead();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool TObjectStream::CanWrite()
|
||||
{
|
||||
|
||||
TDictionary* dict;
|
||||
TStreamHeapObject* strm;
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
auto res = dict->CallMethod(*ls, "getCanWrite",{});
|
||||
bool r;
|
||||
if(GetObject(res,r)) return r;
|
||||
}
|
||||
else if(GetObjectHeap(this->obj, strm))
|
||||
{
|
||||
return strm->stream->CanWrite();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool TObjectStream::CanSeek()
|
||||
{
|
||||
|
||||
TDictionary* dict;
|
||||
TStreamHeapObject* strm;
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
auto res = dict->CallMethod(*ls, "getCanSeek",{});
|
||||
bool r;
|
||||
if(GetObject(res,r)) return r;
|
||||
}
|
||||
else if(GetObjectHeap(this->obj, strm))
|
||||
{
|
||||
return strm->stream->CanSeek();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int64_t TObjectStream::GetPosition()
|
||||
{
|
||||
|
||||
TDictionary* dict;
|
||||
TStreamHeapObject* strm;
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
auto res = dict->CallMethod(*ls, "getPosition",{});
|
||||
int64_t r;
|
||||
if(GetObject(res,r)) return r;
|
||||
}
|
||||
else if(GetObjectHeap(this->obj, strm))
|
||||
{
|
||||
return strm->stream->GetPosition();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int64_t TObjectStream::GetLength()
|
||||
{
|
||||
|
||||
TDictionary* dict;
|
||||
TStreamHeapObject* strm;
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
auto res = dict->CallMethod(*ls, "getEndOfStream",{});
|
||||
bool r;
|
||||
if(GetObject(res,r)) return r;
|
||||
}
|
||||
else if(GetObjectHeap(this->obj, strm))
|
||||
{
|
||||
return strm->stream->GetLength();
|
||||
}
|
||||
return Tesses::Framework::Streams::Stream::GetLength();
|
||||
}
|
||||
void TObjectStream::Flush()
|
||||
{
|
||||
|
||||
TDictionary* dict;
|
||||
TStreamHeapObject* strm;
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
dict->CallMethod(*ls, "Flush",{});
|
||||
|
||||
}
|
||||
else if(GetObjectHeap(this->obj, strm))
|
||||
{
|
||||
strm->stream->Flush();
|
||||
}
|
||||
}
|
||||
void TObjectStream::Seek(int64_t pos, Tesses::Framework::Streams::SeekOrigin whence)
|
||||
{
|
||||
TDictionary* dict;
|
||||
TStreamHeapObject* strm;
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
dict->CallMethod(*ls, "Seek",{pos,(int64_t)(whence == Tesses::Framework::Streams::SeekOrigin::Begin ? 0 : whence == Tesses::Framework::Streams::SeekOrigin::Current ? 1 : 2) });
|
||||
}
|
||||
else if(GetObjectHeap(this->obj, strm))
|
||||
{
|
||||
strm->stream->Seek(pos,whence);
|
||||
}
|
||||
}
|
||||
TObjectStream::~TObjectStream()
|
||||
{
|
||||
delete this->ls;
|
||||
}
|
||||
|
||||
|
||||
TStreamHeapObject* TStreamHeapObject::Create(GCList& ls, Tesses::Framework::Streams::Stream* strm)
|
||||
{
|
||||
TStreamHeapObject* heapObj = new TStreamHeapObject();
|
||||
GC* _gc = ls.GetGC();
|
||||
ls.Add(heapObj);
|
||||
_gc->Watch(heapObj);
|
||||
heapObj->stream = strm;
|
||||
return heapObj;
|
||||
}
|
||||
TStreamHeapObject* TStreamHeapObject::Create(GCList* ls, Tesses::Framework::Streams::Stream* strm)
|
||||
{
|
||||
TStreamHeapObject* heapObj = new TStreamHeapObject();
|
||||
GC* _gc = ls->GetGC();
|
||||
ls->Add(heapObj);
|
||||
_gc->Watch(heapObj);
|
||||
heapObj->stream = strm;
|
||||
return heapObj;
|
||||
}
|
||||
TStreamHeapObject::~TStreamHeapObject()
|
||||
{
|
||||
if(this->stream != nullptr)
|
||||
{
|
||||
delete this->stream;
|
||||
}
|
||||
}
|
||||
void TStreamHeapObject::Close()
|
||||
{
|
||||
if(this->stream != nullptr)
|
||||
{
|
||||
delete this->stream;
|
||||
this->stream = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
namespace Tesses::CrossLang {
|
||||
TObject TSubEnvironment::GetVariable(std::string key)
|
||||
{
|
||||
if(this->dict->HasValue(key))
|
||||
{
|
||||
return this->dict->GetValue(key);
|
||||
}
|
||||
if(this->env->HasVariableRecurse(key))
|
||||
{
|
||||
return this->env->GetVariable(key);
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
void TSubEnvironment::DeclareVariable(std::string key,TObject value)
|
||||
{
|
||||
this->dict->SetValue(key,value);
|
||||
}
|
||||
void TSubEnvironment::SetVariable(std::string key, TObject value)
|
||||
{
|
||||
if(this->dict->HasValue(key))
|
||||
{
|
||||
this->dict->SetValue(key,value);
|
||||
return;
|
||||
}
|
||||
if(this->env->HasVariableRecurse(key))
|
||||
{
|
||||
this->env->SetVariable(key,value);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->dict->SetValue(key,value);
|
||||
}
|
||||
}
|
||||
bool TSubEnvironment::HasVariable(std::string key)
|
||||
{
|
||||
return this->dict->HasValue(key);
|
||||
}
|
||||
bool TSubEnvironment::HasVariableRecurse(std::string key)
|
||||
{
|
||||
if(this->dict->HasValue(key)) return true;
|
||||
return this->env->HasVariableRecurse(key);
|
||||
}
|
||||
TSubEnvironment::TSubEnvironment(TEnvironment* env,TDictionary* dict)
|
||||
{
|
||||
this->env = env;
|
||||
this->dict = dict;
|
||||
}
|
||||
TSubEnvironment* TEnvironment::GetSubEnvironment(TDictionary* dict)
|
||||
{
|
||||
TSubEnvironment* subEnv = new TSubEnvironment(this,dict);
|
||||
return subEnv;
|
||||
}
|
||||
TObject TEnvironment::CallFunction(GCList& ls, std::string key, std::vector<TObject> args)
|
||||
{
|
||||
ls.GetGC()->BarrierBegin();
|
||||
auto res = this->GetVariable(key);
|
||||
ls.GetGC()->BarrierEnd();
|
||||
TCallable* callable;
|
||||
|
||||
if(GetObjectHeap(res,callable))
|
||||
{
|
||||
return callable->Call(ls,args);
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
TSubEnvironment* TEnvironment::GetSubEnvironment(GCList* gc)
|
||||
{
|
||||
auto dict=TDictionary::Create(gc);
|
||||
TSubEnvironment* sEnv = this->GetSubEnvironment(dict);
|
||||
GC* _gc = gc->GetGC();
|
||||
gc->Add(sEnv);
|
||||
_gc->Watch(sEnv);
|
||||
return sEnv;
|
||||
}
|
||||
TSubEnvironment* TEnvironment::GetSubEnvironment(GCList& gc)
|
||||
{
|
||||
auto dict=TDictionary::Create(gc);
|
||||
TSubEnvironment* sEnv = this->GetSubEnvironment(dict);
|
||||
GC* _gc = gc.GetGC();
|
||||
gc.Add(sEnv);
|
||||
_gc->Watch(sEnv);
|
||||
return sEnv;
|
||||
}
|
||||
TSubEnvironment* TSubEnvironment::Create(GCList* gc, TEnvironment* env, TDictionary* dict)
|
||||
{
|
||||
TSubEnvironment* senv = new TSubEnvironment(env,dict);
|
||||
GC* _gc = gc->GetGC();
|
||||
gc->Add(senv);
|
||||
_gc->Watch(senv);
|
||||
return senv;
|
||||
}
|
||||
TSubEnvironment* TSubEnvironment::Create(GCList& gc, TEnvironment* env, TDictionary* dict)
|
||||
{
|
||||
TSubEnvironment* senv = new TSubEnvironment(env,dict);
|
||||
GC* _gc = gc.GetGC();
|
||||
gc.Add(senv);
|
||||
_gc->Watch(senv);
|
||||
return senv;
|
||||
}
|
||||
TEnvironment* TSubEnvironment::GetParentEnvironment()
|
||||
{
|
||||
return this->env;
|
||||
}
|
||||
TRootEnvironment* TSubEnvironment::GetRootEnvironment()
|
||||
{
|
||||
return this->env->GetRootEnvironment();
|
||||
}
|
||||
|
||||
void TSubEnvironment::Mark()
|
||||
{
|
||||
if(this->marked) return;
|
||||
this->marked=true;
|
||||
this->dict->Mark();
|
||||
this->env->Mark();
|
||||
for(auto defer : defers) defer->Mark();
|
||||
}
|
||||
void TEnvironment::DeclareFunction(GC* gc,std::string key,std::string documentation, std::vector<std::string> argNames, std::function<TObject(GCList& ls, std::vector<TObject> args)> cb)
|
||||
{
|
||||
gc->BarrierBegin();
|
||||
GCList ls(gc);
|
||||
this->DeclareVariable(key, TExternalMethod::Create(ls,documentation,argNames,cb));
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
void TEnvironment::DeclareFunction(GC& gc,std::string key,std::string documentation, std::vector<std::string> argNames, std::function<TObject(GCList& ls, std::vector<TObject> args)> cb)
|
||||
{
|
||||
gc.BarrierBegin();
|
||||
GCList ls(gc);
|
||||
this->DeclareVariable(key, TExternalMethod::Create(ls,documentation,argNames,cb));
|
||||
gc.BarrierEnd();
|
||||
}
|
||||
void TEnvironment::DeclareFunction(GC* gc,std::string key,std::string documentation, std::vector<std::string> argNames, std::function<TObject(GCList& ls, std::vector<TObject> args)> cb,std::function<void()> destroy)
|
||||
{
|
||||
gc->BarrierBegin();
|
||||
GCList ls(gc);
|
||||
this->DeclareVariable(key, TExternalMethod::Create(ls,documentation,argNames,cb,destroy));
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
void TEnvironment::DeclareFunction(GC& gc,std::string key,std::string documentation, std::vector<std::string> argNames, std::function<TObject(GCList& ls, std::vector<TObject> args)> cb,std::function<void()> destroy)
|
||||
{
|
||||
gc.BarrierBegin();
|
||||
GCList ls(gc);
|
||||
this->DeclareVariable(key, TExternalMethod::Create(ls,documentation,argNames,cb,destroy));
|
||||
gc.BarrierEnd();
|
||||
}
|
||||
};
|
|
@ -0,0 +1,626 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
namespace Tesses::CrossLang {
|
||||
|
||||
TObjectVFS::TObjectVFS(GC* gc, TObject obj)
|
||||
{
|
||||
this->ls = new GCList(gc);
|
||||
this->ls->Add(obj);
|
||||
this->obj = obj;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(obj,dict))
|
||||
{
|
||||
gc->BarrierBegin();
|
||||
if(!dict->HasValue("OpenFile"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"OpenFile","Open a file",{"path","mode"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
return nullptr;
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("EnumeratePaths"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"EnumeratePaths","Enumerate paths",{"path"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
return TVFSPathEnumerator::Create(ls,Tesses::Framework::Filesystem::VFSPathEnumerator());
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("ReadLink"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"ReadLink","Read a symlink",{"path"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
return Tesses::Framework::Filesystem::VFSPath();
|
||||
});
|
||||
}
|
||||
|
||||
if(!dict->HasValue("VFSPathToSystem"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"VFSPathToSystem","Convert path to system",{"path"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
Tesses::Framework::Filesystem::VFSPath path;
|
||||
if(GetArgumentAsPath(args,0,path))
|
||||
{
|
||||
return path.ToString();
|
||||
}
|
||||
return "/";
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("SystemToVFSPath"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"SystemToVFSPath","Convert system to path",{"path"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
std::string p;
|
||||
if(GetArgument(args,0,p))
|
||||
{
|
||||
return Tesses::Framework::Filesystem::VFSPath(p);
|
||||
}
|
||||
return Tesses::Framework::Filesystem::VFSPath();
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("GetDate"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"GetDate","Get date from file",{"path"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
auto dict = TDictionary::Create(ls);
|
||||
ls.GetGC()->BarrierBegin();
|
||||
dict->SetValue("LastWrite", (int64_t)time(NULL));
|
||||
dict->SetValue("LastAccess", (int64_t)time(NULL));
|
||||
ls.GetGC()->BarrierEnd();
|
||||
return dict;
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("RegularFileExists"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"RegularFileExists","Regular file exists",{"path"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("SymlinkExists"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"SymlinkExists","Symlink exists",{"path"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("CharacterDeviceExists"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"CharacterDeviceExists","Character file exists",{"path"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("BlockDeviceExists"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"BlockDeviceExists","Block file exists",{"path"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("SocketFileExists"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"SocketFileExists","Socket file exists",{"path"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
if(!dict->HasValue("FIFOFileExists"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"FIFOFileExists","FIFO file exists",{"path"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("FileExists"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"FileExists","File exists",{"path"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("SpecialFileExists"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"SpecialFileExists","Special file exists",{"path"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
if(!dict->HasValue("DirectoryExists"))
|
||||
{
|
||||
dict->DeclareFunction(gc,"DirectoryExists","Directory exists",{"path"}, [](GCList& ls, std::vector<TObject> args)->TObject {
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
|
||||
}
|
||||
Tesses::Framework::Streams::Stream* TObjectVFS::OpenFile(Tesses::Framework::Filesystem::VFSPath path, std::string mode)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
return vfs->vfs->OpenFile(path,mode);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
auto res = dict->CallMethod(ls, "OpenFile",{path,mode});
|
||||
TStreamHeapObject* strm;
|
||||
if(GetObjectHeap(res,strm))
|
||||
{
|
||||
return new TObjectStream(this->ls->GetGC(), strm);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void TObjectVFS::CreateDirectory(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
vfs->vfs->CreateDirectory(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
dict->CallMethod(ls, "CreateDirectory",{path});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
void TObjectVFS::DeleteDirectory(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
vfs->vfs->DeleteDirectory(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
dict->CallMethod(ls, "DeleteDirectory",{path});
|
||||
|
||||
}
|
||||
}
|
||||
bool TObjectVFS::RegularFileExists(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
return vfs->vfs->RegularFileExists(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
auto res = dict->CallMethod(ls, "RegularFileExists",{path});
|
||||
bool out;
|
||||
if(GetObject(res,out))
|
||||
{
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool TObjectVFS::SymlinkExists(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
return vfs->vfs->SymlinkExists(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
auto res = dict->CallMethod(ls, "SymlinkExists",{path});
|
||||
bool out;
|
||||
if(GetObject(res,out))
|
||||
{
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool TObjectVFS::CharacterDeviceExists(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
return vfs->vfs->CharacterDeviceExists(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
auto res = dict->CallMethod(ls, "CharacterDeviceExists",{path});
|
||||
bool out;
|
||||
if(GetObject(res,out))
|
||||
{
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool TObjectVFS::BlockDeviceExists(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
return vfs->vfs->BlockDeviceExists(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
auto res = dict->CallMethod(ls, "BlockDeviceExists",{path});
|
||||
bool out;
|
||||
if(GetObject(res,out))
|
||||
{
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool TObjectVFS::SocketFileExists(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
return vfs->vfs->SocketFileExists(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
auto res = dict->CallMethod(ls, "SocketFileExists",{path});
|
||||
bool out;
|
||||
if(GetObject(res,out))
|
||||
{
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool TObjectVFS::FIFOFileExists(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
return vfs->vfs->FIFOFileExists(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
auto res = dict->CallMethod(ls, "FIFOFileExists",{path});
|
||||
bool out;
|
||||
if(GetObject(res,out))
|
||||
{
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool TObjectVFS::FileExists(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
return vfs->vfs->FileExists(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
auto res = dict->CallMethod(ls, "FileExists",{path});
|
||||
bool out;
|
||||
if(GetObject(res,out))
|
||||
{
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool TObjectVFS::SpecialFileExists(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
return vfs->vfs->SpecialFileExists(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
auto res = dict->CallMethod(ls, "SpecialFileExists",{path});
|
||||
bool out;
|
||||
if(GetObject(res,out))
|
||||
{
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
void TObjectVFS::CreateSymlink(Tesses::Framework::Filesystem::VFSPath existingFile, Tesses::Framework::Filesystem::VFSPath symlinkFile)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
vfs->vfs->CreateSymlink(existingFile,symlinkFile);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
dict->CallMethod(ls, "CreateSymlink",{existingFile,symlinkFile});
|
||||
|
||||
}
|
||||
}
|
||||
void TObjectVFS::CreateHardlink(Tesses::Framework::Filesystem::VFSPath existingFile, Tesses::Framework::Filesystem::VFSPath newName)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
vfs->vfs->CreateHardlink(existingFile,newName);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
dict->CallMethod(ls, "CreateHardlink",{existingFile,newName});
|
||||
|
||||
}
|
||||
}
|
||||
bool TObjectVFS::DirectoryExists(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
return vfs->vfs->DirectoryExists(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
auto res = dict->CallMethod(ls, "DirectoryExists",{path});
|
||||
bool out;
|
||||
if(GetObject(res,out))
|
||||
{
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
void TObjectVFS::DeleteFile(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
vfs->vfs->DeleteFile(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
dict->CallMethod(ls, "DeleteFile",{path});
|
||||
|
||||
}
|
||||
}
|
||||
void TObjectVFS::DeleteDirectoryRecurse(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
vfs->vfs->DeleteDirectoryRecurse(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
dict->CallMethod(ls, "DeleteDirectoryRecurse",{path});
|
||||
|
||||
}
|
||||
}
|
||||
Tesses::Framework::Filesystem::VFSPathEnumerator TObjectVFS::EnumeratePaths(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
return vfs->vfs->EnumeratePaths(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList* ls=new GCList(this->ls->GetGC());
|
||||
auto res = dict->CallMethod(*ls, "EnumeratePaths",{path});
|
||||
auto enumerator = TEnumerator::CreateFromObject(*ls,res);
|
||||
return Tesses::Framework::Filesystem::VFSPathEnumerator([path,enumerator,ls](Tesses::Framework::Filesystem::VFSPath& path0)->bool{
|
||||
if(enumerator == nullptr) return false;
|
||||
while(enumerator->MoveNext(ls->GetGC()))
|
||||
{
|
||||
auto res = enumerator->GetCurrent(*ls);
|
||||
std::string name;
|
||||
Tesses::Framework::Filesystem::VFSPath path1;
|
||||
if(GetObject(res,path1))
|
||||
{
|
||||
path0 = path1;
|
||||
return true;
|
||||
}
|
||||
else if(GetObject(res,name))
|
||||
{
|
||||
path0 = path / name;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},[ls]()->void{
|
||||
delete ls;
|
||||
});
|
||||
}
|
||||
}
|
||||
void TObjectVFS::MoveFile(Tesses::Framework::Filesystem::VFSPath src, Tesses::Framework::Filesystem::VFSPath dest)
|
||||
{
|
||||
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
vfs->vfs->MoveFile(src,dest);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
dict->CallMethod(ls, "MoveFile",{src,dest});
|
||||
|
||||
}
|
||||
}
|
||||
void TObjectVFS::MoveDirectory(Tesses::Framework::Filesystem::VFSPath src, Tesses::Framework::Filesystem::VFSPath dest)
|
||||
{
|
||||
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
vfs->vfs->MoveDirectory(src,dest);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
dict->CallMethod(ls, "MoveDirectory",{src,dest});
|
||||
|
||||
}
|
||||
}
|
||||
Tesses::Framework::Filesystem::VFSPath TObjectVFS::ReadLink(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
return vfs->vfs->ReadLink(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
auto res = dict->CallMethod(ls, "ReadLink",{path});
|
||||
Tesses::Framework::Filesystem::VFSPath myPath;
|
||||
if(GetObject(res,myPath))
|
||||
{
|
||||
return myPath;
|
||||
}
|
||||
}
|
||||
return Tesses::Framework::Filesystem::VFSPath();
|
||||
}
|
||||
std::string TObjectVFS::VFSPathToSystem(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
return vfs->vfs->VFSPathToSystem(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
auto res = dict->CallMethod(ls, "VFSPathToSystem",{path});
|
||||
std::string myPath;
|
||||
if(GetObject(res,myPath))
|
||||
{
|
||||
return myPath;
|
||||
}
|
||||
}
|
||||
return "/";
|
||||
}
|
||||
Tesses::Framework::Filesystem::VFSPath TObjectVFS::SystemToVFSPath(std::string path)
|
||||
{
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
return vfs->vfs->SystemToVFSPath(path);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
auto res = dict->CallMethod(ls, "SystemToVFSPath",{path});
|
||||
Tesses::Framework::Filesystem::VFSPath myPath;
|
||||
if(GetObject(res,myPath))
|
||||
{
|
||||
return myPath;
|
||||
}
|
||||
}
|
||||
return Tesses::Framework::Filesystem::VFSPath();
|
||||
}
|
||||
void TObjectVFS::GetDate(Tesses::Framework::Filesystem::VFSPath path, time_t& lastWrite, time_t& lastAccess)
|
||||
{
|
||||
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
vfs->vfs->GetDate(path,lastWrite,lastAccess);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
auto res = dict->CallMethod(ls, "GetDate",{path});
|
||||
if(GetObjectHeap(res,dict))
|
||||
{
|
||||
this->ls->GetGC()->BarrierBegin();
|
||||
res = dict->GetValue("LastWrite");
|
||||
int64_t v;
|
||||
if(GetObject(res,v)) lastWrite=(time_t)v;
|
||||
|
||||
res = dict->GetValue("LastAccess");
|
||||
if(GetObject(res,v)) lastAccess=(time_t)v;
|
||||
|
||||
this->ls->GetGC()->BarrierEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
void TObjectVFS::SetDate(Tesses::Framework::Filesystem::VFSPath path, time_t lastWrite, time_t lastAccess)
|
||||
{
|
||||
|
||||
TVFSHeapObject* vfs;
|
||||
TDictionary* dict;
|
||||
if(GetObjectHeap(this->obj, vfs))
|
||||
{
|
||||
vfs->vfs->SetDate(path,lastWrite,lastAccess);
|
||||
}
|
||||
if(GetObjectHeap(this->obj, dict))
|
||||
{
|
||||
GCList ls(this->ls->GetGC());
|
||||
dict->CallMethod(ls, "SetDate",{path,(int64_t)lastWrite,(int64_t)lastAccess});
|
||||
|
||||
}
|
||||
}
|
||||
TObjectVFS::~TObjectVFS()
|
||||
{
|
||||
delete this->ls;
|
||||
}
|
||||
TVFSHeapObject* TVFSHeapObject::Create(GCList& ls, Tesses::Framework::Filesystem::VFS* vfs)
|
||||
{
|
||||
TVFSHeapObject* heapObj = new TVFSHeapObject();
|
||||
GC* _gc = ls.GetGC();
|
||||
ls.Add(heapObj);
|
||||
_gc->Watch(heapObj);
|
||||
heapObj->vfs = vfs;
|
||||
return heapObj;
|
||||
}
|
||||
TVFSHeapObject* TVFSHeapObject::Create(GCList* ls, Tesses::Framework::Filesystem::VFS* vfs)
|
||||
{
|
||||
TVFSHeapObject* heapObj = new TVFSHeapObject();
|
||||
GC* _gc = ls->GetGC();
|
||||
ls->Add(heapObj);
|
||||
_gc->Watch(heapObj);
|
||||
heapObj->vfs = vfs;
|
||||
return heapObj;
|
||||
}
|
||||
TVFSHeapObject::~TVFSHeapObject()
|
||||
{
|
||||
if(this->vfs != nullptr)
|
||||
delete this->vfs;
|
||||
}
|
||||
void TVFSHeapObject::Close()
|
||||
{
|
||||
if(this->vfs != nullptr)
|
||||
{
|
||||
delete this->vfs;
|
||||
this->vfs = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
|
||||
TFile* TFile::Create(GCList& ls)
|
||||
{
|
||||
TFile* f = new TFile();
|
||||
GC* _gc = ls.GetGC();
|
||||
ls.Add(f);
|
||||
_gc->Watch(f);
|
||||
return f;
|
||||
}
|
||||
TFile* TFile::Create(GCList* ls)
|
||||
{
|
||||
TFile* f = new TFile();
|
||||
GC* _gc = ls->GetGC();
|
||||
ls->Add(f);
|
||||
_gc->Watch(f);
|
||||
return f;
|
||||
}
|
||||
void TFileChunk::Mark()
|
||||
{
|
||||
if(this->marked) return;
|
||||
this->marked=true;
|
||||
this->file->Mark();
|
||||
}
|
||||
|
||||
TFileChunk* TFileChunk::Create(GCList& ls)
|
||||
{
|
||||
TFileChunk* chk = new TFileChunk();
|
||||
GC* _gc = ls.GetGC();
|
||||
ls.Add(chk);
|
||||
_gc->Watch(chk);
|
||||
return chk;
|
||||
}
|
||||
TFileChunk* TFileChunk::Create(GCList* ls)
|
||||
{
|
||||
TFileChunk* chk = new TFileChunk();
|
||||
GC* _gc = ls->GetGC();
|
||||
ls->Add(chk);
|
||||
_gc->Watch(chk);
|
||||
return chk;
|
||||
}
|
||||
void TFile::Mark()
|
||||
{
|
||||
if(this->marked) return;
|
||||
this->marked=true;
|
||||
|
||||
for(auto item : this->chunks)
|
||||
item->Mark();
|
||||
|
||||
}
|
||||
void TFile::Skip(Tesses::Framework::Streams::Stream* stream,size_t len)
|
||||
{
|
||||
if(stream->CanSeek())
|
||||
{
|
||||
stream->Seek((int64_t)len,Tesses::Framework::Streams::SeekOrigin::Current);
|
||||
}
|
||||
else{
|
||||
uint8_t* buffer=new uint8_t[len];
|
||||
Ensure(stream,buffer,len);
|
||||
delete buffer;
|
||||
}
|
||||
}
|
||||
void TFile::Ensure(Tesses::Framework::Streams::Stream* stream, uint8_t* buffer,size_t len)
|
||||
{
|
||||
size_t read = stream->ReadBlock(buffer, len);
|
||||
if(read < len) throw VMException("End of file, could not read " + std::to_string((int64_t)len) + " byte(s)., offset=" + std::to_string(stream->GetLength()));
|
||||
}
|
||||
std::string TFile::EnsureString(Tesses::Framework::Streams::Stream* stream)
|
||||
{
|
||||
auto len = EnsureInt(stream);
|
||||
if(len == 0) return {};
|
||||
std::string str={};
|
||||
str.resize((size_t)len);
|
||||
Ensure(stream,(uint8_t*)str.data(),str.size());
|
||||
return str;
|
||||
}
|
||||
|
||||
uint32_t TFile::EnsureInt(Tesses::Framework::Streams::Stream* stream)
|
||||
{
|
||||
uint8_t buffer[4];
|
||||
Ensure(stream,buffer,4);
|
||||
return BitConverter::ToUint32BE(buffer[0]);
|
||||
}
|
||||
std::string TFile::GetString(Tesses::Framework::Streams::Stream* stream)
|
||||
{
|
||||
uint32_t index=EnsureInt(stream);
|
||||
if(index > this->strings.size()) throw VMException("String does not exist in TCrossVM file, expected string index: " + std::to_string(index) + ", total strings: " + std::to_string(this->strings.size()));
|
||||
return this->strings[index];
|
||||
}
|
||||
|
||||
void TFile::Load(GC* gc, Tesses::Framework::Streams::Stream* stream)
|
||||
{
|
||||
GCList ls(gc);
|
||||
uint8_t main_header[18];
|
||||
Ensure(stream,main_header,sizeof(main_header));
|
||||
if(strncmp((const char*)main_header,"TCROSSVM",8) != 0) throw VMException("Invalid TCrossVM image.");
|
||||
TVMVersion version(main_header+8);
|
||||
if(version.CompareToRuntime() == 1)
|
||||
{
|
||||
throw VMException("Runtime is too old.");
|
||||
}
|
||||
TVMVersion v2(main_header+13);
|
||||
this->version = v2;
|
||||
|
||||
size_t _len = (size_t)EnsureInt(stream);
|
||||
|
||||
char table_name[4];
|
||||
|
||||
for(size_t i = 0;i < _len; i++)
|
||||
{
|
||||
Ensure(stream,(uint8_t*)table_name,sizeof(table_name));
|
||||
size_t tableLen = (size_t)EnsureInt(stream);
|
||||
if(strncmp(table_name,"NAME",4) == 0)
|
||||
{
|
||||
this->name = GetString(stream);
|
||||
}
|
||||
else if(strncmp(table_name,"INFO",4) == 0)
|
||||
{
|
||||
this->info = GetString(stream);
|
||||
}
|
||||
else if(strncmp(table_name,"DEPS",4) == 0) //dependencies
|
||||
{
|
||||
std::string name = GetString(stream);
|
||||
uint8_t version_bytes[5];
|
||||
Ensure(stream,version_bytes,sizeof(version_bytes));
|
||||
TVMVersion depVersion(version_bytes);
|
||||
this->dependencies.push_back(std::pair<std::string,TVMVersion>(name, depVersion));
|
||||
}
|
||||
else if(strncmp(table_name,"RESO",4) == 0) //resources (using embed)
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
data.resize(tableLen);
|
||||
Ensure(stream,data.data(), tableLen);
|
||||
this->resources.push_back(data);
|
||||
}
|
||||
else if(strncmp(table_name,"CHKS",4) == 0) //chunks
|
||||
{
|
||||
size_t chunkCount = (size_t)EnsureInt(stream);
|
||||
for(size_t j = 0; j < chunkCount; j++)
|
||||
{
|
||||
auto chunk = TFileChunk::Create(ls);
|
||||
chunk->file = this;
|
||||
size_t argCount = (size_t)EnsureInt(stream);
|
||||
for(size_t k = 0; k < argCount; k++)
|
||||
{
|
||||
chunk->args.push_back(GetString(stream));
|
||||
}
|
||||
size_t len = (size_t)EnsureInt(stream);
|
||||
chunk->code.resize(len);
|
||||
Ensure(stream,chunk->code.data(),len);
|
||||
//reader.ReadIntoBuffer(chunk->code);
|
||||
|
||||
this->chunks.push_back(chunk);
|
||||
}
|
||||
}
|
||||
else if(strncmp(table_name,"FUNS",4) == 0) //functions
|
||||
{
|
||||
size_t funLength = (size_t)EnsureInt(stream);
|
||||
|
||||
for(size_t j = 0; j < funLength;j++)
|
||||
{
|
||||
std::vector<std::string> fnParts;
|
||||
uint32_t fnPartsC = EnsureInt(stream);
|
||||
for(uint32_t k = 0; k < fnPartsC; k++)
|
||||
{
|
||||
fnParts.push_back(GetString(stream));
|
||||
}
|
||||
|
||||
uint32_t fnNumber = EnsureInt(stream);
|
||||
this->functions.push_back(std::pair<std::vector<std::string>,uint32_t>(fnParts,fnNumber));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else if(strncmp(table_name,"STRS",4) == 0) //strings
|
||||
{
|
||||
size_t strsLen = (size_t)EnsureInt(stream);
|
||||
for(size_t j = 0;j < strsLen;j++)
|
||||
{
|
||||
this->strings.push_back(EnsureString(stream));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Skip(stream,tableLen);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
#include "CrossLang.hpp"
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(CROSSLANG_ENABLE_SQLITE)
|
||||
#include "../sqlite/sqlite3.h"
|
||||
#if defined(GEKKO)
|
||||
extern sqlite3_vfs *sqlite3_demovfs(void);
|
||||
#endif
|
||||
#endif
|
||||
using namespace Tesses::Framework::Threading;
|
||||
using namespace std::chrono;
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
|
||||
bool GC::IsRunning()
|
||||
{
|
||||
|
||||
bool run = this->running;
|
||||
|
||||
return run;
|
||||
}
|
||||
GC::GC()
|
||||
{
|
||||
#if defined(CROSSLANG_ENABLE_SQLITE)
|
||||
sqlite3_initialize();
|
||||
#if defined(GEKKO)
|
||||
sqlite3_vfs_register(sqlite3_demovfs(),1);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
TDictionary* CreateThread(GCList& ls, TCallable* callable,bool detached)
|
||||
{
|
||||
|
||||
TDictionary* dict = TDictionary::Create(ls);
|
||||
|
||||
ThreadHandle* th = new ThreadHandle();
|
||||
th->gc = ls.GetGC();
|
||||
th->callable = callable;
|
||||
th->hasInit=false;
|
||||
th->hasReturned = false;
|
||||
th->detached=detached;
|
||||
ls.Add(th);
|
||||
ls.GetGC()->Watch(th);
|
||||
|
||||
ls.GetGC()->BarrierBegin();
|
||||
dict->SetValue("_internal", th);
|
||||
|
||||
dict->DeclareFunction(ls.GetGC(),"Join","Join thread",{},[th](GCList& _ls, std::vector<TObject> _args)-> TObject{
|
||||
|
||||
|
||||
th->thrd->Join();
|
||||
delete th->thrd;
|
||||
|
||||
if(th->hasReturned)
|
||||
{
|
||||
_ls.GetGC()->BarrierBegin();
|
||||
auto v = th->returnValue;
|
||||
_ls.Add(v);
|
||||
_ls.GetGC()->BarrierEnd();
|
||||
return v;
|
||||
}
|
||||
return Undefined();
|
||||
});
|
||||
dict->DeclareFunction(ls.GetGC(), "Detach","Detach thread",{},[th](GCList& _ls, std::vector<TObject> _args)-> TObject{
|
||||
_ls.GetGC()->BarrierBegin();
|
||||
th->detached=true;
|
||||
_ls.GetGC()->BarrierEnd();
|
||||
return Undefined();
|
||||
});
|
||||
|
||||
dict->DeclareFunction(ls.GetGC(),"getFinished","Get whether thread has finished",{},[th](GCList& _ls, std::vector<TObject> _args)-> TObject{
|
||||
return th->hasReturned;
|
||||
});
|
||||
|
||||
ls.GetGC()->BarrierEnd();
|
||||
th->thrd =new Thread([th]()->void {
|
||||
GC* gc=th->gc;
|
||||
GCList ls(gc);
|
||||
ls.Add(th);
|
||||
th->hasInit=true;
|
||||
TObject cb = th->callable->Call(ls,{});
|
||||
gc->BarrierBegin();
|
||||
th->returnValue=cb;
|
||||
gc->BarrierEnd();
|
||||
th->hasReturned=true;
|
||||
});
|
||||
while(!th->hasInit);
|
||||
return dict;
|
||||
}
|
||||
void GC::Start()
|
||||
{
|
||||
this->mtx=new Mutex();
|
||||
this->running = true;
|
||||
this->thrd = new Thread([this]()->void {
|
||||
std::chrono::time_point<std::chrono::system_clock> last_frame, this_frame;
|
||||
|
||||
this_frame = system_clock::now();
|
||||
last_frame = this_frame;
|
||||
|
||||
while(this->IsRunning())
|
||||
{
|
||||
this_frame = system_clock::now();
|
||||
if((this_frame - last_frame) > 10s)
|
||||
{
|
||||
last_frame = this_frame;
|
||||
this->Collect();
|
||||
usleep(1000000);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
GC::Collect();
|
||||
});
|
||||
}
|
||||
|
||||
bool GC::UsingNullThreads()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void GC::BarrierBegin()
|
||||
{
|
||||
this->mtx->Lock();
|
||||
}
|
||||
void GC::BarrierEnd()
|
||||
{
|
||||
this->mtx->Unlock();
|
||||
}
|
||||
void GC::Watch(TObject obj)
|
||||
{
|
||||
if(std::holds_alternative<THeapObjectHolder>(obj))
|
||||
{
|
||||
auto _item=std::get<THeapObjectHolder>(obj).obj;
|
||||
this->BarrierBegin();
|
||||
|
||||
for(auto item : this->objects)
|
||||
{
|
||||
if(item == _item) {
|
||||
this->BarrierEnd();
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->objects.push_back(_item);
|
||||
this->BarrierEnd();
|
||||
}
|
||||
}
|
||||
void GC::Mark(TObject obj)
|
||||
{
|
||||
if(std::holds_alternative<THeapObjectHolder>(obj))
|
||||
{
|
||||
auto _item=std::get<THeapObjectHolder>(obj).obj;
|
||||
_item->Mark();
|
||||
}
|
||||
}
|
||||
void GC::Unwatch(TObject obj)
|
||||
{
|
||||
if(std::holds_alternative<THeapObjectHolder>(obj))
|
||||
{
|
||||
auto _item=std::get<THeapObjectHolder>(obj).obj;
|
||||
this->BarrierBegin();
|
||||
for(auto index = this->objects.begin();index<this->objects.end();index++)
|
||||
{
|
||||
if(*index == _item)
|
||||
{
|
||||
this->objects.erase(index);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
this->BarrierEnd();
|
||||
}
|
||||
}
|
||||
void GC::SetRoot(TObject obj)
|
||||
{
|
||||
if(std::holds_alternative<THeapObjectHolder>(obj))
|
||||
{
|
||||
auto _item=std::get<THeapObjectHolder>(obj).obj;
|
||||
this->BarrierBegin();
|
||||
|
||||
for(auto item : this->roots)
|
||||
{
|
||||
if(item == _item) {
|
||||
this->BarrierEnd();
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->roots.push_back(_item);
|
||||
this->BarrierEnd();
|
||||
}
|
||||
}
|
||||
void GC::UnsetRoot(TObject obj)
|
||||
{
|
||||
if(std::holds_alternative<THeapObjectHolder>(obj))
|
||||
{
|
||||
auto _item=std::get<THeapObjectHolder>(obj).obj;
|
||||
this->BarrierBegin();
|
||||
for(auto index = this->roots.begin();index<this->roots.end();index++)
|
||||
{
|
||||
if(*index == _item)
|
||||
{
|
||||
this->roots.erase(index);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
this->BarrierEnd();
|
||||
}
|
||||
}
|
||||
|
||||
GC::~GC()
|
||||
{
|
||||
GC::BarrierBegin();
|
||||
this->roots.clear();
|
||||
GC::BarrierEnd();
|
||||
this->running=false;
|
||||
this->thrd->Join();
|
||||
delete this->thrd;
|
||||
for(auto item : objects) delete item;
|
||||
delete this->mtx;
|
||||
}
|
||||
void GC::Collect()
|
||||
{
|
||||
this->BarrierBegin();
|
||||
for(auto item : this->objects)
|
||||
{
|
||||
item->marked=false;
|
||||
}
|
||||
for(auto item : this->roots)
|
||||
{
|
||||
item->Mark();
|
||||
}
|
||||
for(auto index = this->objects.begin();index < this->objects.end();index++)
|
||||
{
|
||||
THeapObject* o = *index;
|
||||
if(!o->marked)
|
||||
{
|
||||
delete o;
|
||||
this->objects.erase(index);
|
||||
index--;
|
||||
}
|
||||
}
|
||||
this->BarrierEnd();
|
||||
|
||||
}
|
||||
};
|
|
@ -0,0 +1,73 @@
|
|||
#include "CrossLang.hpp"
|
||||
|
||||
namespace Tesses::CrossLang
|
||||
{
|
||||
GCList::GCList(GC* gc)
|
||||
{
|
||||
gc->BarrierBegin();
|
||||
this->gc = gc;
|
||||
gc->SetRoot(this);
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
GCList::GCList(GC& gc)
|
||||
{
|
||||
gc.BarrierBegin();
|
||||
this->gc = &gc;
|
||||
gc.SetRoot(this);
|
||||
gc.BarrierEnd();
|
||||
}
|
||||
GC* GCList::GetGC()
|
||||
{
|
||||
return this->gc;
|
||||
}
|
||||
void GCList::Remove(TObject obj)
|
||||
{
|
||||
if(std::holds_alternative<THeapObjectHolder>(obj))
|
||||
{
|
||||
auto _item=std::get<THeapObjectHolder>(obj).obj;
|
||||
this->gc->BarrierBegin();
|
||||
for(auto index = this->items.begin();index<this->items.end();index++)
|
||||
{
|
||||
if(*index == _item)
|
||||
{
|
||||
this->items.erase(index);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
this->gc->BarrierEnd();
|
||||
}
|
||||
}
|
||||
void GCList::Add(TObject obj)
|
||||
{
|
||||
|
||||
if(std::holds_alternative<THeapObjectHolder>(obj))
|
||||
{
|
||||
auto _item=std::get<THeapObjectHolder>(obj).obj;
|
||||
this->gc->BarrierBegin();
|
||||
|
||||
for(auto item : this->items)
|
||||
{
|
||||
if(item == _item) {
|
||||
this->gc->BarrierEnd();
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->items.push_back(_item);
|
||||
this->gc->BarrierEnd();
|
||||
}
|
||||
}
|
||||
void GCList::Mark()
|
||||
{
|
||||
this->marked=true;
|
||||
for(auto item : this->items)
|
||||
{
|
||||
item->Mark();
|
||||
}
|
||||
}
|
||||
GCList::~GCList()
|
||||
{
|
||||
gc->BarrierBegin();
|
||||
this->gc->UnsetRoot(this);
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue