porffor 0.60.24 → 0.60.26

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 CHANGED
@@ -985,7 +985,6 @@ f64 _time_out${id} = (f64)_ts${id}.tv_sec * 1000.0 + (f64)_ts${id}.tv_nsec / 1.0
985
985
  includes.set('netdb.h', true);
986
986
  includes.set('sys/socket.h', true);
987
987
  includes.set('signal.h', true);
988
- includes.set('execinfo.h', true);
989
988
 
990
989
  let copyGlobals = '';
991
990
  let restoreGlobals = '';
@@ -997,51 +996,64 @@ f64 _time_out${id} = (f64)_ts${id}.tv_sec * 1000.0 + (f64)_ts${id}.tv_nsec / 1.0
997
996
  }
998
997
 
999
998
  const lambdaWrapper = `
1000
- #define BUF_SIZE 65536
999
+ #define BUF_SIZE 8192
1001
1000
 
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;
1001
+ static struct addrinfo* runtime_addr = NULL;
1002
+ static int runtime_sock = -1;
1003
+
1004
+ static int init_runtime_addr(const char* host, const char* port) {
1005
+ struct addrinfo hints = {0};
1006
1006
  hints.ai_family = AF_UNSPEC;
1007
1007
  hints.ai_socktype = SOCK_STREAM;
1008
- if (getaddrinfo(host, port, &hints, &res) != 0) return -1;
1008
+ return getaddrinfo(host, port, &hints, &runtime_addr);
1009
+ }
1009
1010
 
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);
1011
+ static int get_sock(void) {
1012
+ if (runtime_sock >= 0) return runtime_sock;
1013
+ if (!runtime_addr) return -1;
1014
+
1015
+ runtime_sock = socket(runtime_addr->ai_family, runtime_addr->ai_socktype,
1016
+ runtime_addr->ai_protocol);
1017
+ if (runtime_sock < 0) return -1;
1018
+
1019
+ int optval = 1;
1020
+ setsockopt(runtime_sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
1021
+
1022
+ if (connect(runtime_sock, runtime_addr->ai_addr, runtime_addr->ai_addrlen) != 0) {
1023
+ close(runtime_sock);
1024
+ runtime_sock = -1;
1015
1025
  return -1;
1016
1026
  }
1027
+ return runtime_sock;
1028
+ }
1029
+
1030
+ static int send_http(const char* req, size_t req_size, char* resp, size_t resp_size) {
1031
+ int sock = get_sock();
1032
+ if (sock < 0) return -1;
1017
1033
 
1018
- write(sock, req, strlen(req));
1034
+ ssize_t w = write(sock, req, req_size);
1035
+ if (w != req_size) return -1;
1019
1036
 
1020
1037
  int len = 0;
1021
1038
  if (resp) {
1022
1039
  len = read(sock, resp, resp_size - 1);
1023
- if (len >= 0) resp[len] = '\\0';
1040
+ if (len < 0) {
1041
+ close(sock);
1042
+ runtime_sock = -1;
1043
+ return -1;
1044
+ }
1045
+ resp[len] = '\\0';
1024
1046
  }
1025
1047
 
1026
- close(sock);
1027
- freeaddrinfo(res);
1028
- return len;
1029
- }
1048
+ if (len == 0) {
1049
+ close(sock);
1050
+ runtime_sock = -1;
1051
+ }
1030
1052
 
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);
1053
+ return len;
1037
1054
  }
1038
1055
 
1039
1056
  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
1057
  user_main();
1046
1058
 
1047
1059
  i32 _memory_pages = _memoryPages;
@@ -1052,31 +1064,34 @@ int main(void) {
1052
1064
 
1053
1065
  char *api = getenv("AWS_LAMBDA_RUNTIME_API");
1054
1066
  if (!api) {
1055
- // Not in Lambda — error
1056
- printf("AWS_LAMBDA_RUNTIME_API not set\\n");
1057
- exit(1);
1067
+ printf("AWS_LAMBDA_RUNTIME_API not set\\n");
1068
+ exit(1);
1058
1069
  }
1059
1070
 
1060
1071
  char host[256], port[16] = "80";
1061
1072
  char *colon = strchr(api, ':');
1062
1073
  if (colon) {
1063
- strncpy(host, api, colon - api);
1064
- host[colon - api] = '\\0';
1065
- strncpy(port, colon + 1, sizeof(port) - 1);
1074
+ strncpy(host, api, colon - api);
1075
+ host[colon - api] = '\\0';
1076
+ strncpy(port, colon + 1, sizeof(port) - 1);
1066
1077
  } else {
1067
- strncpy(host, api, sizeof(host) - 1);
1078
+ strncpy(host, api, sizeof(host) - 1);
1079
+ }
1080
+
1081
+ if (init_runtime_addr(host, port) != 0) {
1082
+ fprintf(stderr, "Failed to resolve runtime host\\n");
1083
+ return 1;
1068
1084
  }
1069
1085
 
1070
1086
  char req[BUF_SIZE], resp[BUF_SIZE];
1071
1087
  char request_id[128];
1072
-
1073
- i32 request_count = 0;
1074
1088
  while (1) {
1075
1089
  // 1. GET next event
1076
- snprintf(req, sizeof(req),
1090
+ int req_size = snprintf(req, sizeof(req),
1077
1091
  "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) {
1092
+ "Host: %s\\r\\n"
1093
+ "Connection: keep-alive\\r\\n\\r\\n", host);
1094
+ if (send_http(req, (size_t)req_size, resp, sizeof(resp)) <= 0) {
1080
1095
  fprintf(stderr, "send_http failed\\n");
1081
1096
  break;
1082
1097
  }
@@ -1098,21 +1113,12 @@ int main(void) {
1098
1113
  request_id[rid_len] = '\\0';
1099
1114
 
1100
1115
  // 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
+ // char *body = strstr(resp, "\\r\\n\\r\\n");
1117
+ // void *event = NULL;
1118
+ // if (body) {
1119
+ // body += 4; // skip \\r\\n\\r\\n
1120
+ // event = body;
1121
+ // }
1116
1122
 
1117
1123
  // Call handler function and JSON.stringify the result
1118
1124
  // size_t eventLen = strlen(event);
@@ -1120,19 +1126,24 @@ int main(void) {
1120
1126
  // memcpy((char*)eventPtr, &eventLen, 4);
1121
1127
  // memcpy((char*)eventPtr + 4, event, eventLen);
1122
1128
 
1123
- // char* resp = _memory + (i32)__Porffor_handler((f64)eventPtr, 195).value + 4;
1124
1129
  i32 ret = (i32)__Porffor_handler().value;
1125
1130
  char* ret_str = _memory + ret + 4;
1126
1131
  i32 ret_str_len = *((i32*)(ret_str - 4));
1127
1132
 
1128
1133
  // 3. POST response
1129
- snprintf(req, sizeof(req),
1134
+ req_size = snprintf(req, sizeof(req),
1130
1135
  "POST /2018-06-01/runtime/invocation/%s/response HTTP/1.1\\r\\n"
1131
1136
  "Host: %s\\r\\n"
1137
+ "Connection: close\\r\\n"
1132
1138
  "Content-Type: text/plain\\r\\n"
1133
1139
  "Content-Length: %zu\\r\\n\\r\\n"
1134
1140
  "%s", request_id, host, ret_str_len, ret_str);
1135
- send_http(host, port, req, resp, sizeof(resp));
1141
+ send_http(req, (size_t)req_size, NULL, 0);
1142
+
1143
+ // reset js state after response
1144
+ memcpy(_memory, _memory_clone, _memory_pages * ${PageSize});
1145
+
1146
+ ${restoreGlobals}
1136
1147
  }
1137
1148
  return 0;
1138
1149
  }`;
