Skip to content

Commit

Permalink
(stdlib) Add File.read_all_text() method
Browse files Browse the repository at this point in the history
...and use this method from the C# code for reading the Perlang program
being executed from disk. Doing it like this makes little "theoretical
sense" since it's much more cumbersome than just using the built-in C#
methods for this, *but* it means that we start using the new
C++/Perlang-based stdlib somewhere. In that sense, it's a small step
towards the effort of becoming self-hosted.

https://gitlab.perlang.org/perlang/perlang/-/merge_requests/527
  • Loading branch information
perlun committed Oct 12, 2024
1 parent 24307b9 commit 8e101ab
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 9 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ RELEASE_PERLANG=$(RELEASE_PERLANG_DIRECTORY)/perlang
# --undef-value-errors are caused by the .NET runtime, so we ignore them for now to avoid noise. Can also not use
# --leak-check=full because .NET has some existing Valgrind issues preventing this:
# https://github.com/dotnet/runtime/issues/52872
VALGRIND=COMPlus_GCHeapHardLimit=C800000 valgrind --undef-value-errors=no --error-exitcode=1 --show-leak-kinds=all
VALGRIND=DOTNET_GCHeapHardLimit=C800000 valgrind --undef-value-errors=no --error-exitcode=1 --show-leak-kinds=all

# Enable fail-fast in case of errors
SHELL=/bin/bash -e -o pipefail
Expand Down
4 changes: 4 additions & 0 deletions release-notes/v0.6.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
- Support `length` property for `string` arrays [[!505][505]]
- Add `enum` support [[!526][526]]

#### stdlib
- Add `File.read_all_text()` method [[!527][527]]

### Changed
##### General
- Upgrade to .NET 8 [[!513][513]]
Expand Down Expand Up @@ -92,6 +95,7 @@
[524]: https://gitlab.perlang.org/perlang/perlang/merge_requests/524
[525]: https://gitlab.perlang.org/perlang/perlang/merge_requests/525
[526]: https://gitlab.perlang.org/perlang/perlang/merge_requests/526
[527]: https://gitlab.perlang.org/perlang/perlang/merge_requests/527
[528]: https://gitlab.perlang.org/perlang/perlang/merge_requests/528
[529]: https://gitlab.perlang.org/perlang/perlang/merge_requests/529
[530]: https://gitlab.perlang.org/perlang/perlang/merge_requests/530
Expand Down
31 changes: 31 additions & 0 deletions src/Perlang.ConsoleApp/NativeFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#nullable enable
#pragma warning disable SA1300
#pragma warning disable SA1601
using System;
using System.Runtime.InteropServices;

namespace Perlang.ConsoleApp;

internal static partial class NativeFile
{
[LibraryImport("perlang_cli", EntryPoint = "File_read_all_text", StringMarshalling = StringMarshalling.Utf8)]
private static partial IntPtr _File_read_all_text(string path);

[LibraryImport("perlang_cli", EntryPoint = "File_read_all_text_free")]
private static partial void _File_read_all_text_free(IntPtr file_contents);

public static string read_all_text(string path)
{
IntPtr file_contents = IntPtr.Zero;

try {
file_contents = _File_read_all_text(path);
return Marshal.PtrToStringUTF8(file_contents)!;
}
finally {
if (file_contents != IntPtr.Zero) {
_File_read_all_text_free(file_contents);
}
}
}
}
6 changes: 2 additions & 4 deletions src/Perlang.ConsoleApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,7 @@ private int RunFile(string path, string? outputPath)
return (int)ExitCodes.FILE_NOT_FOUND;
}

byte[] bytes = File.ReadAllBytes(path);
string source = Encoding.UTF8.GetString(bytes);
string source = NativeFile.read_all_text(path);

CompileAndRun(source, path, outputPath, CompilerWarning);

Expand Down Expand Up @@ -303,8 +302,7 @@ private int CompileAndAssembleFile(string[] scriptFiles, string? targetPath, boo
return (int)ExitCodes.FILE_NOT_FOUND;
}

byte[] bytes = File.ReadAllBytes(scriptFile);
string source = Encoding.UTF8.GetString(bytes);
string source = NativeFile.read_all_text(scriptFile);

completeSource.Append(source);
completeSource.AppendLine();
Expand Down
1 change: 1 addition & 0 deletions src/perlang_cli/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ project(perlang_cli VERSION 0.1.0)
add_library(
perlang_cli SHARED
src/perlang_cli_preprocessed.cc
src/stdlib_wrappers.cc
)

