123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942 |
- /*++
- Copyright (c) 2015 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- vmsys.c
- Abstract:
- This module implements the default support functions needed to wire a
- Chalk interpreter up to the rest of the system in the default configuration.
- Author:
- Evan Green 28-May-2016
- Environment:
- C
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <errno.h>
- #include <limits.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include "chalkp.h"
- #include "vmsys.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // Define some default garbage collection parameters.
- //
- #define CK_INITIAL_HEAP_DEFAULT (1024 * 1024 * 10)
- #define CK_MINIMUM_HEAP_DEFAULT (1024 * 1024)
- #define CK_HEAP_GROWTH_DEFAULT 512
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- PVOID
- CkpDefaultReallocate (
- PVOID Allocation,
- UINTN NewSize
- );
- CK_LOAD_MODULE_RESULT
- CkpDefaultLoadModule (
- PCK_VM Vm,
- PCSTR ModulePath,
- PCK_MODULE_HANDLE ModuleData
- );
- INT
- CkpDefaultSaveModule (
- PCK_VM Vm,
- PCSTR ModulePath,
- PCSTR FrozenData,
- UINTN FrozenDataSize
- );
- VOID
- CkpDefaultUnloadForeignModule (
- PVOID Data
- );
- VOID
- CkpDefaultWrite (
- PCK_VM Vm,
- PCSTR String
- );
- VOID
- CkpDefaultError (
- PCK_VM Vm,
- CK_ERROR_TYPE ErrorType,
- PSTR Message
- );
- VOID
- CkpDefaultUnhandledException (
- PCK_VM Vm
- );
- CK_LOAD_MODULE_RESULT
- CkpLoadSourceFile (
- PCK_VM Vm,
- PCSTR Directory,
- PCSTR ModulePath,
- PCK_MODULE_HANDLE ModuleData
- );
- CK_LOAD_MODULE_RESULT
- CkpReadSource (
- PCK_VM Vm,
- PCSTR ModulePath,
- UINTN PathLength,
- FILE *File,
- UINTN Size,
- PCK_MODULE_HANDLE ModuleData
- );
- CK_LOAD_MODULE_RESULT
- CkpLoadDynamicModule (
- PCK_VM Vm,
- PCSTR Directory,
- PCSTR ModulePath,
- PCK_MODULE_HANDLE ModuleData
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- CK_CONFIGURATION CkDefaultConfiguration = {
- CkpDefaultReallocate,
- CkpDefaultLoadModule,
- CkpDefaultSaveModule,
- CkpDefaultUnloadForeignModule,
- CkpDefaultWrite,
- CkpDefaultError,
- CkpDefaultUnhandledException,
- CK_INITIAL_HEAP_DEFAULT,
- CK_MINIMUM_HEAP_DEFAULT,
- CK_HEAP_GROWTH_DEFAULT,
- 0
- };
- //
- // ------------------------------------------------------------------ Functions
- //
- PVOID
- CkpDefaultReallocate (
- PVOID Allocation,
- UINTN NewSize
- )
- /*++
- Routine Description:
- This routine contains the default Chalk reallocate routine, which wires up
- to the C library realloc function.
- Arguments:
- Allocation - Supplies an optional pointer to the allocation to resize or
- free. If NULL, then this routine will allocate new memory.
- NewSize - Supplies the size of the desired allocation. If this is 0 and the
- allocation parameter is non-null, the given allocation will be freed.
- Otherwise it will be resized to requested size.
- Return Value:
- Returns a pointer to the allocation on success.
- NULL on allocation failure, or in the case the memory is being freed.
- --*/
- {
- return realloc(Allocation, NewSize);
- }
- CK_LOAD_MODULE_RESULT
- CkpDefaultLoadModule (
- PCK_VM Vm,
- PCSTR ModulePath,
- PCK_MODULE_HANDLE ModuleData
- )
- /*++
- Routine Description:
- This routine is called to load a new Chalk module.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- ModulePath - Supplies a pointer to the module path to load. Directories
- will be separated with dots. If this contains a slash, then it is an
- absolute path that should be loaded directly.
- ModuleData - Supplies a pointer where the loaded module information will
- be returned on success.
- Return Value:
- Returns a load module error code.
- --*/
- {
- PSTR Character;
- PCSTR Directory;
- PSTR ModuleCopy;
- UINTN PathCount;
- UINTN PathIndex;
- CK_LOAD_MODULE_RESULT Result;
- if (!CkEnsureStack(Vm, 2)) {
- return CkLoadModuleNoMemory;
- }
- //
- // If the module path contains a slash, just try to load it directly.
- //
- if (strchr(ModulePath, '/') != NULL) {
- Result = CkpLoadDynamicModule(Vm, NULL, ModulePath, ModuleData);
- if (Result != CkLoadModuleForeign) {
- Result = CkpLoadSourceFile(Vm, NULL, ModulePath, ModuleData);
- }
- return Result;
- }
- //
- // Copy the module path, and convert mydir.mymod into mydir/mymod.
- //
- ModuleCopy = strdup(ModulePath);
- if (ModuleCopy == NULL) {
- return CkLoadModuleNoMemory;
- }
- Character = ModuleCopy;
- while (*Character != '\0') {
- if (*Character == '.') {
- *Character = '/';
- }
- Character += 1;
- }
- CkPushModulePath(Vm);
- PathCount = CkListSize(Vm, -1);
- Result = CkLoadModuleNotFound;
- if (PathCount == 0) {
- CkStackPop(Vm);
- free(ModuleCopy);
- return Result;
- }
- for (PathIndex = 0; PathIndex < PathCount; PathIndex += 1) {
- CkListGet(Vm, -1, PathIndex);
- Directory = CkGetString(Vm, -1, NULL);
- if (Directory == NULL) {
- Directory = "";
- }
- Result = CkpLoadSourceFile(Vm, Directory, ModuleCopy, ModuleData);
- if (Result != CkLoadModuleNotFound) {
- break;
- }
- Result = CkpLoadDynamicModule(Vm, Directory, ModuleCopy, ModuleData);
- if (Result != CkLoadModuleNotFound) {
- break;
- }
- CkStackPop(Vm);
- }
- //
- // Pop the last list entry and the module path list.
- //
- CkStackPop(Vm);
- CkStackPop(Vm);
- free(ModuleCopy);
- return Result;
- }
- INT
- CkpDefaultSaveModule (
- PCK_VM Vm,
- PCSTR ModulePath,
- PCSTR FrozenData,
- UINTN FrozenDataSize
- )
- /*++
- Routine Description:
- This routine is called after a module is compiled, so that the caller can
- save the compilation object.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- ModulePath - Supplies a pointer to the source file path that was just
- loaded.
- FrozenData - Supplies an opaque binary representation of the compiled
- module. The format of this data is unspecified and may change between
- revisions of the language.
- FrozenDataSize - Supplies the number of bytes in the frozen module data.
- Return Value:
- 0 always, since failure is non-fatal.
- --*/
- {
- PSTR Dot;
- FILE *File;
- size_t Length;
- CHAR Path[PATH_MAX];
- ssize_t Written;
- //
- // Use the same path as the source file, but replace the extension with
- // the object extension.
- //
- Length = strlen(ModulePath);
- if (Length + 2 > PATH_MAX) {
- return 0;
- }
- memcpy(Path, ModulePath, Length + 1);
- Dot = strrchr(Path, '.');
- if (Dot == NULL) {
- return 0;
- }
- if (Dot + 1 + strlen(CK_OBJECT_EXTENSION) >= Path + PATH_MAX) {
- return 0;
- }
- strcpy(Dot + 1, CK_OBJECT_EXTENSION);
- //
- // Attempt to save the file.
- //
- File = fopen(Path, "wb");
- if (File == NULL) {
- return 0;
- }
- Written = fwrite(FrozenData, 1, FrozenDataSize, File);
- fclose(File);
- //
- // If not everything was written, delete the file so as not to have half
- // baked objects.
- //
- if (Written != FrozenDataSize) {
- unlink(Path);
- }
- return 0;
- }
- VOID
- CkpDefaultUnloadForeignModule (
- PVOID Data
- )
- /*++
- Routine Description:
- This routine is called when a foreign module is being destroyed.
- Arguments:
- Data - Supplies a pointer to the handle returned when the foreign module
- was loaded. This is usually a dynamic library handle.
- Return Value:
- None.
- --*/
- {
- CK_ASSERT(Data != NULL);
- CkpFreeLibrary(Data);
- return;
- }
- VOID
- CkpDefaultWrite (
- PCK_VM Vm,
- PCSTR String
- )
- /*++
- Routine Description:
- This routine is called to print text in Chalk.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- String - Supplies a pointer to the string to print. This routine should not
- modify or free this string.
- Return Value:
- None.
- --*/
- {
- printf("%s", String);
- return;
- }
- VOID
- CkpDefaultError (
- PCK_VM Vm,
- CK_ERROR_TYPE ErrorType,
- PSTR Message
- )
- /*++
- Routine Description:
- This routine when the Chalk interpreter experiences an error.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- ErrorType - Supplies the type of error occurring.
- Message - Supplies a pointer to a string describing the error.
- Return Value:
- None.
- --*/
- {
- if (Message == NULL) {
- Message = "";
- }
- switch (ErrorType) {
- case CkErrorNoMemory:
- fprintf(stderr, "Allocation failure\n");
- break;
- case CkErrorRuntime:
- fprintf(stderr, "Error: %s.\n", Message);
- break;
- case CkErrorCompile:
- default:
- fprintf(stderr, "Compile Error: %s.\n", Message);
- break;
- }
- return;
- }
- VOID
- CkpDefaultUnhandledException (
- PCK_VM Vm
- )
- /*++
- Routine Description:
- This routine implements the default handling of an unhandled exception. It
- takes one argument, the exception, and prints the exception and stack trace
- to stderr.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Return Value:
- None.
- --*/
- {
- UINTN Length;
- PCSTR String;
- fprintf(stderr, "Unhandled Exception:\n");
- CkPushValue(Vm, 1);
- CkCallMethod(Vm, "__str", 0);
- String = CkGetString(Vm, -1, &Length);
- fprintf(stderr, "%s\n", String);
- CkStackPop(Vm);
- //
- // Set the exception as the return value.
- //
- CkStackReplace(Vm, 0);
- return;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- CK_LOAD_MODULE_RESULT
- CkpLoadSourceFile (
- PCK_VM Vm,
- PCSTR Directory,
- PCSTR ModulePath,
- PCK_MODULE_HANDLE ModuleData
- )
- /*++
- Routine Description:
- This routine is called to load a new Chalk source file.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Directory - Supplies a pointer to the directory to find the module in.
- ModulePath - Supplies a pointer to the module path to load.
- ModuleData - Supplies a pointer where the loaded module information will
- be returned on success.
- Return Value:
- Returns a load module error code.
- --*/
- {
- FILE *File;
- off_t FileSize;
- CK_LOAD_MODULE_RESULT LoadStatus;
- CHAR ObjectPath[PATH_MAX];
- INT ObjectPathLength;
- struct stat ObjectStat;
- INT ObjectStatus;
- CHAR Path[PATH_MAX];
- INT PathLength;
- INT SourceStatus;
- struct stat Stat;
- File = NULL;
- LoadStatus = CkLoadModuleStaticError;
- //
- // Get the full path to the source file.
- //
- if (Directory == NULL) {
- PathLength = snprintf(Path, PATH_MAX, "%s", ModulePath);
- } else if (*Directory == '\0') {
- PathLength = snprintf(Path,
- PATH_MAX,
- "%s.%s",
- ModulePath,
- CK_SOURCE_EXTENSION);
- } else {
- PathLength = snprintf(Path,
- PATH_MAX,
- "%s/%s.%s",
- Directory,
- ModulePath,
- CK_SOURCE_EXTENSION);
- }
- if ((PathLength < 0) || (PathLength > PATH_MAX)) {
- ModuleData->Error = "Max path length exceeded";
- return CkLoadModuleStaticError;
- }
- Path[PATH_MAX - 1] = '\0';
- //
- // Get the path to the pre-compiled object.
- //
- if (Directory == NULL) {
- ObjectPathLength = -1;
- } else if (*Directory == '\0') {
- ObjectPathLength = snprintf(ObjectPath,
- PATH_MAX,
- "%s.%s",
- ModulePath,
- CK_OBJECT_EXTENSION);
- } else {
- ObjectPathLength = snprintf(ObjectPath,
- PATH_MAX,
- "%s/%s.%s",
- Directory,
- ModulePath,
- CK_OBJECT_EXTENSION);
- }
- if ((ObjectPathLength < 0) || (ObjectPathLength > PATH_MAX)) {
- ObjectPath[0] = '\0';
- ObjectPathLength = 0;
- }
- //
- // Stat both the source and the object. Both must be files, and the object
- // must have a non-zero size.
- //
- SourceStatus = stat(Path, &Stat);
- if ((SourceStatus == 0) && (!S_ISREG(Stat.st_mode))) {
- SourceStatus = -1;
- }
- ObjectStatus = -1;
- if (ObjectPathLength != 0) {
- ObjectStatus = stat(ObjectPath, &ObjectStat);
- if ((ObjectStatus == 0) &&
- ((!S_ISREG(ObjectStat.st_mode)) || (ObjectStat.st_size == 0))) {
- ObjectStatus = -1;
- }
- }
- //
- // If neither exists, the the module can't be found.
- //
- if ((SourceStatus != 0) && (ObjectStatus != 0)) {
- LoadStatus = CkLoadModuleNotFound;
- goto LoadSourceFileEnd;
- }
- //
- // If the object path exists and is either 1) the only thing that exists or
- // 2) is newer than the source, then try to open the object file.
- //
- FileSize = Stat.st_size;
- if ((ObjectStatus == 0) &&
- ((SourceStatus != 0) || (ObjectStat.st_mtime >= Stat.st_mtime))) {
- File = fopen(ObjectPath, "rb");
- if (File != NULL) {
- FileSize = ObjectStat.st_size;
- PathLength = ObjectPathLength;
- }
- }
- //
- // Try to open the source if opening the object file was not an option or
- // failed.
- //
- if ((File == NULL) && (SourceStatus == 0)) {
- File = fopen(Path, "rb");
- }
- if (File == NULL) {
- LoadStatus = CkLoadModuleStaticError;
- goto LoadSourceFileEnd;
- }
- LoadStatus = CkpReadSource(Vm,
- Path,
- PathLength,
- File,
- FileSize,
- ModuleData);
- LoadSourceFileEnd:
- if (LoadStatus == CkLoadModuleStaticError) {
- if ((errno == ENOENT) || (errno == EACCES) || (errno == EPERM)) {
- return CkLoadModuleNotFound;
- }
- ModuleData->Error = strerror(errno);
- }
- if (File != NULL) {
- fclose(File);
- }
- return LoadStatus;
- }
- CK_LOAD_MODULE_RESULT
- CkpReadSource (
- PCK_VM Vm,
- PCSTR ModulePath,
- UINTN PathLength,
- FILE *File,
- UINTN Size,
- PCK_MODULE_HANDLE ModuleData
- )
- /*++
- Routine Description:
- This routine is called to read in the contents of a Chalk source file.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- ModulePath - Supplies a pointer to the opened path.
- PathLength - Supplies the length of the opened path in bytes, not including
- the null terminator.
- File - Supplies a pointer to the open file.
- Size - Supplies the size of the file in bytes.
- ModuleData - Supplies a pointer where the loaded module information will
- be returned on success.
- Return Value:
- Returns a load module error code.
- --*/
- {
- CK_LOAD_MODULE_RESULT LoadStatus;
- PSTR Source;
- LoadStatus = CkLoadModuleStaticError;
- Source = CkAllocate(Vm, Size + 1);
- if (Source == NULL) {
- LoadStatus = CkLoadModuleNoMemory;
- goto ReadSourceEnd;
- }
- if (fread(Source, 1, Size, File) != Size) {
- LoadStatus = CkLoadModuleStaticError;
- goto ReadSourceEnd;
- }
- Source[Size] = '\0';
- ModuleData->Source.Path = CkAllocate(Vm, PathLength + 1);
- if (ModuleData->Source.Path == NULL) {
- LoadStatus = CkLoadModuleNoMemory;
- goto ReadSourceEnd;
- }
- CkCopy(ModuleData->Source.Path, ModulePath, PathLength + 1);
- ModuleData->Source.PathLength = PathLength;
- ModuleData->Source.Text = Source;
- Source = NULL;
- ModuleData->Source.Length = Size;
- LoadStatus = CkLoadModuleSource;
- ReadSourceEnd:
- if (Source != NULL) {
- CkFree(Vm, Source);
- }
- return LoadStatus;
- }
- CK_LOAD_MODULE_RESULT
- CkpLoadDynamicModule (
- PCK_VM Vm,
- PCSTR Directory,
- PCSTR ModulePath,
- PCK_MODULE_HANDLE ModuleData
- )
- /*++
- Routine Description:
- This routine is called to load a new Chalk dynamic library module.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Directory - Supplies a pointer to the directory to find the module in. If
- this is NULL, then the module path will be used without modification.
- ModulePath - Supplies a pointer to the module path to load.
- ModuleData - Supplies a pointer where the loaded module information will
- be returned on success.
- Return Value:
- Returns a load module error code.
- --*/
- {
- PCK_FOREIGN_FUNCTION EntryPoint;
- PVOID Handle;
- CK_LOAD_MODULE_RESULT LoadStatus;
- CHAR Path[PATH_MAX];
- INT PathLength;
- struct stat Stat;
- Handle = NULL;
- LoadStatus = CkLoadModuleStaticError;
- ModuleData->Error = NULL;
- if (Directory == NULL) {
- PathLength = snprintf(Path, PATH_MAX, "%s", ModulePath);
- } else if (*Directory == '\0') {
- PathLength = snprintf(Path,
- PATH_MAX,
- "%s%s",
- ModulePath,
- CkSharedLibraryExtension);
- } else {
- PathLength = snprintf(Path,
- PATH_MAX,
- "%s/%s%s",
- Directory,
- ModulePath,
- CkSharedLibraryExtension);
- }
- if ((PathLength < 0) || (PathLength > PATH_MAX)) {
- ModuleData->Error = "Max path length exceeded";
- return CkLoadModuleStaticError;
- }
- Path[PATH_MAX - 1] = '\0';
- //
- // Validate that this path points to a regular file before trying to open
- // a dynamic library.
- //
- if (stat(Path, &Stat) != 0) {
- if ((errno == ENOENT) || (errno == EACCES) || (errno == EPERM)) {
- LoadStatus = CkLoadModuleNotFound;
- } else {
- ModuleData->Error = strerror(errno);
- }
- goto LoadDynamicModuleEnd;
- }
- if (!S_ISREG(Stat.st_mode)) {
- return CkLoadModuleNotFound;
- }
- //
- // Open up the dynamic library.
- //
- Handle = CkpLoadLibrary(Path);
- if (Handle == NULL) {
- return CkLoadModuleNotFound;
- }
- EntryPoint = CkpGetLibrarySymbol(Handle, CK_MODULE_ENTRY_NAME);
- if (EntryPoint == NULL) {
- LoadStatus = CkLoadModuleNotFound;
- goto LoadDynamicModuleEnd;
- }
- ModuleData->Foreign.Path = CkAllocate(Vm, PathLength + 1);
- if (ModuleData->Source.Path == NULL) {
- LoadStatus = CkLoadModuleNoMemory;
- goto LoadDynamicModuleEnd;
- }
- CkCopy(ModuleData->Foreign.Path, Path, PathLength + 1);
- ModuleData->Foreign.PathLength = PathLength;
- ModuleData->Foreign.Handle = Handle;
- Handle = NULL;
- ModuleData->Foreign.Entry = EntryPoint;
- LoadStatus = CkLoadModuleForeign;
- LoadDynamicModuleEnd:
- if (Handle != NULL) {
- CkpFreeLibrary(Handle);
- }
- return LoadStatus;
- }
|