Skip to content

Commit

Permalink
Add setenv primitive. (#1483)
Browse files Browse the repository at this point in the history
Also makes Unicode work for setenv and getenv on Windows.
  • Loading branch information
Erik Corry authored Mar 14, 2023
1 parent c11eb10 commit a5d7c9f
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 12 deletions.
1 change: 1 addition & 0 deletions src/compiler/propagation/type_primitive_core.cc
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ TYPE_PRIMITIVE_ANY(debug_set_memory_limit)
TYPE_PRIMITIVE_ANY(dump_heap)
TYPE_PRIMITIVE_ANY(serial_print_heap_report)
TYPE_PRIMITIVE_ANY(get_env)
TYPE_PRIMITIVE_NULL(set_env)
TYPE_PRIMITIVE_ANY(literal_index)
TYPE_PRIMITIVE_ANY(firmware_map)
TYPE_PRIMITIVE_ANY(firmware_unmap)
Expand Down
6 changes: 5 additions & 1 deletion src/os.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,11 @@ class OS {
static word get_heap_tag();
static void heap_summary_report(int max_pages, const char* marker);

static const char* getenv(const char* variable);
// Returns a malloced string.
static char* getenv(const char* variable);
// Returns true for OK.
static bool setenv(const char* variable, const char* value);
static bool unsetenv(const char* variable);

#ifdef TOIT_FREERTOS
static bool use_spiram_for_heap() { return use_spiram_for_heap_; }
Expand Down
12 changes: 11 additions & 1 deletion src/os_esp32.cc
Original file line number Diff line number Diff line change
Expand Up @@ -800,14 +800,24 @@ void OS::heap_summary_report(int max_pages, const char* marker) {}

#endif // def TOIT_CMPCTMALLOC

const char* OS::getenv(const char* variable) {
char* OS::getenv(const char* variable) {
// Unimplemented on purpose.
// We currently prefer not to expose environment variables on embedded devices.
// There is no technical reason for it, so if circumstances change, one can
// just add a call to `::getenv`.
UNIMPLEMENTED();
}

bool OS::setenv(const char* variable, const char* value) {
// Unimplemented on purpose.
UNIMPLEMENTED();
}

bool OS::unsetenv(const char* variable) {
// Unimplemented on purpose.
UNIMPLEMENTED();
}

bool OS::set_real_time(struct timespec* time) {
if (clock_settime(CLOCK_REALTIME, time) == 0) return true;
struct timeval timeofday{};
Expand Down
18 changes: 16 additions & 2 deletions src/os_posix.cc
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,22 @@ void OS::out_of_memory(const char* reason) {
abort();
}

const char* OS::getenv(const char* variable) {
return ::getenv(variable);
char* OS::getenv(const char* variable) {
// Getenv/setenv are not guaranteed to be reentrant.
Locker scope(global_mutex_);
char* result = ::getenv(variable);
if (result == null) return null;
return strdup(result);
}

bool OS::setenv(const char* variable, const char* value) {
Locker scope(global_mutex_);
return ::setenv(variable, value, 1) == 0;
}

bool OS::unsetenv(const char* variable) {
Locker scope(global_mutex_);
return ::unsetenv(variable) == 0;
}

bool OS::set_real_time(struct timespec* time) {
Expand Down
39 changes: 37 additions & 2 deletions src/os_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,43 @@ void OS::out_of_memory(const char* reason) {
abort();
}

const char* OS::getenv(const char* variable) {
return ::getenv(variable);
static wchar_t* malloced_wide_string(const char* string) {
word length = Utils::utf_8_to_16(unsigned_cast(string), strlen(string));
wchar_t* result = reinterpret_cast<wchar_t*>(malloc((length + 1) * sizeof(wchar_t)));
Utils::utf_8_to_16(unsigned_cast(string), strlen(string), result, length);
result[length] = '\0';
return result;
}

char* OS::getenv(const char* variable) {
wchar_t* wide_variable = malloced_wide_string(variable);

const int BUFFER_SIZE = 32767;
wchar_t buffer[BUFFER_SIZE];
int wide_length = GetEnvironmentVariableW(wide_variable, buffer, BUFFER_SIZE);
free(wide_variable);
if (wide_length == 0 || wide_length > BUFFER_SIZE) return null;
word size = Utils::utf_16_to_8(buffer, wide_length, null, 0);
char* result = unvoid_cast<char*>(malloc(size + 1));
Utils::utf_16_to_8(buffer, wide_length, unsigned_cast(result), size);
result[size] = '\0';
return result;
}

bool OS::setenv(const char* variable, const char* value) {
wchar_t* wide_variable = malloced_wide_string(variable);
wchar_t* wide_value = malloced_wide_string(value);
bool ok = SetEnvironmentVariableW(wide_variable, wide_value);
free(wide_variable);
free(wide_value);
return ok;
}

bool OS::unsetenv(const char* variable) {
wchar_t* wide_variable = malloced_wide_string(variable);
bool ok = SetEnvironmentVariableW(wide_variable, null);
free(wide_variable);
return ok;
}

bool OS::set_real_time(struct timespec* time) {
Expand Down
1 change: 1 addition & 0 deletions src/primitive.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ namespace toit {
PRIMITIVE(dump_heap, 1) \
PRIMITIVE(serial_print_heap_report, 2) \
PRIMITIVE(get_env, 1) \
PRIMITIVE(set_env, 2) \
PRIMITIVE(literal_index, 1) \
PRIMITIVE(word_size, 0) \
PRIMITIVE(firmware_map, 1) \
Expand Down
27 changes: 21 additions & 6 deletions src/primitive_core.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2244,7 +2244,7 @@ PRIMITIVE(dump_heap) {
#else
ARGS(int, padding);
if (padding < 0 || padding > 0x10000) OUT_OF_RANGE;
#if defined(TOIT_LINUX)
#ifdef TOIT_LINUX
if (heap_caps_iterate_tagged_memory_areas == null) {
// This always happens on the server unless we are running with
// cmpctmalloc (using LD_PRELOAD), which supports iterating the heap in
Expand Down Expand Up @@ -2290,16 +2290,31 @@ PRIMITIVE(serial_print_heap_report) {
}

PRIMITIVE(get_env) {
#if defined (TOIT_FREERTOS)
#ifdef TOIT_FREERTOS
// FreeRTOS supports environment variables, but we prefer not to expose them.
UNIMPLEMENTED_PRIMITIVE;
#else
ARGS(cstring, key);
// TODO(florian): getenv is not reentrant.
// We should have a lock around `getenv` and `setenv`.
const char* result = OS::getenv(key);
char* result = OS::getenv(key);
if (result == null) return process->program()->null_object();
return process->allocate_string_or_error(result, strlen(result));
Object* string_or_error = process->allocate_string_or_error(result, strlen(result));
free(result);
return string_or_error;
#endif
}

PRIMITIVE(set_env) {
#ifdef TOIT_FREERTOS
// FreeRTOS supports environment variables, but we prefer not to expose them.
UNIMPLEMENTED_PRIMITIVE;
#else
ARGS(cstring, key, cstring, value);
if (value) {
OS::setenv(key, value);
} else {
OS::unsetenv(key);
}
return process->program()->null_object();
#endif
}

Expand Down

0 comments on commit a5d7c9f

Please sign in to comment.