@@ -2393,7 +2393,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2393
2393
 
2394
2394
  protoBC.default = decl.optional ?
2395
2395
  withType(scope, [ number(UNDEFINED) ], TYPES.undefined) :
2396
- internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`, true);
2396
+ generate(scope, {
2397
+ ...decl,
2398
+ _protoInternalCall: true
2399
+ });
2397
2400
 
2398
2401
  // fallback to object prototype impl as a basic prototype chain hack
2399
2402
  if (protoBC[TYPES.object]) {
@@ -91,7 +91,7 @@ export default ({ name, wasm, locals: _locals, params }, _globals) => {
91
91
  // if (Prefs.f === name) console.log(invOpcodes[opcode], stack);
92
92
  switch (opcode) {
93
93
  case Opcodes.if: {
94
- if (stack.length < 1) { empty(); break; }
94
+ if (stack.length < 1) { reset(); break; }
95
95
  const cond = bool(pop());
96
96
 
97
97
  // find else split and end
@@ -598,7 +598,6 @@ export default ({ name, wasm, locals: _locals, params }, _globals) => {
598
598
  case Opcodes.block:
599
599
  case Opcodes.loop:
600
600
  case Opcodes.try:
601
- case Opcodes.if:
602
601
  case Opcodes.else:
603
602
  case Opcodes.catch:
604
603
  case Opcodes.end: {
@@ -1,7 +1,3 @@
1
- // todo: sloppy vs strict mode
2
- // todo: function/class decls ?
3
- // todo: function params
4
-
5
1
  const varId = name => {
6
2
  const lastFunc = scopes[scopes.lastFuncs.at(-1)];
7
3
  lastFunc._variableIds ??= Object.create(null);
@@ -14,7 +10,18 @@ const varId = name => {
14
10
  };
15
11
 
16
12
  const declVar = (name, kind, node) => {
17
- const parent = kind === 'var' ? scopes[scopes.lastFuncs.at(-1)] : scopes.at(-1);
13
+ let parent;
14
+ if (kind === 'var') {
15
+ parent = scopes[scopes.lastFuncs.at(-1)];
16
+ // same id for redecl
17
+ if (parent._variables?.[name]) {
18
+ parent._variables[name].node = node;
19
+ return;
20
+ }
21
+ } else {
22
+ parent = scopes.at(-1);
23
+ }
24
+
18
25
  parent._variables ??= Object.create(null);
19
26
  parent._variables[name] = { node, id: varId(name) };
20
27
  };
@@ -53,9 +60,14 @@ const analyzePattern = (kind, node) => {
53
60
  };
54
61
 
55
62
  let scopes;
56
- const analyze = node => {
63
+ const analyze = (node, strict = false) => {
57
64
  if (!node) return;
58
65
 
66
+ const top = scopes.at(-1);
67
+ if (node.directive === 'use strict') {
68
+ top._strict = true;
69
+ }
70
+
59
71
  let openedScope = false;
60
72
  switch (node.type) {
61
73
  case 'ForStatement':
@@ -77,18 +89,31 @@ const analyze = node => {
77
89
  for (const x of node.declarations) analyzePattern(node.kind, x.id);
78
90
  break;
79
91
 
92
+ case 'ClassDeclaration':
93
+ if (node.id?.name) declVar(node.id.name, 'let', node);
94
+ break;
95
+
80
96
  case 'FunctionDeclaration':
97
+ if (node.id?.name) if (strict) {
98
+ declVar(node.id.name, 'let', node);
99
+ } else {
100
+ declVar(node.id.name, 'var', node);
101
+ }
81
102
  case 'FunctionExpression':
82
103
  case 'ArrowFunctionExpression':
83
104
  scopes.lastFuncs.push(scopes.length);
105
+ scopes.push(node);
106
+ openedScope = true;
107
+
108
+ for (const p of node.params) analyzePattern('var', p);
84
109
  break;
85
110
  }
86
111
 
87
112
  for (const x in node) {
88
113
  if (node[x] != null && typeof node[x] === 'object') {
89
- if (node[x].type) analyze(node[x]);
114
+ if (node[x].type) analyze(node[x], strict || top._strict);
90
115
  if (Array.isArray(node[x])) {
91
- for (const y of node[x]) analyze(y);
116
+ for (const y of node[x]) analyze(y, strict || top._strict);
92
117
  }
93
118
  }
94
119
  }
@@ -138,19 +163,21 @@ const annotate = node => {
138
163
  case 'CallExpression':
139
164
  if (node.callee.name === 'eval' || (node.callee.type === 'SequenceExpression' && node.callee.expressions.at(-1)?.name === 'eval')) {
140
165
  if (node.callee.type === 'SequenceExpression' || node.optional) {
141
- // indirect eval, no scopes
142
- node._semanticScopes = [ node ];
166
+ // indirect eval, only top scope
167
+ node._semanticScopes = [ scopes[0], node ];
143
168
  node._semanticScopes.lastFuncs = [ 0 ];
144
169
  } else {
145
170
  // direct eval, use existing scope
146
171
  node._semanticScopes = Object.assign([], scopes);
172
+ node._semanticScopes.push(node);
147
173
  }
148
174
  }
149
175
 
150
176
  case 'NewExpression':
151
177
  if (node.callee.name === 'Function') {
152
- // todo: this is probably wrong and needs to add own new scope
153
- node._semanticScopes = Object.assign([], scopes);
178
+ // new Function(...) - use global scope and self as scope
179
+ node._semanticScopes = [ scopes[0], node ];
180
+ node._semanticScopes.lastFuncs = [ 0, 1 ];
154
181
  }
155
182
  break;
156
183
  }
package/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@honk/porffor",
3
- "version": "0.60.24",
3
+ "version": "0.60.26",
4
4
  "exports": "./compiler/wrap.js",
5
5
  "publish": {
6
6
  "exclude": [
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "porffor",
3
3
  "description": "An ahead-of-time JavaScript compiler",
4
- "version": "0.60.24",
4
+ "version": "0.60.26",
5
5
  "author": "Oliver Medhurst <honk@goose.icu>",
6
6
  "license": "MIT",
7
7
  "scripts": {},
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.24';
3
+ globalThis.version = '0.60.26';
4
4
 
5
5
  // deno compat
6
6
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
package/runtime/lambda.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import fs from 'node:fs';
3
3
  import { execSync } from 'node:child_process';
4
4
  import compile from '../compiler/wrap.js';
5
+ import { log } from '../compiler/log.js';
5
6
 
6
7
  // Parse arguments
7
8
  const args = process.argv.slice(2);
@@ -29,8 +30,24 @@ Prefs.lambda = true;
29
30
 
30
31
  compile(source, true);
31
32
 
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' });
33
+ let compiler = process.env.CC;
34
+ if (!compiler) {
35
+ try {
36
+ execSync('musl-gcc --version');
37
+ compiler = 'musl-gcc';
38
+ } catch {
39
+ // todo: this should be in future docs somewhere and linked instead
40
+ log.warning('lambda', `musl-gcc is recommended for Lambda as it noticably improves deployed performance. install it or explicitly specify a compiler via CC to hide this warning. defaulting to gcc.`);
41
+ compiler = 'gcc';
42
+ }
43
+ }
44
+
45
+ if (Prefs.d) {
46
+ execSync(`${compiler} -lm -O0 -g -flto -march=x86-64-v3 -o ${binaryPath} ${cPath}`, { stdio: 'inherit' });
47
+ } else {
48
+ execSync(`${compiler} ${compiler === 'musl-gcc' ? '-static' : ''} -lm -O3 -fomit-frame-pointer -s -flto -march=x86-64-v3 -ffunction-sections -fdata-sections -Wl,--gc-sections -o ${binaryPath} ${cPath}`, { stdio: 'inherit' });
49
+ execSync(`strip ${binaryPath}`, { stdio: 'inherit' });
50
+ }
34
51
 
35
52
  try {
36
53
  execSync(`zip -r "${outputZip}" ${binaryPath}`, { stdio: 'inherit' });