porffor 0.60.5 → 0.60.7
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 +182 -5
- package/compiler/codegen.js +3 -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/{AGENT.md → AGENTS.md} +0 -0
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 += '}';
|
@@ -426,6 +429,7 @@ export default ({ funcs, globals, data, pages }) => {
|
|
426
429
|
for (const name in symbols) {
|
427
430
|
line(`*(void**)(&${name}) = dlsym(_dl, "${name}")`);
|
428
431
|
ffiFuncs[name] = symbols[name];
|
432
|
+
cified.add(name, true);
|
429
433
|
}
|
430
434
|
|
431
435
|
continue;
|
@@ -639,9 +643,13 @@ extern ${importFunc.returns.length > 0 ? CValtype[importFunc.returns[0]] : 'void
|
|
639
643
|
|
640
644
|
case 'time': {
|
641
645
|
const id = tmpId++;
|
642
|
-
|
643
|
-
|
644
|
-
|
646
|
+
platformSpecific(`
|
647
|
+
time_t _time_t${id};
|
648
|
+
time(&_time_t${id});
|
649
|
+
f64 _time_out${id} = (f64)_time_t${id} * 1000.0;`, `
|
650
|
+
struct timespec _ts${id};
|
651
|
+
clock_gettime(CLOCK_REALTIME, &_ts${id});
|
652
|
+
f64 _time_out${id} = (f64)_ts${id}.tv_sec * 1000.0 + (f64)_ts${id}.tv_nsec / 1.0e6;`);
|
645
653
|
vals.push(`_time_out${id}`);
|
646
654
|
|
647
655
|
includes.set('time.h', true);
|
@@ -969,6 +977,175 @@ extern ${importFunc.returns.length > 0 ? CValtype[importFunc.returns[0]] : 'void
|
|
969
977
|
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
978
|
}).join('\n'));
|
971
979
|
|
980
|
+
if (Prefs.lambda) {
|
981
|
+
includes.set('stdio.h', true);
|
982
|
+
includes.set('stdlib.h', true);
|
983
|
+
includes.set('string.h', true);
|
984
|
+
includes.set('unistd.h', true);
|
985
|
+
includes.set('netdb.h', true);
|
986
|
+
includes.set('sys/socket.h', true);
|
987
|
+
includes.set('signal.h', true);
|
988
|
+
includes.set('execinfo.h', true);
|
989
|
+
|
990
|
+
let copyGlobals = '';
|
991
|
+
let restoreGlobals = '';
|
992
|
+
for (const x in globals) {
|
993
|
+
const g = globals[x];
|
994
|
+
|
995
|
+
copyGlobals += `${CValtype[g.type]} copy_${sanitize(x)} = ${sanitize(x)};\n`;
|
996
|
+
restoreGlobals += `${sanitize(x)} = copy_${sanitize(x)};\n`;
|
997
|
+
}
|
998
|
+
|
999
|
+
const lambdaWrapper = `
|
1000
|
+
#define BUF_SIZE 65536
|
1001
|
+
|
1002
|
+
// minimal http send/receive
|
1003
|
+
static int send_http(const char *host, const char *port, const char *req, char *resp, size_t resp_size) {
|
1004
|
+
struct addrinfo hints = {0}, *res;
|
1005
|
+
int sock;
|
1006
|
+
hints.ai_family = AF_UNSPEC;
|
1007
|
+
hints.ai_socktype = SOCK_STREAM;
|
1008
|
+
if (getaddrinfo(host, port, &hints, &res) != 0) return -1;
|
1009
|
+
|
1010
|
+
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
1011
|
+
if (sock < 0) return -1;
|
1012
|
+
if (connect(sock, res->ai_addr, res->ai_addrlen) != 0) {
|
1013
|
+
close(sock);
|
1014
|
+
freeaddrinfo(res);
|
1015
|
+
return -1;
|
1016
|
+
}
|
1017
|
+
|
1018
|
+
write(sock, req, strlen(req));
|
1019
|
+
|
1020
|
+
int len = 0;
|
1021
|
+
if (resp) {
|
1022
|
+
len = read(sock, resp, resp_size - 1);
|
1023
|
+
if (len >= 0) resp[len] = '\\0';
|
1024
|
+
}
|
1025
|
+
|
1026
|
+
close(sock);
|
1027
|
+
freeaddrinfo(res);
|
1028
|
+
return len;
|
1029
|
+
}
|
1030
|
+
|
1031
|
+
void crash_handler(int sig) {
|
1032
|
+
void *array[20];
|
1033
|
+
size_t size = backtrace(array, 20);
|
1034
|
+
fprintf(stderr, "Caught signal %d\\n", sig);
|
1035
|
+
backtrace_symbols_fd(array, size, STDERR_FILENO);
|
1036
|
+
_exit(1);
|
1037
|
+
}
|
1038
|
+
|
1039
|
+
int main(void) {
|
1040
|
+
signal(SIGSEGV, crash_handler);
|
1041
|
+
signal(SIGABRT, crash_handler);
|
1042
|
+
signal(SIGBUS, crash_handler);
|
1043
|
+
signal(SIGILL, crash_handler);
|
1044
|
+
|
1045
|
+
user_main();
|
1046
|
+
|
1047
|
+
i32 _memory_pages = _memoryPages;
|
1048
|
+
char* _memory_clone = calloc(1, _memory_pages * ${PageSize});
|
1049
|
+
memcpy(_memory_clone, _memory, _memory_pages * ${PageSize});
|
1050
|
+
|
1051
|
+
${copyGlobals}
|
1052
|
+
|
1053
|
+
char *api = getenv("AWS_LAMBDA_RUNTIME_API");
|
1054
|
+
if (!api) {
|
1055
|
+
// Not in Lambda — error
|
1056
|
+
printf("AWS_LAMBDA_RUNTIME_API not set\\n");
|
1057
|
+
exit(1);
|
1058
|
+
}
|
1059
|
+
|
1060
|
+
char host[256], port[16] = "80";
|
1061
|
+
char *colon = strchr(api, ':');
|
1062
|
+
if (colon) {
|
1063
|
+
strncpy(host, api, colon - api);
|
1064
|
+
host[colon - api] = '\\0';
|
1065
|
+
strncpy(port, colon + 1, sizeof(port) - 1);
|
1066
|
+
} else {
|
1067
|
+
strncpy(host, api, sizeof(host) - 1);
|
1068
|
+
}
|
1069
|
+
|
1070
|
+
char req[BUF_SIZE], resp[BUF_SIZE];
|
1071
|
+
char request_id[128];
|
1072
|
+
|
1073
|
+
i32 request_count = 0;
|
1074
|
+
while (1) {
|
1075
|
+
// 1. GET next event
|
1076
|
+
snprintf(req, sizeof(req),
|
1077
|
+
"GET /2018-06-01/runtime/invocation/next HTTP/1.1\\r\\n"
|
1078
|
+
"Host: %s\\r\\n\\r\\n", host);
|
1079
|
+
if (send_http(host, port, req, resp, sizeof(resp)) <= 0) {
|
1080
|
+
fprintf(stderr, "send_http failed\\n");
|
1081
|
+
break;
|
1082
|
+
}
|
1083
|
+
|
1084
|
+
char *rid = strstr(resp, "Lambda-Runtime-Aws-Request-Id: ");
|
1085
|
+
if (!rid) {
|
1086
|
+
fprintf(stderr, "No Request ID header in response:\\n%s\\n", resp);
|
1087
|
+
break;
|
1088
|
+
}
|
1089
|
+
|
1090
|
+
rid += strlen("Lambda-Runtime-Aws-Request-Id: ");
|
1091
|
+
char *end = strchr(rid, '\\r');
|
1092
|
+
if (!end) {
|
1093
|
+
fprintf(stderr, "No CR after Request ID in response:\\n%s\\n", resp);
|
1094
|
+
break;
|
1095
|
+
}
|
1096
|
+
size_t rid_len = end - rid;
|
1097
|
+
strncpy(request_id, rid, rid_len);
|
1098
|
+
request_id[rid_len] = '\\0';
|
1099
|
+
|
1100
|
+
// 2. Parse event data from response body
|
1101
|
+
char *body = strstr(resp, "\\r\\n\\r\\n");
|
1102
|
+
void *event = NULL;
|
1103
|
+
if (body) {
|
1104
|
+
body += 4; // skip \\r\\n\\r\\n
|
1105
|
+
event = body;
|
1106
|
+
}
|
1107
|
+
|
1108
|
+
if (request_count++ > 0) {
|
1109
|
+
_memoryPages = _memory_pages;
|
1110
|
+
free(_memory);
|
1111
|
+
_memory = calloc(1, _memory_pages * ${PageSize});
|
1112
|
+
memcpy(_memory, _memory_clone, _memory_pages * ${PageSize});
|
1113
|
+
|
1114
|
+
${restoreGlobals}
|
1115
|
+
}
|
1116
|
+
|
1117
|
+
// Call handler function and JSON.stringify the result
|
1118
|
+
// size_t eventLen = strlen(event);
|
1119
|
+
// i32 eventPtr = __Porffor_allocateBytes(eventLen + 4);
|
1120
|
+
// memcpy((char*)eventPtr, &eventLen, 4);
|
1121
|
+
// memcpy((char*)eventPtr + 4, event, eventLen);
|
1122
|
+
|
1123
|
+
// char* resp = _memory + (i32)__Porffor_handler((f64)eventPtr, 195).value + 4;
|
1124
|
+
i32 ret = (i32)__Porffor_handler().value;
|
1125
|
+
char* ret_str = _memory + ret + 4;
|
1126
|
+
i32 ret_str_len = *((i32*)(ret_str - 4));
|
1127
|
+
|
1128
|
+
// 3. POST response
|
1129
|
+
snprintf(req, sizeof(req),
|
1130
|
+
"POST /2018-06-01/runtime/invocation/%s/response HTTP/1.1\\r\\n"
|
1131
|
+
"Host: %s\\r\\n"
|
1132
|
+
"Content-Type: text/plain\\r\\n"
|
1133
|
+
"Content-Length: %zu\\r\\n\\r\\n"
|
1134
|
+
"%s", request_id, host, ret_str_len, ret_str);
|
1135
|
+
send_http(host, port, req, resp, sizeof(resp));
|
1136
|
+
}
|
1137
|
+
return 0;
|
1138
|
+
}`;
|
1139
|
+
out += lambdaWrapper;
|
1140
|
+
|
1141
|
+
// preallocate 1 chunk allocator chunk
|
1142
|
+
// out = out.replace(`_memory = calloc(1, _memoryPages * ${PageSize});\n`, `
|
1143
|
+
// jjporfjjchunkPtr = _memoryPages * 65536;
|
1144
|
+
// jjporfjjchunkOffset = 0;
|
1145
|
+
// _memoryPages += 16;
|
1146
|
+
// _memory = calloc(1, _memoryPages * ${PageSize});`);
|
1147
|
+
}
|
1148
|
+
|
972
1149
|
const makeIncludes = includes => [...includes.keys()].map(x => `#include <${x}>\n`).join('');
|
973
1150
|
out = platformSpecific(makeIncludes(winIncludes), makeIncludes(unixIncludes), false) + '\n' + makeIncludes(includes) + '\n' + alwaysPreface + [...prepend.values()].join('\n') + '\n\n' + out;
|
974
1151
|
|
package/compiler/codegen.js
CHANGED
@@ -2638,7 +2638,8 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2638
2638
|
args.push({
|
2639
2639
|
type: 'ArrayExpression',
|
2640
2640
|
elements: restArgs,
|
2641
|
-
_doNotMarkTypeUsed: true
|
2641
|
+
_doNotMarkTypeUsed: true,
|
2642
|
+
_staticAlloc: func.internal
|
2642
2643
|
});
|
2643
2644
|
}
|
2644
2645
|
}
|
@@ -5594,7 +5595,7 @@ const generateArray = (scope, decl, global = false, name = '$undeclared', static
|
|
5594
5595
|
const out = [];
|
5595
5596
|
let pointer;
|
5596
5597
|
|
5597
|
-
if (staticAlloc) {
|
5598
|
+
if (staticAlloc || decl._staticAlloc) {
|
5598
5599
|
const uniqueName = name === '$undeclared' ? name + uniqId() : name;
|
5599
5600
|
|
5600
5601
|
const ptr = allocPage({ scope, pages }, uniqueName);
|
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.7';
|
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
|
+
}
|
package/foo.js
DELETED
/package/{AGENT.md → AGENTS.md}
RENAMED
File without changes
|