mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 19:43:32 +00:00
e16a7d8f3b
`et` means `expandtab`. ```sh rg 'vi: .* :vi' -l -0 | \ xargs -0 sed -i '' 's/vi: \(.*\) et\(.*\) :vi/vi: \1 xoet\2:vi/' rg 'vi: .* :vi' -l -0 | \ xargs -0 sed -i '' 's/vi: \(.*\)noet\(.*\):vi/vi: \1et\2 :vi/' rg 'vi: .* :vi' -l -0 | \ xargs -0 sed -i '' 's/vi: \(.*\)xoet\(.*\):vi/vi: \1noet\2:vi/' ```
548 lines
16 KiB
C
548 lines
16 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
│ │
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/fmt/conv.h"
|
|
#include "libc/fmt/libgen.h"
|
|
#include "libc/serialize.h"
|
|
#include "libc/log/libfatal.internal.h"
|
|
#include "libc/mem/gc.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/stdio/append.h"
|
|
#include "libc/str/str.h"
|
|
#include "libc/x/xasprintf.h"
|
|
#include "third_party/chibicc/chibicc.h"
|
|
|
|
static void AppendStringLiteral(char **b, const char *s, const char *indent) {
|
|
int c, w, l, o;
|
|
for (o = l = 0;; l = c) {
|
|
switch ((c = *s++ & 255)) {
|
|
case 0:
|
|
return;
|
|
case '\r':
|
|
continue;
|
|
case '\t':
|
|
w = READ16LE("\\t");
|
|
break;
|
|
case '\n':
|
|
w = READ32LE("\\n\\\n");
|
|
break;
|
|
case '"':
|
|
w = READ16LE("\\\"");
|
|
break;
|
|
case '\\':
|
|
w = READ16LE("\\\\");
|
|
break;
|
|
case '`':
|
|
/* convert markdown <code> to restructured text */
|
|
if (o) {
|
|
o = 0;
|
|
w = READ16LE("''");
|
|
} else if (*s == '`') {
|
|
w = '`';
|
|
++s;
|
|
} else {
|
|
o = 1;
|
|
w = READ16LE("``");
|
|
}
|
|
break;
|
|
default:
|
|
if ((0x00 <= c && c <= 0x1F) || c == 0x7F || (c == '?' && l == '?')) {
|
|
w = '\\';
|
|
w |= ('0' + ((c & 0300) >> 6)) << 010;
|
|
w |= ('0' + ((c & 0070) >> 3)) << 020;
|
|
w |= ('0' + ((c & 0007) >> 0)) << 030;
|
|
} else {
|
|
w = c;
|
|
}
|
|
break;
|
|
}
|
|
appendw(b, w);
|
|
if (c == '\n' && indent) {
|
|
appends(b, indent);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void AppendJavadown(char **b, const struct Javadown *j) {
|
|
size_t i;
|
|
const char *s, *s2;
|
|
if (j->title && *j->title) {
|
|
AppendStringLiteral(b, j->title, 0);
|
|
if (j->text && *j->text) {
|
|
appendw(b, READ32LE("\\n\\\n"));
|
|
appendw(b, READ32LE("\\n\\\n"));
|
|
}
|
|
}
|
|
if (j->text && *j->text) {
|
|
AppendStringLiteral(b, j->text, 0);
|
|
}
|
|
if (j->tags.n) {
|
|
appendw(b, READ32LE("\\n\\\n"));
|
|
for (i = 0; i < j->tags.n; ++i) {
|
|
appendw(b, READ64LE("\\n\\\n:\0\0"));
|
|
AppendStringLiteral(b, j->tags.p[i].tag, 0);
|
|
s = j->tags.p[i].text;
|
|
if (!strcmp(j->tags.p[i].tag, "param") && s && (s2 = strchr(s, ' '))) {
|
|
appendw(b, ' ');
|
|
appendd(b, s, s2 - s);
|
|
s = s2 + 1;
|
|
}
|
|
appendw(b, ':');
|
|
if (s && *s) {
|
|
appendw(b, ' ');
|
|
AppendStringLiteral(b, s, " ");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void AppendScalar(char **b, struct Type *ty, char *name, int i) {
|
|
if (ty->is_atomic) appendw(b, READ64LE("_Atomic "));
|
|
if (i && ty->is_const) appendw(b, READ64LE("const \0"));
|
|
if (ty->is_unsigned) appends(b, "unsigned ");
|
|
appends(b, name);
|
|
}
|
|
|
|
static void AppendType(char **b, struct Type *ty, int i) {
|
|
switch (ty->kind) {
|
|
case TY_VOID:
|
|
appends(b, "void");
|
|
break;
|
|
case TY_BOOL:
|
|
appends(b, "_Bool");
|
|
break;
|
|
case TY_CHAR:
|
|
AppendScalar(b, ty, "char", i);
|
|
break;
|
|
case TY_SHORT:
|
|
AppendScalar(b, ty, "short", i);
|
|
break;
|
|
case TY_INT:
|
|
case TY_ENUM:
|
|
AppendScalar(b, ty, "int", i);
|
|
break;
|
|
case TY_LONG:
|
|
AppendScalar(b, ty, "long", i);
|
|
break;
|
|
case TY_INT128:
|
|
AppendScalar(b, ty, "__int128", i);
|
|
break;
|
|
case TY_FLOAT:
|
|
AppendScalar(b, ty, "float", i);
|
|
break;
|
|
case TY_DOUBLE:
|
|
AppendScalar(b, ty, "double", i);
|
|
break;
|
|
case TY_LDOUBLE:
|
|
AppendScalar(b, ty, "long double", i);
|
|
break;
|
|
case TY_FUNC:
|
|
AppendType(b, ty->return_ty, i);
|
|
appends(b, " (*)()");
|
|
break;
|
|
case TY_PTR:
|
|
if (!ty->array_len) {
|
|
AppendType(b, ty->base, i + 1);
|
|
if (ty->base->kind != TY_FUNC) {
|
|
appendw(b, '*');
|
|
if (i && ty->is_const) appendw(b, READ64LE(" const\0"));
|
|
if (ty->is_restrict) appends(b, " restrict");
|
|
}
|
|
break;
|
|
}
|
|
/* undecay */
|
|
case TY_ARRAY:
|
|
AppendType(b, ty->base, i + 1);
|
|
appendw(b, '[');
|
|
if (!i && ty->is_static) appendw(b, READ64LE("static "));
|
|
if (!i && ty->is_restrict) appends(b, "restrict ");
|
|
appendf(b, "%lu", ty->array_len);
|
|
appendw(b, ']');
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
static bool IsSupportedReturnType(struct Type *ty) {
|
|
switch (ty->kind) {
|
|
case TY_VOID:
|
|
case TY_BOOL:
|
|
case TY_CHAR:
|
|
case TY_SHORT:
|
|
case TY_INT:
|
|
case TY_LONG:
|
|
case TY_FLOAT:
|
|
case TY_DOUBLE:
|
|
return true;
|
|
case TY_PTR:
|
|
if (ty->base->kind == TY_CHAR) {
|
|
return !ty->base->is_unsigned;
|
|
} else {
|
|
return false;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool IsSupportedParameterType(struct Type *ty) {
|
|
switch (ty->kind) {
|
|
case TY_BOOL:
|
|
case TY_CHAR:
|
|
case TY_SHORT:
|
|
case TY_INT:
|
|
case TY_LONG:
|
|
case TY_FLOAT:
|
|
case TY_DOUBLE:
|
|
return true;
|
|
case TY_PTR:
|
|
if (ty->base->kind == TY_CHAR) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool Reject(char **b, struct Obj *obj, struct Type *ty,
|
|
const char *reason) {
|
|
appendf(b, "\n/* %s: %s: ", obj->name, reason);
|
|
AppendType(b, ty, 0);
|
|
appendw(b, READ32LE(" */\n"));
|
|
return false;
|
|
}
|
|
|
|
static bool IsFunctionSupported(char **b, struct Obj *func) {
|
|
Obj *param;
|
|
if (!IsSupportedReturnType(func->ty->return_ty)) {
|
|
return Reject(b, func, func->ty->return_ty, "unsupported return type");
|
|
}
|
|
for (param = func->params; param; param = param->next) {
|
|
if (!IsSupportedParameterType(param->ty)) {
|
|
return Reject(b, func, param->ty, "unsupported parameter type");
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static int GetParamDirective(struct Obj **param) {
|
|
bool is_unsigned;
|
|
is_unsigned = (*param)->ty->is_unsigned;
|
|
switch ((*param)->ty->kind) {
|
|
case TY_BOOL:
|
|
return 'p';
|
|
case TY_CHAR:
|
|
return is_unsigned ? 'B' : 'b';
|
|
case TY_SHORT:
|
|
return is_unsigned ? 'H' : 'h';
|
|
case TY_INT:
|
|
return is_unsigned ? 'I' : 'i';
|
|
case TY_LONG:
|
|
return is_unsigned ? 'L' : 'l';
|
|
case TY_FLOAT:
|
|
return 'f';
|
|
case TY_DOUBLE:
|
|
return 'd';
|
|
case TY_PTR:
|
|
if ((*param)->ty->base->kind == TY_CHAR) {
|
|
if ((*param)->ty->base->is_unsigned &&
|
|
((*param)->next && ((*param)->next->ty->kind == TY_LONG &&
|
|
(*param)->next->ty->is_unsigned))) {
|
|
*param = (*param)->next;
|
|
return READ16LE("y*");
|
|
} else {
|
|
return READ16LE("s*");
|
|
}
|
|
} else {
|
|
UNREACHABLE();
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
static char *GetParamIntermediate(struct Obj **param) {
|
|
bool is_unsigned;
|
|
is_unsigned = (*param)->ty->is_unsigned;
|
|
switch ((*param)->ty->kind) {
|
|
case TY_BOOL:
|
|
return "int";
|
|
case TY_CHAR:
|
|
return is_unsigned ? "unsigned char" : "signed char";
|
|
case TY_SHORT:
|
|
return is_unsigned ? "unsigned short" : "short";
|
|
case TY_INT:
|
|
return is_unsigned ? "unsigned" : "int";
|
|
case TY_LONG:
|
|
return is_unsigned ? "unsigned long" : "long";
|
|
case TY_FLOAT:
|
|
return "float";
|
|
case TY_DOUBLE:
|
|
return "float";
|
|
case TY_PTR:
|
|
if ((*param)->ty->base->kind == TY_CHAR) {
|
|
*param = (*param)->next;
|
|
return "Py_buffer";
|
|
} else {
|
|
UNREACHABLE();
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
static const char *GetReturnIntermediate(struct Type *ty) {
|
|
bool is_unsigned;
|
|
is_unsigned = ty->is_unsigned;
|
|
switch (ty->kind) {
|
|
case TY_BOOL:
|
|
return "int";
|
|
case TY_CHAR:
|
|
return is_unsigned ? "unsigned char" : "signed char";
|
|
case TY_SHORT:
|
|
return is_unsigned ? "unsigned short" : "short";
|
|
case TY_INT:
|
|
return is_unsigned ? "unsigned" : "int";
|
|
case TY_LONG:
|
|
return is_unsigned ? "unsigned long" : "long";
|
|
case TY_FLOAT:
|
|
return "float";
|
|
case TY_DOUBLE:
|
|
return "float";
|
|
case TY_PTR:
|
|
if (ty->base->kind == TY_CHAR) {
|
|
if (ty->base->is_const) {
|
|
return "const char*";
|
|
} else {
|
|
return "char*";
|
|
}
|
|
} else {
|
|
UNREACHABLE();
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
static void AppendFunction(char **b, Obj *func, const char *module) {
|
|
Obj *param;
|
|
const char *name;
|
|
appendf(b, "\nPyDoc_STRVAR(pb_%s_%s_doc,\n\"%s($module", module, func->name,
|
|
func->name);
|
|
for (param = func->params; param; param = param->next) {
|
|
appendw(b, READ16LE(", "));
|
|
appends(b, param->name);
|
|
}
|
|
appends(b, ")");
|
|
if (func->javadown) {
|
|
appends(b, "\\n\\\n--\\n\\n\\\n");
|
|
AppendJavadown(b, func->javadown->javadown);
|
|
}
|
|
appendw(b, READ32LE("\");\n\n"));
|
|
AppendType(b, func->ty->return_ty, 0);
|
|
appendw(b, ' ');
|
|
appends(b, func->name);
|
|
appendw(b, '(');
|
|
if (func->params) {
|
|
AppendType(b, func->params->ty, 0);
|
|
for (param = func->params->next; param; param = param->next) {
|
|
appendw(b, READ16LE(", "));
|
|
AppendType(b, param->ty, 0);
|
|
}
|
|
} else {
|
|
appendw(b, READ32LE("void"));
|
|
}
|
|
appendw(b, READ32LE(");\n\n"));
|
|
appends(b, "static PyObject*\n");
|
|
appendf(b, "pb_%s_%s(PyObject* self_, PyObject* args_)\n", module,
|
|
func->name);
|
|
appendw(b, READ16LE("{\n"));
|
|
appendw(b, READ32LE(" "));
|
|
appends(b, "PyObject* res_;\n");
|
|
if (func->ty->return_ty->kind != TY_VOID) {
|
|
appendw(b, READ32LE(" "));
|
|
appends(b, GetReturnIntermediate(func->ty->return_ty));
|
|
appendw(b, READ64LE(" ret_;\n"));
|
|
}
|
|
if (func->params) {
|
|
for (param = func->params; param; param = param->next) {
|
|
name = param->name;
|
|
appendw(b, READ32LE(" "));
|
|
appends(b, GetParamIntermediate(¶m));
|
|
appendw(b, ' ');
|
|
appends(b, name);
|
|
appendw(b, READ16LE(";\n"));
|
|
if (!param) break;
|
|
}
|
|
appends(b, " if (!PyArg_ParseTuple(args_, \"");
|
|
for (param = func->params; param; param = param->next) {
|
|
appendw(b, GetParamDirective(¶m));
|
|
if (!param) break;
|
|
}
|
|
appendf(b, ":%s\"", func->name);
|
|
for (param = func->params; param; param = param->next) {
|
|
appendf(b, ", &%s", param->name);
|
|
}
|
|
appends(b, ")) return 0;\n");
|
|
}
|
|
appendw(b, READ32LE(" "));
|
|
if (func->ty->return_ty->kind != TY_VOID) {
|
|
appendw(b, READ64LE("ret_ = "));
|
|
}
|
|
appends(b, func->name);
|
|
appendw(b, '(');
|
|
for (param = func->params; param; param = param->next) {
|
|
if (param != func->params) {
|
|
appendw(b, READ16LE(", "));
|
|
}
|
|
appends(b, param->name);
|
|
if (param->ty->kind == TY_PTR && param->ty->base->kind == TY_CHAR) {
|
|
appendw(b, READ32LE(".buf"));
|
|
if (param->ty->base->is_unsigned &&
|
|
(param->next && (param->next->ty->kind == TY_LONG &&
|
|
param->next->ty->is_unsigned))) {
|
|
appendf(b, ", %s.len", param->name);
|
|
param = param->next;
|
|
}
|
|
}
|
|
}
|
|
appends(b, ");\n");
|
|
switch (func->ty->return_ty->kind) {
|
|
case TY_VOID:
|
|
appends(b, " res_ = Py_None;\n");
|
|
appends(b, " Py_INCREF(res_);\n");
|
|
break;
|
|
case TY_BOOL:
|
|
appends(b, " res_ = ret_ ? Py_True : Py_False;\n");
|
|
appends(b, " Py_INCREF(res_);\n");
|
|
break;
|
|
case TY_CHAR:
|
|
case TY_SHORT:
|
|
case TY_INT:
|
|
appends(b, " res_ = PyLong_FromLong(ret_);\n");
|
|
break;
|
|
case TY_LONG:
|
|
if (func->ty->return_ty->is_unsigned) {
|
|
appends(b, " res_ = PyLong_FromUnsignedLong(ret_);\n");
|
|
} else {
|
|
appends(b, " res_ = PyLong_FromLong(ret_);\n");
|
|
}
|
|
break;
|
|
case TY_FLOAT:
|
|
case TY_DOUBLE:
|
|
appends(b, " res_ = PyFloat_FromDouble(ret_);\n");
|
|
break;
|
|
case TY_PTR:
|
|
appends(b, "\
|
|
if (ret_) {\n\
|
|
res_ = PyUnicode_DecodeUTF8(ret_, strlen(ret_), 0);\n\
|
|
} else {\n\
|
|
res_ = Py_None;\n\
|
|
Py_INCREF(res_);\n\
|
|
}\n");
|
|
if (!func->ty->return_ty->base->is_const) {
|
|
appends(b, " free(res_);\n");
|
|
}
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
for (param = func->params; param; param = param->next) {
|
|
if (param->ty->kind == TY_PTR && param->ty->base->kind == TY_CHAR) {
|
|
appendf(b, " PyBuffer_Release(&%s);\n", param->name);
|
|
}
|
|
}
|
|
appends(b, " return res_;\n");
|
|
appendw(b, READ16LE("}\n"));
|
|
}
|
|
|
|
void output_bindings_python(const char *path, Obj *prog, Token *tok) {
|
|
int fd;
|
|
Obj *obj;
|
|
char *b = 0;
|
|
char *bm = 0;
|
|
const char *module;
|
|
module = basename(stripexts(strdup(tok->file->name)));
|
|
appends(&b, "\
|
|
#define PY_SSIZE_T_CLEAN\n\
|
|
#include \"third_party/python/Include/abstract.h\"\n\
|
|
#include \"third_party/python/Include/boolobject.h\"\n\
|
|
#include \"third_party/python/Include/floatobject.h\"\n\
|
|
#include \"third_party/python/Include/import.h\"\n\
|
|
#include \"third_party/python/Include/longobject.h\"\n\
|
|
#include \"third_party/python/Include/methodobject.h\"\n\
|
|
#include \"third_party/python/Include/modsupport.h\"\n\
|
|
#include \"third_party/python/Include/moduleobject.h\"\n\
|
|
#include \"third_party/python/Include/pymacro.h\"\n\
|
|
#include \"third_party/python/Include/pyport.h\"\n\
|
|
");
|
|
if (tok->file->javadown) {
|
|
appendf(&b, "\nPyDoc_STRVAR(pb_%s_doc,\n\"", module);
|
|
AppendJavadown(&b, tok->file->javadown);
|
|
appendw(&b, READ32LE("\");\n"));
|
|
}
|
|
for (obj = prog; obj; obj = obj->next) {
|
|
if (obj->is_function) {
|
|
if (obj->is_static) continue;
|
|
if (!obj->is_definition) continue;
|
|
if (*obj->name == '_') continue;
|
|
if (strchr(obj->name, '$')) continue;
|
|
if (!IsFunctionSupported(&b, obj)) continue;
|
|
AppendFunction(&b, obj, module);
|
|
appendf(&bm, " {\"%s\", pb_%s_%s, %s, pb_%s_%s_doc},\n", obj->name,
|
|
module, obj->name, obj->params ? "METH_VARARGS" : "METH_NOARGS",
|
|
module, obj->name);
|
|
}
|
|
}
|
|
appends(&bm, " {0},\n");
|
|
appendf(&b, "\nstatic PyMethodDef pb_%s_methods[] = {\n", module);
|
|
appendd(&b, bm, appendz(bm).i);
|
|
appends(&b, "};\n");
|
|
appendf(&b, "\n\
|
|
static struct PyModuleDef pb_%s_module = {\n\
|
|
PyModuleDef_HEAD_INIT,\n\
|
|
\"%s\",\n\
|
|
%s,\n\
|
|
-1,\n\
|
|
pb_%s_methods,\n\
|
|
};\n\
|
|
\n\
|
|
PyMODINIT_FUNC\n\
|
|
PyInit_%s(void)\n\
|
|
{\n\
|
|
return PyModule_Create(&pb_%s_module);\n\
|
|
}\n\
|
|
\n\
|
|
__attribute__((__section__(\".rodata.pytab.1\")))\n\
|
|
const struct _inittab _PyImport_Inittab_%s = {\n\
|
|
\"%s\",\n\
|
|
PyInit_%s,\n\
|
|
};\n\
|
|
",
|
|
module, module,
|
|
tok->file->javadown ? _gc(xasprintf("pb_%s_doc", module)) : "0",
|
|
module, module, module, module, module, module);
|
|
CHECK_NE(-1, (fd = creat(path, 0644)));
|
|
CHECK_NE(-1, xwrite(fd, b, appendz(b).i));
|
|
CHECK_NE(-1, close(fd));
|
|
free(bm);
|
|
free(b);
|
|
}
|