mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-05 20:26:53 +08:00
Merge pull request #199 from ThakeeNathees/modules
modules were moved to cli/modules
This commit is contained in:
commit
34622afc0b
35
cli/all.c
Normal file
35
cli/all.c
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2020-2022 Thakee Nathees
|
||||
* Copyright (c) 2021-2022 Pocketlang Contributors
|
||||
* Distributed Under The MIT License
|
||||
*/
|
||||
|
||||
// This file will include all source files from the modules, modules/thirdparty
|
||||
// sub directories here into this single file. That'll make it easy to compile
|
||||
// with the command `gcc cli/*.c ...` instead of having to add every single
|
||||
// sub directory to the list of source directory.
|
||||
|
||||
/*****************************************************************************/
|
||||
/* STD MODULE SOURCES */
|
||||
/*****************************************************************************/
|
||||
|
||||
#include "modules/modules.c"
|
||||
|
||||
#include "modules/std_file.c"
|
||||
#include "modules/std_path.c"
|
||||
|
||||
/*****************************************************************************/
|
||||
/* THIRDPARTY SOURCES */
|
||||
/*****************************************************************************/
|
||||
|
||||
// Library : cwalk
|
||||
// Source : https://github.com/likle/cwalk/
|
||||
// Doc : https://likle.github.io/cwalk/
|
||||
// About : Path library for C/C++. Cross-Platform for Windows, MacOS and
|
||||
// Linux. Supports UNIX and Windows path styles on those platforms.
|
||||
#include "modules/thirdparty/cwalk/cwalk.c"
|
||||
|
||||
// Library : argparse
|
||||
// Source : https://github.com/cofyc/argparse/
|
||||
// About : Command-line arguments parsing library.
|
||||
#include "thirdparty/argparse/argparse.c"
|
@ -16,14 +16,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Note that the cli itself is not a part of the pocketlang compiler, instead
|
||||
// it's a host application to run pocketlang from the command line. We're
|
||||
// embedding the pocketlang VM and we can only use its public APIs, not any
|
||||
// internals of it, including assertion macros. So that we've copyied the
|
||||
// "common.h" header. This can be moved to "src/include/common.h" and include
|
||||
// as optional header, which is something I don't like because it makes
|
||||
// pocketlang contain 2 headers (we'll always try to be minimal).
|
||||
#include "common.h"
|
||||
#include "modules/common.h"
|
||||
|
||||
#define CLI_NOTICE \
|
||||
"PocketLang " PK_VERSION_STRING " (https://github.com/ThakeeNathees/pocketlang/)\n" \
|
||||
|
@ -5,8 +5,7 @@
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include "modules.h"
|
||||
|
||||
#include "modules/modules.h"
|
||||
#include "thirdparty/argparse/argparse.h"
|
||||
|
||||
// FIXME: Everything below here is temporary and for testing.
|
||||
|
@ -4,17 +4,9 @@
|
||||
* Distributed Under The MIT License
|
||||
*/
|
||||
|
||||
// A collection of reusable macros that pocketlang use. This file doesn't have
|
||||
// any dependencies, you can just drag and drop this file in your project if
|
||||
// you want to use these macros.
|
||||
|
||||
#ifndef PK_COMMON_H
|
||||
#define PK_COMMON_H
|
||||
|
||||
#ifndef __has_builtin
|
||||
#define __has_builtin(x) 0
|
||||
#endif
|
||||
|
||||
#include <stdio.h> //< Only needed here for ASSERT() macro and for release mode
|
||||
//< TODO; macro use this to print a crash report.
|
||||
|
91
cli/modules/modules.c
Normal file
91
cli/modules/modules.c
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2020-2022 Thakee Nathees
|
||||
* Copyright (c) 2021-2022 Pocketlang Contributors
|
||||
* Distributed Under The MIT License
|
||||
*/
|
||||
|
||||
#include "modules.h"
|
||||
|
||||
// Note: Everything here is for testing the native API, and will have to
|
||||
// refactor everything.
|
||||
|
||||
// Allocate a new module object of type [Ty].
|
||||
#define NEW_OBJ(Ty) (Ty*)malloc(sizeof(Ty))
|
||||
|
||||
// Dellocate module object, allocated by NEW_OBJ(). Called by the freeObj
|
||||
// callback.
|
||||
#define FREE_OBJ(ptr) free(ptr)
|
||||
|
||||
/*****************************************************************************/
|
||||
/* MODULE FUNCTIONS DECLARATION */
|
||||
/*****************************************************************************/
|
||||
|
||||
void fileGetAttrib(PKVM* vm, File* file, const char* attrib);
|
||||
bool fileSetAttrib(PKVM* vm, File* file, const char* attrib);
|
||||
void fileClean(PKVM* vm, File* file);
|
||||
|
||||
void registerModuleFile(PKVM* vm);
|
||||
void registerModulePath(PKVM* vm);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* MODULE PUBLIC FUNCTIONS */
|
||||
/*****************************************************************************/
|
||||
|
||||
void initObj(Obj* obj, ObjType type) {
|
||||
obj->type = type;
|
||||
}
|
||||
|
||||
void objGetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib) {
|
||||
Obj* obj = (Obj*)instance;
|
||||
ASSERT(obj->type == (ObjType)id, OOPS);
|
||||
|
||||
switch (obj->type) {
|
||||
case OBJ_FILE:
|
||||
fileGetAttrib(vm, (File*)obj, attrib.string);
|
||||
return;
|
||||
}
|
||||
STATIC_ASSERT(_OBJ_MAX_ == 2);
|
||||
}
|
||||
|
||||
bool objSetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib) {
|
||||
Obj* obj = (Obj*)instance;
|
||||
ASSERT(obj->type == (ObjType)id, OOPS);
|
||||
|
||||
switch (obj->type) {
|
||||
case OBJ_FILE:
|
||||
return fileSetAttrib(vm, (File*)obj, attrib.string);
|
||||
}
|
||||
STATIC_ASSERT(_OBJ_MAX_ == 2);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void freeObj(PKVM* vm, void* instance, uint32_t id) {
|
||||
Obj* obj = (Obj*)instance;
|
||||
ASSERT(obj->type == (ObjType)id, OOPS);
|
||||
|
||||
switch (obj->type) {
|
||||
case OBJ_FILE:
|
||||
fileClean(vm, (File*)obj);
|
||||
}
|
||||
STATIC_ASSERT(_OBJ_MAX_ == 2);
|
||||
|
||||
FREE_OBJ(obj);
|
||||
}
|
||||
|
||||
const char* getObjName(uint32_t id) {
|
||||
switch ((ObjType)id) {
|
||||
case OBJ_FILE: return "File";
|
||||
}
|
||||
STATIC_ASSERT(_OBJ_MAX_ == 2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* REGISTER MODULES */
|
||||
/*****************************************************************************/
|
||||
|
||||
void registerModules(PKVM* vm) {
|
||||
registerModuleFile(vm);
|
||||
registerModulePath(vm);
|
||||
}
|
@ -4,35 +4,20 @@
|
||||
* Distributed Under The MIT License
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#ifndef MODULES_H
|
||||
#define MODULES_H
|
||||
|
||||
#include <pocketlang.h>
|
||||
|
||||
#include "common.h"
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/*****************************************************************************/
|
||||
/* MODULE OBJECTS */
|
||||
/*****************************************************************************/
|
||||
|
||||
// Type enums of cli module objects.
|
||||
typedef enum {
|
||||
OBJ_FILE = 1,
|
||||
} ObjType;
|
||||
|
||||
// The abstract type of the objects.
|
||||
typedef struct {
|
||||
ObjType type;
|
||||
} Obj;
|
||||
|
||||
// File access mode.
|
||||
typedef enum {
|
||||
|
||||
FMODE_READ = (1 << 0),
|
||||
FMODE_WRITE = (1 << 1),
|
||||
FMODE_APPEND = (1 << 2),
|
||||
|
||||
_FMODE_EXT = (1 << 3),
|
||||
FMODE_READ_EXT = (_FMODE_EXT | FMODE_READ),
|
||||
FMODE_WRITE_EXT = (_FMODE_EXT | FMODE_WRITE),
|
||||
FMODE_APPEND_EXT = (_FMODE_EXT | FMODE_APPEND),
|
||||
} FileAccessMode;
|
||||
|
||||
// Str | If already exists | If does not exist |
|
||||
// -----+-------------------+-------------------|
|
||||
// 'r' | read from start | failure to open |
|
||||
@ -41,11 +26,28 @@ typedef enum {
|
||||
// 'r+' | read from start | error |
|
||||
// 'w+' | destroy contents | create new |
|
||||
// 'a+' | write to end | create new |
|
||||
typedef enum {
|
||||
FMODE_READ = (1 << 0),
|
||||
FMODE_WRITE = (1 << 1),
|
||||
FMODE_APPEND = (1 << 2),
|
||||
_FMODE_EXT = (1 << 3),
|
||||
FMODE_READ_EXT = (_FMODE_EXT | FMODE_READ),
|
||||
FMODE_WRITE_EXT = (_FMODE_EXT | FMODE_WRITE),
|
||||
FMODE_APPEND_EXT = (_FMODE_EXT | FMODE_APPEND),
|
||||
} FileAccessMode;
|
||||
|
||||
typedef enum {
|
||||
OBJ_FILE = 1,
|
||||
|
||||
_OBJ_MAX_
|
||||
} ObjType;
|
||||
|
||||
typedef struct {
|
||||
ObjType type;
|
||||
} Obj;
|
||||
|
||||
// A wrapper around the FILE* for the File module.
|
||||
typedef struct {
|
||||
Obj _super;
|
||||
|
||||
FILE* fp; // C file poinnter.
|
||||
FileAccessMode mode; // Access mode of the file.
|
||||
bool closed; // True if the file isn't closed yet.
|
||||
@ -59,7 +61,10 @@ typedef struct {
|
||||
void initObj(Obj* obj, ObjType type);
|
||||
|
||||
// A function callback called by pocket VM to get attribute of a native
|
||||
// instance.
|
||||
// instance. The value of the attributes will be returned with pkReturn...()
|
||||
// functions and if the attribute doesn't exists on the instance we're
|
||||
// shouldn't return anything, PKVM will know it and set error (or use some
|
||||
// common attributes like "as_string", "as_repr", etc).
|
||||
void objGetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib);
|
||||
|
||||
// A function callback called by pocket VM to set attribute of a native
|
||||
@ -77,9 +82,21 @@ const char* getObjName(uint32_t id);
|
||||
// Registers all the cli modules.
|
||||
void registerModules(PKVM* vm);
|
||||
|
||||
// 'path' moudle public functions used at various cli functions.
|
||||
/*****************************************************************************/
|
||||
/* SHARED FUNCTIONS */
|
||||
/*****************************************************************************/
|
||||
|
||||
// These are "public" module functions that can be shared. Since some modules
|
||||
// can be used for cli's internals we're defining such functions here and they
|
||||
// will be imported in the cli.
|
||||
|
||||
bool pathIsAbsolute(const char* path);
|
||||
|
||||
void pathGetDirName(const char* path, size_t* length);
|
||||
|
||||
size_t pathNormalize(const char* path, char* buff, size_t buff_size);
|
||||
|
||||
size_t pathJoin(const char* from, const char* path, char* buffer,
|
||||
size_t buff_size);
|
||||
|
||||
#endif // MODULES_H
|
145
cli/modules/std_file.c
Normal file
145
cli/modules/std_file.c
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (c) 2020-2022 Thakee Nathees
|
||||
* Copyright (c) 2021-2022 Pocketlang Contributors
|
||||
* Distributed Under The MIT License
|
||||
*/
|
||||
|
||||
#include "modules.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/* FILE OBJECT OPERATORS */
|
||||
/*****************************************************************************/
|
||||
|
||||
void fileGetAttrib(PKVM* vm, File* file, const char* attrib) {
|
||||
if (strcmp(attrib, "closed") == 0) {
|
||||
pkReturnBool(vm, file->closed);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool fileSetAttrib(PKVM* vm, File* file, const char* attrib) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void fileClean(PKVM* vm, File* file) {
|
||||
if (!file->closed) {
|
||||
if (fclose(file->fp) != 0) { /* TODO: error! */ }
|
||||
file->closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* FILE MODULE FUNCTIONS */
|
||||
/*****************************************************************************/
|
||||
|
||||
static void _fileOpen(PKVM* vm) {
|
||||
|
||||
int argc = pkGetArgc(vm);
|
||||
if (!pkCheckArgcRange(vm, argc, 1, 2)) return;
|
||||
|
||||
const char* path;
|
||||
if (!pkGetArgString(vm, 1, &path, NULL)) return;
|
||||
|
||||
const char* mode_str = "r";
|
||||
FileAccessMode mode = FMODE_READ;
|
||||
|
||||
if (argc == 2) {
|
||||
if (!pkGetArgString(vm, 2, &mode_str, NULL)) return;
|
||||
|
||||
// Check if the mode string is valid, and update the mode value.
|
||||
do {
|
||||
if (strcmp(mode_str, "r") == 0) { mode = FMODE_READ; break; }
|
||||
if (strcmp(mode_str, "w") == 0) { mode = FMODE_WRITE; break; }
|
||||
if (strcmp(mode_str, "a") == 0) { mode = FMODE_APPEND; break; }
|
||||
if (strcmp(mode_str, "r+") == 0) { mode = FMODE_READ_EXT; break; }
|
||||
if (strcmp(mode_str, "w+") == 0) { mode = FMODE_WRITE_EXT; break; }
|
||||
if (strcmp(mode_str, "a+") == 0) { mode = FMODE_APPEND_EXT; break; }
|
||||
|
||||
// TODO: (fmt, ...) va_arg for runtime error public api.
|
||||
// If we reached here, that means it's an invalid mode string.
|
||||
pkSetRuntimeError(vm, "Invalid mode string.");
|
||||
return;
|
||||
} while (false);
|
||||
}
|
||||
|
||||
FILE* fp = fopen(path, mode_str);
|
||||
|
||||
if (fp != NULL) {
|
||||
File* file = NEW_OBJ(File);
|
||||
initObj(&file->_super, OBJ_FILE);
|
||||
file->fp = fp;
|
||||
file->mode = mode;
|
||||
file->closed = false;
|
||||
|
||||
pkReturnInstNative(vm, (void*)file, OBJ_FILE);
|
||||
|
||||
} else {
|
||||
pkReturnNull(vm);
|
||||
}
|
||||
}
|
||||
|
||||
static void _fileRead(PKVM* vm) {
|
||||
File* file;
|
||||
if (!pkGetArgInst(vm, 1, OBJ_FILE, (void**)&file)) return;
|
||||
|
||||
if (file->closed) {
|
||||
pkSetRuntimeError(vm, "Cannot read from a closed file.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((file->mode != FMODE_READ) && ((_FMODE_EXT & file->mode) == 0)) {
|
||||
pkSetRuntimeError(vm, "File is not readable.");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: this is temporary.
|
||||
char buff[2048];
|
||||
fread((void*)buff, sizeof(char), sizeof(buff), file->fp);
|
||||
pkReturnString(vm, (const char*)buff);
|
||||
}
|
||||
|
||||
static void _fileWrite(PKVM* vm) {
|
||||
File* file;
|
||||
const char* text; uint32_t length;
|
||||
if (!pkGetArgInst(vm, 1, OBJ_FILE, (void**)&file)) return;
|
||||
if (!pkGetArgString(vm, 2, &text, &length)) return;
|
||||
|
||||
if (file->closed) {
|
||||
pkSetRuntimeError(vm, "Cannot write to a closed file.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((file->mode != FMODE_WRITE) && ((_FMODE_EXT & file->mode) == 0)) {
|
||||
pkSetRuntimeError(vm, "File is not writable.");
|
||||
return;
|
||||
}
|
||||
|
||||
fwrite(text, sizeof(char), (size_t)length, file->fp);
|
||||
}
|
||||
|
||||
static void _fileClose(PKVM* vm) {
|
||||
File* file;
|
||||
if (!pkGetArgInst(vm, 1, OBJ_FILE, (void**)&file)) return;
|
||||
|
||||
if (file->closed) {
|
||||
pkSetRuntimeError(vm, "File already closed.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fclose(file->fp) != 0) {
|
||||
pkSetRuntimeError(vm, "fclose() failed!\n" \
|
||||
" at " __FILE__ ":" STRINGIFY(__LINE__) ".");
|
||||
}
|
||||
file->closed = true;
|
||||
}
|
||||
|
||||
void registerModuleFile(PKVM* vm) {
|
||||
PkHandle* file = pkNewModule(vm, "File");
|
||||
|
||||
pkModuleAddFunction(vm, file, "open", _fileOpen, -1);
|
||||
pkModuleAddFunction(vm, file, "read", _fileRead, 1);
|
||||
pkModuleAddFunction(vm, file, "write", _fileWrite, 2);
|
||||
pkModuleAddFunction(vm, file, "close", _fileClose, 1);
|
||||
|
||||
pkReleaseHandle(vm, file);
|
||||
}
|
@ -4,76 +4,6 @@
|
||||
* Distributed Under The MIT License
|
||||
*/
|
||||
|
||||
#include "modules.h"
|
||||
|
||||
// Note: Everything here is for testing the native API, and will have to
|
||||
// refactor everything.
|
||||
|
||||
// Allocate a new module object of type [Ty].
|
||||
#define NEW_OBJ(Ty) (Ty*)malloc(sizeof(Ty))
|
||||
|
||||
// Dellocate module object, allocated by NEW_OBJ(). Called by the freeObj
|
||||
// callback.
|
||||
#define FREE_OBJ(ptr) free(ptr)
|
||||
|
||||
void initObj(Obj* obj, ObjType type) {
|
||||
obj->type = type;
|
||||
}
|
||||
|
||||
void objGetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib) {
|
||||
Obj* obj = (Obj*)instance;
|
||||
ASSERT(obj->type == (ObjType)id, OOPS);
|
||||
|
||||
if (obj->type == OBJ_FILE) {
|
||||
File* file = (File*)obj;
|
||||
if (strcmp(attrib.string, "closed") == 0) {
|
||||
pkReturnBool(vm, file->closed);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return; // Attribute not found.
|
||||
}
|
||||
|
||||
bool objSetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib) {
|
||||
Obj* obj = (Obj*)instance;
|
||||
ASSERT(obj->type == (ObjType)id, OOPS);
|
||||
|
||||
if (obj->type == OBJ_FILE) {
|
||||
File* file = (File*)obj;
|
||||
// Nothing to change.
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void freeObj(PKVM* vm, void* instance, uint32_t id) {
|
||||
Obj* obj = (Obj*)instance;
|
||||
ASSERT(obj->type == (ObjType)id, OOPS);
|
||||
|
||||
// If the file isn't closed, close it to flush it's buffer.
|
||||
if (obj->type == OBJ_FILE) {
|
||||
File* file = (File*)obj;
|
||||
if (!file->closed) {
|
||||
if (fclose(file->fp) != 0) { /* TODO: error! */ }
|
||||
file->closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
FREE_OBJ(obj);
|
||||
}
|
||||
|
||||
const char* getObjName(uint32_t id) {
|
||||
switch ((ObjType)id) {
|
||||
case OBJ_FILE: return "File";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* PATH MODULE */
|
||||
/*****************************************************************************/
|
||||
|
||||
#include "thirdparty/cwalk/cwalk.h"
|
||||
#if defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
|
||||
#include "thirdparty/dirent/dirent.h"
|
||||
@ -95,7 +25,7 @@ const char* getObjName(uint32_t id) {
|
||||
#define MAX_JOIN_PATHS 8
|
||||
|
||||
/*****************************************************************************/
|
||||
/* PUBLIC FUNCTIONS */
|
||||
/* PATH SHARED FUNCTIONS */
|
||||
/*****************************************************************************/
|
||||
|
||||
bool pathIsAbsolute(const char* path) {
|
||||
@ -157,7 +87,7 @@ static inline size_t pathAbs(const char* path, char* buff, size_t buff_size) {
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* PATH MODULES FUNCTIONS */
|
||||
/* PATH MODULE FUNCTIONS */
|
||||
/*****************************************************************************/
|
||||
|
||||
static void _pathSetStyleUnix(PKVM* vm) {
|
||||
@ -305,128 +235,3 @@ void registerModulePath(PKVM* vm) {
|
||||
|
||||
pkReleaseHandle(vm, path);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* FILE MODULE */
|
||||
/*****************************************************************************/
|
||||
|
||||
static void _fileOpen(PKVM* vm) {
|
||||
|
||||
int argc = pkGetArgc(vm);
|
||||
if (!pkCheckArgcRange(vm, argc, 1, 2)) return;
|
||||
|
||||
const char* path;
|
||||
if (!pkGetArgString(vm, 1, &path, NULL)) return;
|
||||
|
||||
const char* mode_str = "r";
|
||||
FileAccessMode mode = FMODE_READ;
|
||||
|
||||
if (argc == 2) {
|
||||
if (!pkGetArgString(vm, 2, &mode_str, NULL)) return;
|
||||
|
||||
// Check if the mode string is valid, and update the mode value.
|
||||
do {
|
||||
if (strcmp(mode_str, "r") == 0) { mode = FMODE_READ; break; }
|
||||
if (strcmp(mode_str, "w") == 0) { mode = FMODE_WRITE; break; }
|
||||
if (strcmp(mode_str, "a") == 0) { mode = FMODE_APPEND; break; }
|
||||
if (strcmp(mode_str, "r+") == 0) { mode = FMODE_READ_EXT; break; }
|
||||
if (strcmp(mode_str, "w+") == 0) { mode = FMODE_WRITE_EXT; break; }
|
||||
if (strcmp(mode_str, "a+") == 0) { mode = FMODE_APPEND_EXT; break; }
|
||||
|
||||
// TODO: (fmt, ...) va_arg for runtime error public api.
|
||||
// If we reached here, that means it's an invalid mode string.
|
||||
pkSetRuntimeError(vm, "Invalid mode string.");
|
||||
return;
|
||||
} while (false);
|
||||
}
|
||||
|
||||
FILE* fp = fopen(path, mode_str);
|
||||
|
||||
if (fp != NULL) {
|
||||
File* file = NEW_OBJ(File);
|
||||
initObj(&file->_super, OBJ_FILE);
|
||||
file->fp = fp;
|
||||
file->mode = mode;
|
||||
file->closed = false;
|
||||
|
||||
pkReturnInstNative(vm, (void*)file, OBJ_FILE);
|
||||
|
||||
} else {
|
||||
pkReturnNull(vm);
|
||||
}
|
||||
}
|
||||
|
||||
static void _fileRead(PKVM* vm) {
|
||||
File* file;
|
||||
if (!pkGetArgInst(vm, 1, OBJ_FILE, (void**)&file)) return;
|
||||
|
||||
if (file->closed) {
|
||||
pkSetRuntimeError(vm, "Cannot read from a closed file.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((file->mode != FMODE_READ) && ((_FMODE_EXT & file->mode) == 0)) {
|
||||
pkSetRuntimeError(vm, "File is not readable.");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: this is temporary.
|
||||
char buff[2048];
|
||||
fread((void*)buff, sizeof(char), sizeof(buff), file->fp);
|
||||
pkReturnString(vm, (const char*)buff);
|
||||
}
|
||||
|
||||
static void _fileWrite(PKVM* vm) {
|
||||
File* file;
|
||||
const char* text; uint32_t length;
|
||||
if (!pkGetArgInst(vm, 1, OBJ_FILE, (void**)&file)) return;
|
||||
if (!pkGetArgString(vm, 2, &text, &length)) return;
|
||||
|
||||
if (file->closed) {
|
||||
pkSetRuntimeError(vm, "Cannot write to a closed file.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((file->mode != FMODE_WRITE) && ((_FMODE_EXT & file->mode) == 0)) {
|
||||
pkSetRuntimeError(vm, "File is not writable.");
|
||||
return;
|
||||
}
|
||||
|
||||
fwrite(text, sizeof(char), (size_t)length, file->fp);
|
||||
}
|
||||
|
||||
static void _fileClose(PKVM* vm) {
|
||||
File* file;
|
||||
if (!pkGetArgInst(vm, 1, OBJ_FILE, (void**)&file)) return;
|
||||
|
||||
if (file->closed) {
|
||||
pkSetRuntimeError(vm, "File already closed.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fclose(file->fp) != 0) {
|
||||
pkSetRuntimeError(vm, "fclose() failed!\n" \
|
||||
" at " __FILE__ ":" STRINGIFY(__LINE__) ".");
|
||||
}
|
||||
file->closed = true;
|
||||
}
|
||||
|
||||
void registerModuleFile(PKVM* vm) {
|
||||
PkHandle* file = pkNewModule(vm, "File");
|
||||
|
||||
pkModuleAddFunction(vm, file, "open", _fileOpen, -1);
|
||||
pkModuleAddFunction(vm, file, "read", _fileRead, 1);
|
||||
pkModuleAddFunction(vm, file, "write", _fileWrite, 2);
|
||||
pkModuleAddFunction(vm, file, "close", _fileClose, 1);
|
||||
|
||||
pkReleaseHandle(vm, file);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* REGISTER MODULES */
|
||||
/*****************************************************************************/
|
||||
|
||||
void registerModules(PKVM* vm) {
|
||||
registerModuleFile(vm);
|
||||
registerModulePath(vm);
|
||||
}
|
17
cli/modules/thirdparty/dlfcn-win32/COPYING
vendored
Normal file
17
cli/modules/thirdparty/dlfcn-win32/COPYING
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
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.
|
785
cli/modules/thirdparty/dlfcn-win32/dlfcn.c
vendored
Normal file
785
cli/modules/thirdparty/dlfcn-win32/dlfcn.c
vendored
Normal file
@ -0,0 +1,785 @@
|
||||
/*
|
||||
* dlfcn-win32
|
||||
* Copyright (c) 2007 Ramiro Polla
|
||||
* Copyright (c) 2015 Tiancheng "Timothy" Gu
|
||||
* Copyright (c) 2019 Pali Rohár <pali.rohar@gmail.com>
|
||||
* Copyright (c) 2020 Ralf Habacker <ralf.habacker@freenet.de>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define _CRTDBG_MAP_ALLOC
|
||||
#include <stdlib.h>
|
||||
#include <crtdbg.h>
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Older versions do not have this type */
|
||||
#if _WIN32_WINNT < 0x0500
|
||||
typedef ULONG ULONG_PTR;
|
||||
#endif
|
||||
|
||||
/* Older SDK versions do not have these macros */
|
||||
#ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
|
||||
#define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x4
|
||||
#endif
|
||||
#ifndef GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
|
||||
#define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 0x2
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* https://docs.microsoft.com/en-us/cpp/intrinsics/returnaddress */
|
||||
#pragma intrinsic( _ReturnAddress )
|
||||
#else
|
||||
/* https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html */
|
||||
#ifndef _ReturnAddress
|
||||
#define _ReturnAddress( ) ( __builtin_extract_return_addr( __builtin_return_address( 0 ) ) )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef DLFCN_WIN32_SHARED
|
||||
#define DLFCN_WIN32_EXPORTS
|
||||
#endif
|
||||
#include "dlfcn.h"
|
||||
|
||||
#if defined( _MSC_VER ) && _MSC_VER >= 1300
|
||||
/* https://docs.microsoft.com/en-us/cpp/cpp/noinline */
|
||||
#define DLFCN_NOINLINE __declspec( noinline )
|
||||
#elif defined( __GNUC__ ) && ( ( __GNUC__ > 3 ) || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 1 ) )
|
||||
/* https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html */
|
||||
#define DLFCN_NOINLINE __attribute__(( noinline ))
|
||||
#else
|
||||
#define DLFCN_NOINLINE
|
||||
#endif
|
||||
|
||||
/* Note:
|
||||
* MSDN says these functions are not thread-safe. We make no efforts to have
|
||||
* any kind of thread safety.
|
||||
*/
|
||||
|
||||
typedef struct local_object {
|
||||
HMODULE hModule;
|
||||
struct local_object *previous;
|
||||
struct local_object *next;
|
||||
} local_object;
|
||||
|
||||
static local_object first_object;
|
||||
|
||||
/* These functions implement a double linked list for the local objects. */
|
||||
static local_object *local_search( HMODULE hModule )
|
||||
{
|
||||
local_object *pobject;
|
||||
|
||||
if( hModule == NULL )
|
||||
return NULL;
|
||||
|
||||
for( pobject = &first_object; pobject; pobject = pobject->next )
|
||||
if( pobject->hModule == hModule )
|
||||
return pobject;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static BOOL local_add( HMODULE hModule )
|
||||
{
|
||||
local_object *pobject;
|
||||
local_object *nobject;
|
||||
|
||||
if( hModule == NULL )
|
||||
return TRUE;
|
||||
|
||||
pobject = local_search( hModule );
|
||||
|
||||
/* Do not add object again if it's already on the list */
|
||||
if( pobject != NULL )
|
||||
return TRUE;
|
||||
|
||||
for( pobject = &first_object; pobject->next; pobject = pobject->next );
|
||||
|
||||
nobject = (local_object *) malloc( sizeof( local_object ) );
|
||||
|
||||
if( !nobject )
|
||||
return FALSE;
|
||||
|
||||
pobject->next = nobject;
|
||||
nobject->next = NULL;
|
||||
nobject->previous = pobject;
|
||||
nobject->hModule = hModule;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void local_rem( HMODULE hModule )
|
||||
{
|
||||
local_object *pobject;
|
||||
|
||||
if( hModule == NULL )
|
||||
return;
|
||||
|
||||
pobject = local_search( hModule );
|
||||
|
||||
if( pobject == NULL )
|
||||
return;
|
||||
|
||||
if( pobject->next )
|
||||
pobject->next->previous = pobject->previous;
|
||||
if( pobject->previous )
|
||||
pobject->previous->next = pobject->next;
|
||||
|
||||
free( pobject );
|
||||
}
|
||||
|
||||
/* POSIX says dlerror( ) doesn't have to be thread-safe, so we use one
|
||||
* static buffer.
|
||||
* MSDN says the buffer cannot be larger than 64K bytes, so we set it to
|
||||
* the limit.
|
||||
*/
|
||||
static char error_buffer[65535];
|
||||
static BOOL error_occurred;
|
||||
|
||||
static void save_err_str( const char *str, DWORD dwMessageId )
|
||||
{
|
||||
DWORD ret;
|
||||
size_t pos, len;
|
||||
|
||||
len = strlen( str );
|
||||
if( len > sizeof( error_buffer ) - 5 )
|
||||
len = sizeof( error_buffer ) - 5;
|
||||
|
||||
/* Format error message to:
|
||||
* "<argument to function that failed>": <Windows localized error message>
|
||||
*/
|
||||
pos = 0;
|
||||
error_buffer[pos++] = '"';
|
||||
memcpy( error_buffer + pos, str, len );
|
||||
pos += len;
|
||||
error_buffer[pos++] = '"';
|
||||
error_buffer[pos++] = ':';
|
||||
error_buffer[pos++] = ' ';
|
||||
|
||||
ret = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwMessageId,
|
||||
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
|
||||
error_buffer + pos, (DWORD) ( sizeof( error_buffer ) - pos ), NULL );
|
||||
pos += ret;
|
||||
|
||||
/* When FormatMessageA() fails it returns zero and does not touch buffer
|
||||
* so add trailing null byte */
|
||||
if( ret == 0 )
|
||||
error_buffer[pos] = '\0';
|
||||
|
||||
if( pos > 1 )
|
||||
{
|
||||
/* POSIX says the string must not have trailing <newline> */
|
||||
if( error_buffer[pos-2] == '\r' && error_buffer[pos-1] == '\n' )
|
||||
error_buffer[pos-2] = '\0';
|
||||
}
|
||||
|
||||
error_occurred = TRUE;
|
||||
}
|
||||
|
||||
static void save_err_ptr_str( const void *ptr, DWORD dwMessageId )
|
||||
{
|
||||
char ptr_buf[2 + 2 * sizeof( ptr ) + 1];
|
||||
char num;
|
||||
size_t i;
|
||||
|
||||
ptr_buf[0] = '0';
|
||||
ptr_buf[1] = 'x';
|
||||
|
||||
for( i = 0; i < 2 * sizeof( ptr ); i++ )
|
||||
{
|
||||
num = (char) ( ( ( (ULONG_PTR) ptr ) >> ( 8 * sizeof( ptr ) - 4 * ( i + 1 ) ) ) & 0xF );
|
||||
ptr_buf[2 + i] = num + ( ( num < 0xA ) ? '0' : ( 'A' - 0xA ) );
|
||||
}
|
||||
|
||||
ptr_buf[2 + 2 * sizeof( ptr )] = 0;
|
||||
|
||||
save_err_str( ptr_buf, dwMessageId );
|
||||
}
|
||||
|
||||
static HMODULE MyGetModuleHandleFromAddress( void *addr )
|
||||
{
|
||||
static BOOL (WINAPI *GetModuleHandleExAPtr)(DWORD, LPCSTR, HMODULE *) = NULL;
|
||||
static BOOL failed = FALSE;
|
||||
HMODULE kernel32;
|
||||
HMODULE hModule;
|
||||
MEMORY_BASIC_INFORMATION info;
|
||||
SIZE_T sLen;
|
||||
|
||||
if( !failed && GetModuleHandleExAPtr == NULL )
|
||||
{
|
||||
kernel32 = GetModuleHandleA( "Kernel32.dll" );
|
||||
if( kernel32 != NULL )
|
||||
GetModuleHandleExAPtr = (BOOL (WINAPI *)(DWORD, LPCSTR, HMODULE *)) GetProcAddress( kernel32, "GetModuleHandleExA" );
|
||||
if( GetModuleHandleExAPtr == NULL )
|
||||
failed = TRUE;
|
||||
}
|
||||
|
||||
if( !failed )
|
||||
{
|
||||
/* If GetModuleHandleExA is available use it with GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS */
|
||||
if( !GetModuleHandleExAPtr( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR) addr, &hModule ) )
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* To get HMODULE from address use undocumented hack from https://stackoverflow.com/a/2396380
|
||||
* The HMODULE of a DLL is the same value as the module's base address.
|
||||
*/
|
||||
sLen = VirtualQuery( addr, &info, sizeof( info ) );
|
||||
if( sLen != sizeof( info ) )
|
||||
return NULL;
|
||||
hModule = (HMODULE) info.AllocationBase;
|
||||
}
|
||||
|
||||
return hModule;
|
||||
}
|
||||
|
||||
/* Load Psapi.dll at runtime, this avoids linking caveat */
|
||||
static BOOL MyEnumProcessModules( HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded )
|
||||
{
|
||||
static BOOL (WINAPI *EnumProcessModulesPtr)(HANDLE, HMODULE *, DWORD, LPDWORD) = NULL;
|
||||
static BOOL failed = FALSE;
|
||||
UINT uMode;
|
||||
HMODULE psapi;
|
||||
|
||||
if( failed )
|
||||
return FALSE;
|
||||
|
||||
if( EnumProcessModulesPtr == NULL )
|
||||
{
|
||||
/* Windows 7 and newer versions have K32EnumProcessModules in Kernel32.dll which is always pre-loaded */
|
||||
psapi = GetModuleHandleA( "Kernel32.dll" );
|
||||
if( psapi != NULL )
|
||||
EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress( psapi, "K32EnumProcessModules" );
|
||||
|
||||
/* Windows Vista and older version have EnumProcessModules in Psapi.dll which needs to be loaded */
|
||||
if( EnumProcessModulesPtr == NULL )
|
||||
{
|
||||
/* Do not let Windows display the critical-error-handler message box */
|
||||
uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
|
||||
psapi = LoadLibraryA( "Psapi.dll" );
|
||||
if( psapi != NULL )
|
||||
{
|
||||
EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress( psapi, "EnumProcessModules" );
|
||||
if( EnumProcessModulesPtr == NULL )
|
||||
FreeLibrary( psapi );
|
||||
}
|
||||
SetErrorMode( uMode );
|
||||
}
|
||||
|
||||
if( EnumProcessModulesPtr == NULL )
|
||||
{
|
||||
failed = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return EnumProcessModulesPtr( hProcess, lphModule, cb, lpcbNeeded );
|
||||
}
|
||||
|
||||
DLFCN_EXPORT
|
||||
void *dlopen( const char *file, int mode )
|
||||
{
|
||||
HMODULE hModule;
|
||||
UINT uMode;
|
||||
|
||||
error_occurred = FALSE;
|
||||
|
||||
/* Do not let Windows display the critical-error-handler message box */
|
||||
uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
|
||||
|
||||
if( file == NULL )
|
||||
{
|
||||
/* POSIX says that if the value of file is NULL, a handle on a global
|
||||
* symbol object must be provided. That object must be able to access
|
||||
* all symbols from the original program file, and any objects loaded
|
||||
* with the RTLD_GLOBAL flag.
|
||||
* The return value from GetModuleHandle( ) allows us to retrieve
|
||||
* symbols only from the original program file. EnumProcessModules() is
|
||||
* used to access symbols from other libraries. For objects loaded
|
||||
* with the RTLD_LOCAL flag, we create our own list later on. They are
|
||||
* excluded from EnumProcessModules() iteration.
|
||||
*/
|
||||
hModule = GetModuleHandle( NULL );
|
||||
|
||||
if( !hModule )
|
||||
save_err_str( "(null)", GetLastError( ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
HANDLE hCurrentProc;
|
||||
DWORD dwProcModsBefore, dwProcModsAfter;
|
||||
char lpFileName[MAX_PATH];
|
||||
size_t i, len;
|
||||
|
||||
len = strlen( file );
|
||||
|
||||
if( len >= sizeof( lpFileName ) )
|
||||
{
|
||||
save_err_str( file, ERROR_FILENAME_EXCED_RANGE );
|
||||
hModule = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* MSDN says backslashes *must* be used instead of forward slashes. */
|
||||
for( i = 0; i < len; i++ )
|
||||
{
|
||||
if( file[i] == '/' )
|
||||
lpFileName[i] = '\\';
|
||||
else
|
||||
lpFileName[i] = file[i];
|
||||
}
|
||||
lpFileName[len] = '\0';
|
||||
|
||||
hCurrentProc = GetCurrentProcess( );
|
||||
|
||||
if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsBefore ) == 0 )
|
||||
dwProcModsBefore = 0;
|
||||
|
||||
/* POSIX says the search path is implementation-defined.
|
||||
* LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely
|
||||
* to UNIX's search paths (start with system folders instead of current
|
||||
* folder).
|
||||
*/
|
||||
hModule = LoadLibraryExA( lpFileName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
|
||||
|
||||
if( !hModule )
|
||||
{
|
||||
save_err_str( lpFileName, GetLastError( ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsAfter ) == 0 )
|
||||
dwProcModsAfter = 0;
|
||||
|
||||
/* If the object was loaded with RTLD_LOCAL, add it to list of local
|
||||
* objects, so that its symbols cannot be retrieved even if the handle for
|
||||
* the original program file is passed. POSIX says that if the same
|
||||
* file is specified in multiple invocations, and any of them are
|
||||
* RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the
|
||||
* symbols will remain global. If number of loaded modules was not
|
||||
* changed after calling LoadLibraryEx(), it means that library was
|
||||
* already loaded.
|
||||
*/
|
||||
if( (mode & RTLD_LOCAL) && dwProcModsBefore != dwProcModsAfter )
|
||||
{
|
||||
if( !local_add( hModule ) )
|
||||
{
|
||||
save_err_str( lpFileName, ERROR_NOT_ENOUGH_MEMORY );
|
||||
FreeLibrary( hModule );
|
||||
hModule = NULL;
|
||||
}
|
||||
}
|
||||
else if( !(mode & RTLD_LOCAL) && dwProcModsBefore == dwProcModsAfter )
|
||||
{
|
||||
local_rem( hModule );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return to previous state of the error-mode bit flags. */
|
||||
SetErrorMode( uMode );
|
||||
|
||||
return (void *) hModule;
|
||||
}
|
||||
|
||||
DLFCN_EXPORT
|
||||
int dlclose( void *handle )
|
||||
{
|
||||
HMODULE hModule = (HMODULE) handle;
|
||||
BOOL ret;
|
||||
|
||||
error_occurred = FALSE;
|
||||
|
||||
ret = FreeLibrary( hModule );
|
||||
|
||||
/* If the object was loaded with RTLD_LOCAL, remove it from list of local
|
||||
* objects.
|
||||
*/
|
||||
if( ret )
|
||||
local_rem( hModule );
|
||||
else
|
||||
save_err_ptr_str( handle, GetLastError( ) );
|
||||
|
||||
/* dlclose's return value in inverted in relation to FreeLibrary's. */
|
||||
ret = !ret;
|
||||
|
||||
return (int) ret;
|
||||
}
|
||||
|
||||
DLFCN_NOINLINE /* Needed for _ReturnAddress() */
|
||||
DLFCN_EXPORT
|
||||
void *dlsym( void *handle, const char *name )
|
||||
{
|
||||
FARPROC symbol;
|
||||
HMODULE hCaller;
|
||||
HMODULE hModule;
|
||||
DWORD dwMessageId;
|
||||
|
||||
error_occurred = FALSE;
|
||||
|
||||
symbol = NULL;
|
||||
hCaller = NULL;
|
||||
hModule = GetModuleHandle( NULL );
|
||||
dwMessageId = 0;
|
||||
|
||||
if( handle == RTLD_DEFAULT )
|
||||
{
|
||||
/* The symbol lookup happens in the normal global scope; that is,
|
||||
* a search for a symbol using this handle would find the same
|
||||
* definition as a direct use of this symbol in the program code.
|
||||
* So use same lookup procedure as when filename is NULL.
|
||||
*/
|
||||
handle = hModule;
|
||||
}
|
||||
else if( handle == RTLD_NEXT )
|
||||
{
|
||||
/* Specifies the next object after this one that defines name.
|
||||
* This one refers to the object containing the invocation of dlsym().
|
||||
* The next object is the one found upon the application of a load
|
||||
* order symbol resolution algorithm. To get caller function of dlsym()
|
||||
* use _ReturnAddress() intrinsic. To get HMODULE of caller function
|
||||
* use MyGetModuleHandleFromAddress() which calls either standard
|
||||
* GetModuleHandleExA() function or hack via VirtualQuery().
|
||||
*/
|
||||
hCaller = MyGetModuleHandleFromAddress( _ReturnAddress( ) );
|
||||
|
||||
if( hCaller == NULL )
|
||||
{
|
||||
dwMessageId = ERROR_INVALID_PARAMETER;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
if( handle != RTLD_NEXT )
|
||||
{
|
||||
symbol = GetProcAddress( (HMODULE) handle, name );
|
||||
|
||||
if( symbol != NULL )
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* If the handle for the original program file is passed, also search
|
||||
* in all globally loaded objects.
|
||||
*/
|
||||
|
||||
if( hModule == handle || handle == RTLD_NEXT )
|
||||
{
|
||||
HANDLE hCurrentProc;
|
||||
HMODULE *modules;
|
||||
DWORD cbNeeded;
|
||||
DWORD dwSize;
|
||||
size_t i;
|
||||
|
||||
hCurrentProc = GetCurrentProcess( );
|
||||
|
||||
/* GetModuleHandle( NULL ) only returns the current program file. So
|
||||
* if we want to get ALL loaded module including those in linked DLLs,
|
||||
* we have to use EnumProcessModules( ).
|
||||
*/
|
||||
if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwSize ) != 0 )
|
||||
{
|
||||
modules = malloc( dwSize );
|
||||
if( modules )
|
||||
{
|
||||
if( MyEnumProcessModules( hCurrentProc, modules, dwSize, &cbNeeded ) != 0 && dwSize == cbNeeded )
|
||||
{
|
||||
for( i = 0; i < dwSize / sizeof( HMODULE ); i++ )
|
||||
{
|
||||
if( handle == RTLD_NEXT && hCaller )
|
||||
{
|
||||
/* Next modules can be used for RTLD_NEXT */
|
||||
if( hCaller == modules[i] )
|
||||
hCaller = NULL;
|
||||
continue;
|
||||
}
|
||||
if( local_search( modules[i] ) )
|
||||
continue;
|
||||
symbol = GetProcAddress( modules[i], name );
|
||||
if( symbol != NULL )
|
||||
{
|
||||
free( modules );
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
free( modules );
|
||||
}
|
||||
else
|
||||
{
|
||||
dwMessageId = ERROR_NOT_ENOUGH_MEMORY;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if( symbol == NULL )
|
||||
{
|
||||
if( !dwMessageId )
|
||||
dwMessageId = ERROR_PROC_NOT_FOUND;
|
||||
save_err_str( name, dwMessageId );
|
||||
}
|
||||
|
||||
return *(void **) (&symbol);
|
||||
}
|
||||
|
||||
DLFCN_EXPORT
|
||||
char *dlerror( void )
|
||||
{
|
||||
/* If this is the second consecutive call to dlerror, return NULL */
|
||||
if( !error_occurred )
|
||||
return NULL;
|
||||
|
||||
/* POSIX says that invoking dlerror( ) a second time, immediately following
|
||||
* a prior invocation, shall result in NULL being returned.
|
||||
*/
|
||||
error_occurred = FALSE;
|
||||
|
||||
return error_buffer;
|
||||
}
|
||||
|
||||
/* See https://docs.microsoft.com/en-us/archive/msdn-magazine/2002/march/inside-windows-an-in-depth-look-into-the-win32-portable-executable-file-format-part-2
|
||||
* for details */
|
||||
|
||||
/* Get specific image section */
|
||||
static BOOL get_image_section( HMODULE module, int index, void **ptr, DWORD *size )
|
||||
{
|
||||
IMAGE_DOS_HEADER *dosHeader;
|
||||
IMAGE_OPTIONAL_HEADER *optionalHeader;
|
||||
|
||||
dosHeader = (IMAGE_DOS_HEADER *) module;
|
||||
|
||||
if( dosHeader->e_magic != 0x5A4D )
|
||||
return FALSE;
|
||||
|
||||
optionalHeader = (IMAGE_OPTIONAL_HEADER *) ( (BYTE *) module + dosHeader->e_lfanew + 24 );
|
||||
|
||||
if( optionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC )
|
||||
return FALSE;
|
||||
|
||||
if( index < 0 || index > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR )
|
||||
return FALSE;
|
||||
|
||||
if( optionalHeader->DataDirectory[index].Size == 0 || optionalHeader->DataDirectory[index].VirtualAddress == 0 )
|
||||
return FALSE;
|
||||
|
||||
if( size != NULL )
|
||||
*size = optionalHeader->DataDirectory[index].Size;
|
||||
|
||||
*ptr = (void *)( (BYTE *) module + optionalHeader->DataDirectory[index].VirtualAddress );
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Return symbol name for a given address from export table */
|
||||
static const char *get_export_symbol_name( HMODULE module, IMAGE_EXPORT_DIRECTORY *ied, void *addr, void **func_address )
|
||||
{
|
||||
DWORD i;
|
||||
void *candidateAddr = NULL;
|
||||
int candidateIndex = -1;
|
||||
BYTE *base = (BYTE *) module;
|
||||
DWORD *functionAddressesOffsets = (DWORD *) (base + ied->AddressOfFunctions);
|
||||
DWORD *functionNamesOffsets = (DWORD *) (base + ied->AddressOfNames);
|
||||
USHORT *functionNameOrdinalsIndexes = (USHORT *) (base + ied->AddressOfNameOrdinals);
|
||||
|
||||
for( i = 0; i < ied->NumberOfFunctions; i++ )
|
||||
{
|
||||
if( (void *) ( base + functionAddressesOffsets[i] ) > addr || candidateAddr >= (void *) ( base + functionAddressesOffsets[i] ) )
|
||||
continue;
|
||||
|
||||
candidateAddr = (void *) ( base + functionAddressesOffsets[i] );
|
||||
candidateIndex = i;
|
||||
}
|
||||
|
||||
if( candidateIndex == -1 )
|
||||
return NULL;
|
||||
|
||||
*func_address = candidateAddr;
|
||||
|
||||
for( i = 0; i < ied->NumberOfNames; i++ )
|
||||
{
|
||||
if( functionNameOrdinalsIndexes[i] == candidateIndex )
|
||||
return (const char *) ( base + functionNamesOffsets[i] );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static BOOL is_valid_address( void *addr )
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION info;
|
||||
SIZE_T result;
|
||||
|
||||
if( addr == NULL )
|
||||
return FALSE;
|
||||
|
||||
/* check valid pointer */
|
||||
result = VirtualQuery( addr, &info, sizeof( info ) );
|
||||
|
||||
if( result == 0 || info.AllocationBase == NULL || info.AllocationProtect == 0 || info.AllocationProtect == PAGE_NOACCESS )
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Return state if address points to an import thunk
|
||||
*
|
||||
* An import thunk is setup with a 'jmp' instruction followed by an
|
||||
* absolute address (32bit) or relative offset (64bit) pointing into
|
||||
* the import address table (iat), which is partially maintained by
|
||||
* the runtime linker.
|
||||
*/
|
||||
static BOOL is_import_thunk( void *addr )
|
||||
{
|
||||
return *(short *) addr == 0x25ff ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
/* Return adress from the import address table (iat),
|
||||
* if the original address points to a thunk table entry.
|
||||
*/
|
||||
static void *get_address_from_import_address_table( void *iat, DWORD iat_size, void *addr )
|
||||
{
|
||||
BYTE *thkp = (BYTE *) addr;
|
||||
/* Get offset from thunk table (after instruction 0xff 0x25)
|
||||
* 4018c8 <_VirtualQuery>: ff 25 4a 8a 00 00
|
||||
*/
|
||||
ULONG offset = *(ULONG *)( thkp + 2 );
|
||||
#ifdef _WIN64
|
||||
/* On 64 bit the offset is relative
|
||||
* 4018c8: ff 25 4a 8a 00 00 jmpq *0x8a4a(%rip) # 40a318 <__imp_VirtualQuery>
|
||||
* And can be also negative (MSVC in WDK)
|
||||
* 100002f20: ff 25 3a e1 ff ff jmpq *-0x1ec6(%rip) # 0x100001060
|
||||
* So cast to signed LONG type
|
||||
*/
|
||||
BYTE *ptr = (BYTE *)( thkp + 6 + (LONG) offset );
|
||||
#else
|
||||
/* On 32 bit the offset is absolute
|
||||
* 4019b4: ff 25 90 71 40 00 jmp *0x40719
|
||||
*/
|
||||
BYTE *ptr = (BYTE *) offset;
|
||||
#endif
|
||||
|
||||
if( !is_valid_address( ptr ) || ptr < (BYTE *) iat || ptr > (BYTE *) iat + iat_size )
|
||||
return NULL;
|
||||
|
||||
return *(void **) ptr;
|
||||
}
|
||||
|
||||
/* Holds module filename */
|
||||
static char module_filename[2*MAX_PATH];
|
||||
|
||||
static BOOL fill_info( void *addr, Dl_info *info )
|
||||
{
|
||||
HMODULE hModule;
|
||||
DWORD dwSize;
|
||||
IMAGE_EXPORT_DIRECTORY *ied;
|
||||
void *funcAddress = NULL;
|
||||
|
||||
/* Get module of the specified address */
|
||||
hModule = MyGetModuleHandleFromAddress( addr );
|
||||
|
||||
if( hModule == NULL )
|
||||
return FALSE;
|
||||
|
||||
dwSize = GetModuleFileNameA( hModule, module_filename, sizeof( module_filename ) );
|
||||
|
||||
if( dwSize == 0 || dwSize == sizeof( module_filename ) )
|
||||
return FALSE;
|
||||
|
||||
info->dli_fname = module_filename;
|
||||
info->dli_fbase = (void *) hModule;
|
||||
|
||||
/* Find function name and function address in module's export table */
|
||||
if( get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_EXPORT, (void **) &ied, NULL ) )
|
||||
info->dli_sname = get_export_symbol_name( hModule, ied, addr, &funcAddress );
|
||||
else
|
||||
info->dli_sname = NULL;
|
||||
|
||||
info->dli_saddr = info->dli_sname == NULL ? NULL : funcAddress != NULL ? funcAddress : addr;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
DLFCN_EXPORT
|
||||
int dladdr( void *addr, Dl_info *info )
|
||||
{
|
||||
if( info == NULL )
|
||||
return 0;
|
||||
|
||||
if( !is_valid_address( addr ) )
|
||||
return 0;
|
||||
|
||||
if( is_import_thunk( addr ) )
|
||||
{
|
||||
void *iat;
|
||||
DWORD iatSize;
|
||||
HMODULE hModule;
|
||||
|
||||
/* Get module of the import thunk address */
|
||||
hModule = MyGetModuleHandleFromAddress( addr );
|
||||
|
||||
if( hModule == NULL )
|
||||
return 0;
|
||||
|
||||
if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IAT, &iat, &iatSize ) )
|
||||
{
|
||||
/* Fallback for cases where the iat is not defined,
|
||||
* for example i586-mingw32msvc-gcc */
|
||||
IMAGE_IMPORT_DESCRIPTOR *iid;
|
||||
DWORD iidSize;
|
||||
|
||||
if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, &iidSize ) )
|
||||
return 0;
|
||||
|
||||
if( iid == NULL || iid->Characteristics == 0 || iid->FirstThunk == 0 )
|
||||
return 0;
|
||||
|
||||
iat = (void *)( (BYTE *) hModule + iid->FirstThunk );
|
||||
/* We assume that in this case iid and iat's are in linear order */
|
||||
iatSize = iidSize - (DWORD) ( (BYTE *) iat - (BYTE *) iid );
|
||||
}
|
||||
|
||||
addr = get_address_from_import_address_table( iat, iatSize, addr );
|
||||
|
||||
if( !is_valid_address( addr ) )
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( !fill_info( addr, info ) )
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef DLFCN_WIN32_SHARED
|
||||
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
|
||||
{
|
||||
(void) hinstDLL;
|
||||
(void) fdwReason;
|
||||
(void) lpvReserved;
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
94
cli/modules/thirdparty/dlfcn-win32/dlfcn.h
vendored
Normal file
94
cli/modules/thirdparty/dlfcn-win32/dlfcn.h
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* dlfcn-win32
|
||||
* Copyright (c) 2007 Ramiro Polla
|
||||
*
|
||||
* 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 DLFCN_H
|
||||
#define DLFCN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(DLFCN_WIN32_SHARED)
|
||||
#if defined(DLFCN_WIN32_EXPORTS)
|
||||
# define DLFCN_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
# define DLFCN_EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
# define DLFCN_EXPORT
|
||||
#endif
|
||||
|
||||
/* Relocations are performed when the object is loaded. */
|
||||
#define RTLD_NOW 0
|
||||
|
||||
/* Relocations are performed at an implementation-defined time.
|
||||
* Windows API does not support lazy symbol resolving (when first reference
|
||||
* to a given symbol occurs). So RTLD_LAZY implementation is same as RTLD_NOW.
|
||||
*/
|
||||
#define RTLD_LAZY RTLD_NOW
|
||||
|
||||
/* All symbols are available for relocation processing of other modules. */
|
||||
#define RTLD_GLOBAL (1 << 1)
|
||||
|
||||
/* All symbols are not made available for relocation processing by other modules. */
|
||||
#define RTLD_LOCAL (1 << 2)
|
||||
|
||||
/* These two were added in The Open Group Base Specifications Issue 6.
|
||||
* Note: All other RTLD_* flags in any dlfcn.h are not standard compliant.
|
||||
*/
|
||||
|
||||
/* The symbol lookup happens in the normal global scope. */
|
||||
#define RTLD_DEFAULT ((void *)0)
|
||||
|
||||
/* Specifies the next object after this one that defines name. */
|
||||
#define RTLD_NEXT ((void *)-1)
|
||||
|
||||
/* Structure filled in by dladdr() */
|
||||
typedef struct dl_info
|
||||
{
|
||||
const char *dli_fname; /* Filename of defining object (thread unsafe and reused on every call to dladdr) */
|
||||
void *dli_fbase; /* Load address of that object */
|
||||
const char *dli_sname; /* Name of nearest lower symbol */
|
||||
void *dli_saddr; /* Exact value of nearest symbol */
|
||||
} Dl_info;
|
||||
|
||||
/* Open a symbol table handle. */
|
||||
DLFCN_EXPORT void *dlopen(const char *file, int mode);
|
||||
|
||||
/* Close a symbol table handle. */
|
||||
DLFCN_EXPORT int dlclose(void *handle);
|
||||
|
||||
/* Get the address of a symbol from a symbol table handle. */
|
||||
DLFCN_EXPORT void *dlsym(void *handle, const char *name);
|
||||
|
||||
/* Get diagnostic information. */
|
||||
DLFCN_EXPORT char *dlerror(void);
|
||||
|
||||
/* Translate address to symbolic information (no POSIX standard) */
|
||||
DLFCN_EXPORT int dladdr(void *addr, Dl_info *info);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DLFCN_H */
|
122
cli/native.py
Normal file
122
cli/native.py
Normal file
@ -0,0 +1,122 @@
|
||||
#!python
|
||||
## Copyright (c) 2020-2021 Thakee Nathees
|
||||
## Copyright (c) 2021-2022 Pocketlang Contributors
|
||||
## Distributed Under The MIT License
|
||||
|
||||
import re, os
|
||||
from os.path import (join, exists, abspath,
|
||||
relpath, dirname)
|
||||
|
||||
## The absolute path of this file, when run as a script.
|
||||
## This file is not intended to be included in other files at the moment.
|
||||
THIS_PATH = abspath(dirname(__file__))
|
||||
|
||||
POCKET_HEADER = join(THIS_PATH, "../src/include/pocketlang.h")
|
||||
TARGET = join(THIS_PATH, "./modules/pknative.gen.c")
|
||||
|
||||
PK_API = "pk_api"
|
||||
PK_API_TYPE = "PkNativeApi"
|
||||
API_DEF = f'''\
|
||||
static {PK_API_TYPE} {PK_API};
|
||||
void pkInitApi({PK_API_TYPE}* api) {{%s
|
||||
}}
|
||||
'''
|
||||
|
||||
SOURCE_GEN = f'''\
|
||||
/*
|
||||
* Copyright (c) 2020-2022 Thakee Nathees
|
||||
* Copyright (c) 2021-2022 Pocketlang Contributors
|
||||
* Distributed Under The MIT License
|
||||
*/
|
||||
|
||||
#include <pocketlang.h>
|
||||
|
||||
// !! THIS FILE IS GENERATED DO NOT EDIT !!
|
||||
|
||||
'''
|
||||
|
||||
def get_source():
|
||||
f = open(POCKET_HEADER, 'r')
|
||||
source = f.read()
|
||||
f.close()
|
||||
start = source.find("POCKETLANG PUBLIC API")
|
||||
source = source[start:]
|
||||
return source
|
||||
|
||||
MULTIPLE_WHITE_SPACES = re.compile(r"\s+")
|
||||
def flaten(s):
|
||||
return MULTIPLE_WHITE_SPACES.sub(" ", s).strip()
|
||||
|
||||
def parse_definition(_def):
|
||||
assert '\n' not in _def
|
||||
pattern = r'PK_PUBLIC (.*?) ([a-zA-Z0-9_]+)\('
|
||||
match = re.search(pattern, _def)
|
||||
assert(match)
|
||||
return_type = match.group(1)
|
||||
fn_name = match.group(2)
|
||||
params = []
|
||||
if '()' in _def or '(void)' in _def:
|
||||
pass ## No parameters.
|
||||
else:
|
||||
params_match = _def[_def.find('(')+1:_def.find(')')]
|
||||
for param in params_match.split(','):
|
||||
last_space = param.rfind(' ')
|
||||
param_type = param[:last_space].strip()
|
||||
param_name = param[last_space:].strip()
|
||||
params.append((param_name, param_type))
|
||||
return fn_name, params, return_type
|
||||
|
||||
def get_api_functions():
|
||||
api_functions = []
|
||||
source = get_source()
|
||||
match = re.findall(r'^PK_PUBLIC [\S\n ]+?;', source, re.MULTILINE)
|
||||
for m in match:
|
||||
definition = flaten(m)
|
||||
api_functions.append(parse_definition(definition))
|
||||
return api_functions
|
||||
|
||||
def fn_typedefs(api_functions):
|
||||
typedefs = ""
|
||||
for fn, params, ret in api_functions:
|
||||
params_join = ", ".join(map(lambda x: x[1], params))
|
||||
typedefs += f'typedef {ret} (*{fn}_t)({params_join});\n'
|
||||
return typedefs + '\n'
|
||||
|
||||
def api_typedef(api_functions):
|
||||
typedef = "typedef struct {\n"
|
||||
for fn, params, ret in api_functions:
|
||||
typedef += f" {fn}_t {fn}_ptr;\n"
|
||||
typedef += f"}} {PK_API_TYPE};\n"
|
||||
return typedef + '\n'
|
||||
|
||||
def define_functions(api_functions):
|
||||
definitions = ""
|
||||
for fn, params, ret in api_functions:
|
||||
fn_def = ""
|
||||
params_join = ", ".join(map(lambda x: x[1] + " " + x[0], params))
|
||||
param_names_join = ", ".join(map(lambda x: x[0], params))
|
||||
return_ = "" if ret == "void" else "return "
|
||||
fn_def += f'{ret} {fn}({params_join}) {{\n'
|
||||
fn_def += f" {return_}{PK_API}.{fn}_ptr({param_names_join});\n"
|
||||
fn_def += "}\n\n"
|
||||
definitions += fn_def
|
||||
return definitions[:-1] ## Skip the last newline.
|
||||
|
||||
def init_api(api_functions):
|
||||
assign = ""
|
||||
for fn, params, ret in api_functions:
|
||||
assign += f"\n {PK_API}.{fn}_ptr = api->{fn}_ptr;"
|
||||
return API_DEF % assign + '\n'
|
||||
|
||||
def generate():
|
||||
api_functions = get_api_functions()
|
||||
with open(TARGET, 'w') as fp:
|
||||
fp.write(SOURCE_GEN)
|
||||
fp.write(fn_typedefs(api_functions))
|
||||
fp.write(api_typedef(api_functions))
|
||||
fp.write(init_api(api_functions))
|
||||
fp.write(define_functions(api_functions))
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate()
|
||||
print("Generated:", relpath(TARGET, os.getcwd()))
|
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020-2022 Thakee Nathees
|
||||
* Copyright (c) 2021-2022 Pocketlang Contributors
|
||||
* Distributed Under The MIT License
|
||||
*/
|
||||
|
||||
// This file will include all thirdparty source files from the thirdparty sub
|
||||
// directories here into a single file. That'll make it easy to compile with
|
||||
// the command `gcc cli/*.c ...` instead of having to add every single sub
|
||||
// directory to the list of source directory.
|
||||
|
||||
// Library : cwalk
|
||||
// Source : https://github.com/likle/cwalk/
|
||||
// Doc : https://likle.github.io/cwalk/
|
||||
// About : Path library for C/C++. Cross-Platform for Windows, MacOS and
|
||||
// Linux. Supports UNIX and Windows path styles on those platforms.
|
||||
#include "thirdparty/cwalk/cwalk.c"
|
||||
|
||||
// Library : argparse
|
||||
// Source : https://github.com/cofyc/argparse/
|
||||
// About : Command-line arguments parsing library.
|
||||
#include "thirdparty/argparse/argparse.c"
|
@ -2070,6 +2070,7 @@ static int compilerAddVariable(Compiler* compiler, const char* name,
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void compilerAddForward(Compiler* compiler, int instruction, Fn* fn,
|
||||
@ -2655,6 +2656,7 @@ static int compilerImportName(Compiler* compiler, int line,
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// This will called by the compilerImportAll() function to import a single
|
||||
|
@ -40,6 +40,7 @@ PkVarType pkGetValueType(const PkVar value) {
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return PK_NULL;
|
||||
}
|
||||
|
||||
PkHandle* pkNewString(PKVM* vm, const char* value) {
|
||||
@ -872,6 +873,7 @@ static uint32_t _hashObject(Object* obj) {
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t varHashValue(Var v) {
|
||||
@ -1282,6 +1284,7 @@ bool instGetAttrib(PKVM* vm, Instance* inst, String* attrib, Var* value) {
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool instSetAttrib(PKVM* vm, Instance* inst, String* attrib, Var value) {
|
||||
@ -1338,6 +1341,7 @@ bool instSetAttrib(PKVM* vm, Instance* inst, String* attrib, Var value) {
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -1365,6 +1369,7 @@ const char* getPkVarTypeName(PkVarType type) {
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* getObjectTypeName(ObjectType type) {
|
||||
@ -1382,6 +1387,7 @@ const char* getObjectTypeName(ObjectType type) {
|
||||
case OBJ_INST: return "Inst";
|
||||
}
|
||||
UNREACHABLE();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* varTypeName(Var v) {
|
||||
@ -1774,4 +1780,5 @@ bool toBool(Var v) {
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
|
@ -1664,5 +1664,6 @@ L_vm_main_loop:
|
||||
|
||||
}
|
||||
|
||||
UNREACHABLE(); //return PK_RESULT_SUCCESS;
|
||||
UNREACHABLE();
|
||||
return PK_RESULT_RUNTIME_ERROR;
|
||||
}
|
||||
|
@ -8,22 +8,12 @@
|
||||
|
||||
import os, sys, re
|
||||
from os import listdir
|
||||
from os.path import join, abspath, dirname, relpath
|
||||
from os.path import join, abspath, dirname, relpath, normpath
|
||||
|
||||
## The absolute path of this file, when run as a script.
|
||||
## This file is not intended to be included in other files at the moment.
|
||||
THIS_PATH = abspath(dirname(__file__))
|
||||
|
||||
## Converts a list of relative paths from the working directory
|
||||
## to a list of relative paths from this file's absolute directory.
|
||||
def to_abs_paths(sources):
|
||||
return map(lambda s: os.path.join(THIS_PATH, s), sources)
|
||||
|
||||
## Converts the path from absolute path to relative path from the
|
||||
## toplelve of the project.
|
||||
def to_rel_path(path):
|
||||
return relpath(path, join(THIS_PATH, '..'))
|
||||
|
||||
## A list of source files, to check if the fnv1a hash values match it's
|
||||
## corresponding cstring in the CASE_ATTRIB(name, hash) macro calls.
|
||||
HASH_CHECK_LIST = [
|
||||
@ -37,13 +27,20 @@ CHECK_EXTENTIONS = ('.c', '.h', '.py', '.pk', '.js')
|
||||
|
||||
## A list of strings, if a line contains it we allow it to be longer than
|
||||
## 79 characters, It's not "the correct way" but it works.
|
||||
ALLOW_LONG = ('http://', 'https://', '<script ', '<link ', '<svg ')
|
||||
ALLOW_LONG_LINES = ('http://', 'https://', '<script ', '<link ', '<svg ')
|
||||
|
||||
## A list of files that are allowed to be longer than 79 characters.
|
||||
ALLOW_LONG_FILES = (
|
||||
"../cli/native.py",
|
||||
"../cli/modules/pknative.gen.c",
|
||||
)
|
||||
|
||||
## A list of directory, contains C source files to perform static checks.
|
||||
## This will include all files with extension from CHECK_EXTENTIONS.
|
||||
SOURCE_DIRS = [
|
||||
"../src/",
|
||||
"../cli/",
|
||||
"../cli/modules/",
|
||||
|
||||
"../docs/",
|
||||
"../docs/wasm/",
|
||||
@ -52,6 +49,16 @@ SOURCE_DIRS = [
|
||||
## This global variable will be set to true if any check failed.
|
||||
checks_failed = False
|
||||
|
||||
## Converts a list of relative paths from the working directory
|
||||
## to a list of relative paths from this file's absolute directory.
|
||||
def to_abs_paths(sources):
|
||||
return map(lambda s: os.path.join(THIS_PATH, s), sources)
|
||||
|
||||
## Converts the path from absolute path to relative path from the
|
||||
## toplelve of the project.
|
||||
def to_rel_path(path):
|
||||
return relpath(path, join(THIS_PATH, '..'))
|
||||
|
||||
def main():
|
||||
check_fnv1_hash(to_abs_paths(HASH_CHECK_LIST))
|
||||
check_static(to_abs_paths(SOURCE_DIRS))
|
||||
@ -90,7 +97,8 @@ def check_static(dirs):
|
||||
if not file.endswith(CHECK_EXTENTIONS): continue
|
||||
if os.path.isdir(join(dir, file)): continue
|
||||
|
||||
fp = open(join(dir, file), 'r')
|
||||
curr_file = normpath(join(dir, file))
|
||||
fp = open(curr_file, 'r')
|
||||
|
||||
## Path of the file relative to top-level.
|
||||
file_path = to_rel_path(join(dir, file))
|
||||
@ -112,8 +120,15 @@ def check_static(dirs):
|
||||
|
||||
if len(line) >= 80:
|
||||
skip = False
|
||||
for ignore in ALLOW_LONG:
|
||||
if ignore in line: skip = True; break
|
||||
for ignore in ALLOW_LONG_LINES:
|
||||
if ignore in line:
|
||||
skip = True
|
||||
break
|
||||
for ignore in ALLOW_LONG_FILES:
|
||||
## TODO: the bellow normpath(join()) should be calcuated once.
|
||||
if curr_file == normpath(join(THIS_PATH, ignore)):
|
||||
skip = True
|
||||
break
|
||||
if skip: continue
|
||||
|
||||
_location = location(file_path, line_no)
|
||||
|
Loading…
Reference in New Issue
Block a user