porffor 0.60.5 → 0.60.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/2c.js +174 -2
- package/compiler/index.js +1 -1
- package/package.json +1 -1
- package/runtime/index.js +14 -6
- package/runtime/lambda.js +46 -0
- package/foo.js +0 -9
package/compiler/2c.js
CHANGED
@@ -350,8 +350,11 @@ export default ({ funcs, globals, data, pages }) => {
|
|
350
350
|
const typedReturns = f.returnType == null;
|
351
351
|
|
352
352
|
const shouldInline = false; // f.internal;
|
353
|
-
if (f.name === '#main')
|
354
|
-
|
353
|
+
if (f.name === '#main') {
|
354
|
+
out += `int ${Prefs.lambda ? 'user_main' : 'main'}(${prependMain.has('argv') ? 'int argc, char* argv[]' : ''}) {\n`;
|
355
|
+
} else {
|
356
|
+
out += `${!typedReturns ? (returns ? CValtype[f.returns[0]] : 'void') : 'struct ReturnValue'} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
|
357
|
+
}
|
355
358
|
|
356
359
|
if (f.name === '__Porffor_promise_runJobs') {
|
357
360
|
out += '}';
|
@@ -969,6 +972,175 @@ extern ${importFunc.returns.length > 0 ? CValtype[importFunc.returns[0]] : 'void
|
|
969
972
|
return `${!typedReturns ? (returns ? CValtype[f.returns[0]] : 'void') : 'struct ReturnValue'} ${shouldInline ? 'inline ' : ''}${ffiFuncs[f.name] ? '(*' : ''}${sanitize(f.name)}${ffiFuncs[f.name] ? ')' : ''}(${rawParams(f).map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')});`;
|
970
973
|
}).join('\n'));
|
971
974
|
|
975
|
+
if (Prefs.lambda) {
|
976
|
+
includes.set('stdio.h', true);
|
977
|
+
includes.set('stdlib.h', true);
|
978
|
+
includes.set('string.h', true);
|
979
|
+
includes.set('unistd.h', true);
|
980
|
+
includes.set('netdb.h', true);
|
981
|
+
includes.set('sys/socket.h', true);
|
982
|
+
includes.set('signal.h', true);
|
983
|
+
includes.set('execinfo.h', true);
|
984
|
+
|
985
|
+
let copyGlobals = '';
|
986
|
+
let restoreGlobals = '';
|
987
|
+
for (const x in globals) {
|
988
|
+
const g = globals[x];
|
989
|
+
|
990
|
+
copyGlobals += `${CValtype[g.type]} copy_${sanitize(x)} = ${sanitize(x)};\n`;
|
991
|
+
restoreGlobals += `${sanitize(x)} = copy_${sanitize(x)};\n`;
|
992
|
+
}
|
993
|
+
|
994
|
+
const lambdaWrapper = `
|
995
|
+
#define BUF_SIZE 65536
|
996
|
+
|
997
|
+
// minimal http send/receive
|
998
|
+
static int send_http(const char *host, const char *port, const char *req, char *resp, size_t resp_size) {
|
999
|
+
struct addrinfo hints = {0}, *res;
|
1000
|
+
int sock;
|
1001
|
+
hints.ai_family = AF_UNSPEC;
|
1002
|
+
hints.ai_socktype = SOCK_STREAM;
|
1003
|
+
if (getaddrinfo(host, port, &hints, &res) != 0) return -1;
|
1004
|
+
|
1005
|
+
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
1006
|
+
if (sock < 0) return -1;
|
1007
|
+
if (connect(sock, res->ai_addr, res->ai_addrlen) != 0) {
|
1008
|
+
close(sock);
|
1009
|
+
freeaddrinfo(res);
|
1010
|
+
return -1;
|
1011
|
+
}
|
1012
|
+
|
1013
|
+
write(sock, req, strlen(req));
|
1014
|
+
|
1015
|
+
int len = 0;
|
1016
|
+
if (resp) {
|
1017
|
+
len = read(sock, resp, resp_size - 1);
|
1018
|
+
if (len >= 0) resp[len] = '\\0';
|
1019
|
+
}
|
1020
|
+
|
1021
|
+
close(sock);
|
1022
|
+
freeaddrinfo(res);
|
1023
|
+
return len;
|
1024
|
+
}
|
1025
|
+
|
1026
|
+
void crash_handler(int sig) {
|
1027
|
+
void *array[20];
|
1028
|
+
size_t size = backtrace(array, 20);
|
1029
|
+
fprintf(stderr, "Caught signal %d\\n", sig);
|
1030
|
+
backtrace_symbols_fd(array, size, STDERR_FILENO);
|
1031
|
+
_exit(1);
|
1032
|
+
}
|
1033
|
+
|
1034
|
+
int main(void) {
|
1035
|
+
signal(SIGSEGV, crash_handler);
|
1036
|
+
signal(SIGABRT, crash_handler);
|
1037
|
+
signal(SIGBUS, crash_handler);
|
1038
|
+
signal(SIGILL, crash_handler);
|
1039
|
+
|
1040
|
+
user_main();
|
1041
|
+
|
1042
|
+
i32 _memory_pages = _memoryPages;
|
1043
|
+
char* _memory_clone = calloc(1, _memory_pages * ${PageSize});
|
1044
|
+
memcpy(_memory_clone, _memory, _memory_pages * ${PageSize});
|
1045
|
+
|
1046
|
+
${copyGlobals}
|
1047
|
+
|
1048
|
+
char *api = getenv("AWS_LAMBDA_RUNTIME_API");
|
1049
|
+
if (!api) {
|
1050
|
+
// Not in Lambda — error
|
1051
|
+
printf("AWS_LAMBDA_RUNTIME_API not set\\n");
|
1052
|
+
exit(1);
|
1053
|
+
}
|
1054
|
+
|
1055
|
+
char host[256], port[16] = "80";
|
1056
|
+
char *colon = strchr(api, ':');
|
1057
|
+
if (colon) {
|
1058
|
+
strncpy(host, api, colon - api);
|
1059
|
+
host[colon - api] = '\\0';
|
1060
|
+
strncpy(port, colon + 1, sizeof(port) - 1);
|
1061
|
+
} else {
|
1062
|
+
strncpy(host, api, sizeof(host) - 1);
|
1063
|
+
}
|
1064
|
+
|
1065
|
+
char req[BUF_SIZE], resp[BUF_SIZE];
|
1066
|
+
char request_id[128];
|
1067
|
+
|
1068
|
+
i32 request_count = 0;
|
1069
|
+
while (1) {
|
1070
|
+
// 1. GET next event
|
1071
|
+
snprintf(req, sizeof(req),
|
1072
|
+
"GET /2018-06-01/runtime/invocation/next HTTP/1.1\\r\\n"
|
1073
|
+
"Host: %s\\r\\n\\r\\n", host);
|
1074
|
+
if (send_http(host, port, req, resp, sizeof(resp)) <= 0) {
|
1075
|
+
fprintf(stderr, "send_http failed\\n");
|
1076
|
+
break;
|
1077
|
+
}
|
1078
|
+
|
1079
|
+
char *rid = strstr(resp, "Lambda-Runtime-Aws-Request-Id: ");
|
1080
|
+
if (!rid) {
|
1081
|
+
fprintf(stderr, "No Request ID header in response:\\n%s\\n", resp);
|
1082
|
+
break;
|
1083
|
+
}
|
1084
|
+
|
1085
|
+
rid += strlen("Lambda-Runtime-Aws-Request-Id: ");
|
1086
|
+
char *end = strchr(rid, '\\r');
|
1087
|
+
if (!end) {
|
1088
|
+
fprintf(stderr, "No CR after Request ID in response:\\n%s\\n", resp);
|
1089
|
+
break;
|
1090
|
+
}
|
1091
|
+
size_t rid_len = end - rid;
|
1092
|
+
strncpy(request_id, rid, rid_len);
|
1093
|
+
request_id[rid_len] = '\\0';
|
1094
|
+
|
1095
|
+
// 2. Parse event data from response body
|
1096
|
+
char *body = strstr(resp, "\\r\\n\\r\\n");
|
1097
|
+
void *event = NULL;
|
1098
|
+
if (body) {
|
1099
|
+
body += 4; // skip \\r\\n\\r\\n
|
1100
|
+
event = body;
|
1101
|
+
}
|
1102
|
+
|
1103
|
+
if (request_count++ > 0) {
|
1104
|
+
_memoryPages = _memory_pages;
|
1105
|
+
free(_memory);
|
1106
|
+
_memory = calloc(1, _memory_pages * ${PageSize});
|
1107
|
+
memcpy(_memory, _memory_clone, _memory_pages * ${PageSize});
|
1108
|
+
|
1109
|
+
${restoreGlobals}
|
1110
|
+
}
|
1111
|
+
|
1112
|
+
// Call handler function and JSON.stringify the result
|
1113
|
+
// size_t eventLen = strlen(event);
|
1114
|
+
// i32 eventPtr = __Porffor_allocateBytes(eventLen + 4);
|
1115
|
+
// memcpy((char*)eventPtr, &eventLen, 4);
|
1116
|
+
// memcpy((char*)eventPtr + 4, event, eventLen);
|
1117
|
+
|
1118
|
+
// char* resp = _memory + (i32)__Porffor_handler((f64)eventPtr, 195).value + 4;
|
1119
|
+
i32 ret = (i32)__Porffor_handler().value;
|
1120
|
+
char* ret_str = _memory + ret + 4;
|
1121
|
+
i32 ret_str_len = *((i32*)(ret_str - 4));
|
1122
|
+
|
1123
|
+
// 3. POST response
|
1124
|
+
snprintf(req, sizeof(req),
|
1125
|
+
"POST /2018-06-01/runtime/invocation/%s/response HTTP/1.1\\r\\n"
|
1126
|
+
"Host: %s\\r\\n"
|
1127
|
+
"Content-Type: text/plain\\r\\n"
|
1128
|
+
"Content-Length: %zu\\r\\n\\r\\n"
|
1129
|
+
"%s", request_id, host, ret_str_len, ret_str);
|
1130
|
+
send_http(host, port, req, resp, sizeof(resp));
|
1131
|
+
}
|
1132
|
+
return 0;
|
1133
|
+
}`;
|
1134
|
+
out += lambdaWrapper;
|
1135
|
+
|
1136
|
+
// preallocate 1 chunk allocator chunk
|
1137
|
+
// out = out.replace(`_memory = calloc(1, _memoryPages * ${PageSize});\n`, `
|
1138
|
+
// jjporfjjchunkPtr = _memoryPages * 65536;
|
1139
|
+
// jjporfjjchunkOffset = 0;
|
1140
|
+
// _memoryPages += 16;
|
1141
|
+
// _memory = calloc(1, _memoryPages * ${PageSize});`);
|
1142
|
+
}
|
1143
|
+
|
972
1144
|
const makeIncludes = includes => [...includes.keys()].map(x => `#include <${x}>\n`).join('');
|
973
1145
|
out = platformSpecific(makeIncludes(winIncludes), makeIncludes(unixIncludes), false) + '\n' + makeIncludes(includes) + '\n' + alwaysPreface + [...prepend.values()].join('\n') + '\n\n' + out;
|
974
1146
|
|
package/compiler/index.js
CHANGED
@@ -284,7 +284,7 @@ int m()`);
|
|
284
284
|
console.log(`\u001b[2m[${total.toFixed(0)}ms]\u001b[0m \u001b[32mcompiled ${globalThis.file} \u001b[90m->\u001b[0m \u001b[92m${outFile}\u001b[90m (${(fs.statSync(outFile).size / 1000).toFixed(1)}KB)\u001b[0m`);
|
285
285
|
}
|
286
286
|
|
287
|
-
if (process.version) process.exit();
|
287
|
+
if (process.version && !Prefs.lambda) process.exit();
|
288
288
|
}
|
289
289
|
|
290
290
|
if (target === 'native') {
|
package/package.json
CHANGED
package/runtime/index.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
2
|
import fs from 'node:fs';
|
3
|
-
globalThis.version = '0.60.
|
3
|
+
globalThis.version = '0.60.6';
|
4
4
|
|
5
5
|
// deno compat
|
6
6
|
if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
|
@@ -9,7 +9,7 @@ if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
|
|
9
9
|
|
10
10
|
const start = performance.now();
|
11
11
|
|
12
|
-
|
12
|
+
const help = () => {
|
13
13
|
// description + version
|
14
14
|
console.log(`\x1B[1m\x1B[35mPorffor\x1B[0m is a JavaScript/TypeScript engine/compiler/runtime. \x1B[2m(${globalThis.version})\x1B[0m`);
|
15
15
|
|
@@ -23,6 +23,7 @@ if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
23
23
|
wasm: [ 34, 'foo.js foo.wasm', 'Compile to a Wasm binary' ],
|
24
24
|
c: [ 94, 'foo.js foo.c', 'Compile to C source code' ],
|
25
25
|
native: [ 94, 'foo.js foo', 'Compile to a native binary' ],
|
26
|
+
lambda: [ 36, 'foo.js function.zip', 'Compile Lambda code to a deployable zip' ],
|
26
27
|
|
27
28
|
'Analyze': [],
|
28
29
|
profile: [ 93, 'foo.js', 'View detailed func-by-func performance' ],
|
@@ -81,6 +82,10 @@ if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
81
82
|
|
82
83
|
console.log();
|
83
84
|
process.exit(0);
|
85
|
+
};
|
86
|
+
|
87
|
+
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
88
|
+
help();
|
84
89
|
}
|
85
90
|
|
86
91
|
const done = async () => {
|
@@ -90,7 +95,9 @@ const done = async () => {
|
|
90
95
|
};
|
91
96
|
|
92
97
|
let file = process.argv.slice(2).find(x => x[0] !== '-');
|
93
|
-
if (
|
98
|
+
if (file === 'help') help();
|
99
|
+
|
100
|
+
if (['precompile', 'run', 'wasm', 'native', 'c', 'lambda', 'profile', 'debug'].includes(file)) {
|
94
101
|
// remove this arg
|
95
102
|
process.argv.splice(process.argv.indexOf(file), 1);
|
96
103
|
|
@@ -113,9 +120,10 @@ if (['precompile', 'run', 'wasm', 'native', 'c', 'profile', 'debug', 'dissect'].
|
|
113
120
|
process.argv.push(`--target=${file}`);
|
114
121
|
}
|
115
122
|
|
116
|
-
|
117
|
-
|
118
|
-
|
123
|
+
if (file === 'lambda') {
|
124
|
+
await import('./lambda.js');
|
125
|
+
await done();
|
126
|
+
}
|
119
127
|
|
120
128
|
file = process.argv.slice(2).find(x => x[0] !== '-');
|
121
129
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
import fs from 'node:fs';
|
3
|
+
import { execSync } from 'node:child_process';
|
4
|
+
import compile from '../compiler/wrap.js';
|
5
|
+
|
6
|
+
// Parse arguments
|
7
|
+
const args = process.argv.slice(2);
|
8
|
+
const sourceFile = args.find(x => x[0] !== '-' && !x.includes('='));
|
9
|
+
const outputZip = args.find(x => x[0] !== '-' && !x.includes('=') && x !== sourceFile);
|
10
|
+
|
11
|
+
console.log(`Compiling ${sourceFile} to AWS Lambda function...`);
|
12
|
+
|
13
|
+
// Read source file
|
14
|
+
const source = `${fs.readFileSync(sourceFile, 'utf8').replace('handler = async', 'handler =')}
|
15
|
+
|
16
|
+
export const __Porffor_handler = () => {
|
17
|
+
const res = handler();
|
18
|
+
if (Porffor.type(res) == Porffor.TYPES.bytestring) return res;
|
19
|
+
return JSON.stringify(res);
|
20
|
+
};`;
|
21
|
+
|
22
|
+
const binaryPath = 'bootstrap';
|
23
|
+
const cPath = 'bootstrap.c';
|
24
|
+
|
25
|
+
Prefs.allocator = 'oneshot'; // oneshot is faster for lambda
|
26
|
+
Prefs.target = 'c';
|
27
|
+
Prefs.o = cPath;
|
28
|
+
Prefs.lambda = true;
|
29
|
+
|
30
|
+
compile(source, true);
|
31
|
+
|
32
|
+
if (Prefs.d) execSync(`gcc -lm -O0 -g -flto -march=x86-64-v3 -o ${binaryPath} ${cPath}`, { stdio: 'inherit' });
|
33
|
+
else execSync(`gcc -lm -Os -s -flto -march=x86-64-v3 -o ${binaryPath} ${cPath}`, { stdio: 'inherit' });
|
34
|
+
|
35
|
+
try {
|
36
|
+
execSync(`zip -r "${outputZip}" ${binaryPath}`, { stdio: 'inherit' });
|
37
|
+
console.log(`Lambda function packaged to ${outputZip}`);
|
38
|
+
} catch (e) {
|
39
|
+
console.error('Error creating zip file:', e.message);
|
40
|
+
process.exit(1);
|
41
|
+
} finally {
|
42
|
+
if (!Prefs.d) {
|
43
|
+
fs.rmSync(binaryPath, { force: true });
|
44
|
+
fs.rmSync(cPath, { force: true });
|
45
|
+
}
|
46
|
+
}
|