# Enable C++ 17 support. This is required on e.g. AppleClang 13
Expand Down
25 changes: 25 additions & 0 deletions src/perlang_cli/src/stdlib_wrappers.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Wrappers on top of the Perlang standard library, to make it usable from C#
//
// These wrappers must take care of a few things:
//
// - Make C++ methods accessible from C# (by wrapping them in extern "C" functions)
// - Handle conversion from Perlang/C++ shared_ptr types to types that can be consumed from the C# side

#include <cstring>
#include <stdexcept>

#include "perlang_stdlib.h"

using namespace perlang;

extern "C" {
const char* File_read_all_text(const char* path) {
std::unique_ptr<const String> file_contents = perlang::io::File::read_all_text(*UTF8String::from_copied_string(path));
const char* result = strdup(file_contents->bytes());
return result;
}

void File_read_all_text_free(const char* s) {
free((void*) s);
}
}
20 changes: 17 additions & 3 deletions src/stdlib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,19 @@ set(headers
src/ascii_string.h
src/bigint.h
src/int_array.h
src/internal/string_utils.h
src/perlang_stdlib.h
src/perlang_string.h
src/string_array.h
src/utf8_string.h
src/double-conversion/utils.h

# src/double-conversion/utils.h
#
# src/internal/string_utils.h

)

set(io_headers
src/io/file.h
)

set(libtommath_headers
Expand All @@ -33,6 +40,8 @@ add_library(
src/string_array.cc
src/utf8_string.cc

src/io/file.cc

src/libtommath/bn_cutoffs.c
src/libtommath/bn_mp_add.c
src/libtommath/bn_mp_clamp.c
Expand Down Expand Up @@ -91,7 +100,7 @@ set_property(TARGET stdlib PROPERTY CXX_STANDARD 17)

target_include_directories(
stdlib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
)

target_compile_options(
Expand All @@ -117,6 +126,11 @@ install(
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)

install(
FILES ${io_headers}
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/io"
)

install(
FILES ${libtommath_headers}
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/libtommath"
Expand Down
52 changes: 52 additions & 0 deletions src/stdlib/src/io/file.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include <cstdio>
#include <stdexcept>

#include "io/file.h"
#include "utf8_string.h"

namespace perlang::io
{
[[nodiscard]]
std::unique_ptr<const String> File::read_all_text(const String& path)
{
FILE *file = fopen(path.bytes(), "r");

if (file == nullptr) {
return nullptr;
}

// Determine the size of the file
fseek(file, 0, SEEK_END);
long length = ftell(file);

if (length == -1) {
fclose(file);
return nullptr;
}

// Go back to the beginning and read the file into a buffer
fseek(file, 0, SEEK_SET);

std::unique_ptr<char[]> buffer = std::make_unique<char[]>(length + 1);

if (buffer == nullptr) {
fclose(file);
throw std::runtime_error("Failed to allocate memory when attempting to read file " + std::string(path.bytes()));
}

size_t read = fread(buffer.get(), 1, length, file);
fclose(file);

// Ensure that we read the entire file; if not, return an error to the caller
if (read != (size_t)length) {
throw std::runtime_error("Expected to read " + std::to_string(length) + " bytes, but only read " + std::to_string(read) + " bytes");
}

// Create a Perlang string based on the newly read data. Releasing the buffer afterwards is crucial to avoid
// double-free()ing the memory.
buffer[length] = '\0';
std::unique_ptr<const String> ptr = UTF8String::from_owned_string(buffer.release(), length);

return ptr;
}
}
16 changes: 16 additions & 0 deletions src/stdlib/src/io/file.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include <memory>

#include "perlang_string.h"

namespace perlang::io
{
class File
{
public:
// Reads a file from the given path and returns its contents as a string. The file is presumed to be encoded in
// UTF-8.
static std::unique_ptr<const String> read_all_text(const String &path);
};
}
2 changes: 2 additions & 0 deletions src/stdlib/src/perlang_stdlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include "string_array.h"
#include "utf8_string.h"

#include "io/file.h"

namespace perlang
{
namespace stdlib
Expand Down
3 changes: 2 additions & 1 deletion src/stdlib/src/perlang_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ namespace perlang
virtual ~String() = default;

// Returns the backing byte array for this String. This method is generally to be avoided; it is safer to use
// the String throughout the code and only call this when you really must.
// the String throughout the code and only call this when you really must. If you call it, you
// **MUST MUST MUST** not modify the data in any way, or use it after the lifetime of the String object.
[[nodiscard]]
virtual const char* bytes() const = 0;

Expand Down

0 comments on commit 8e101ab

Please sign in to comment.