@silverbulletmd/silverbullet 2.9.0 → 2.11.0
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/README.md +38 -25
- package/client/asset_bundle/bundle.ts +0 -1
- package/client/config.ts +343 -0
- package/client/markdown_parser/constants.ts +3 -1
- package/client/plugos/hooks/command.ts +0 -5
- package/client/plugos/hooks/event.ts +0 -6
- package/client/plugos/hooks/mq.ts +0 -3
- package/client/plugos/hooks/slash_command.ts +1 -7
- package/client/plugos/hooks/syscall.ts +24 -14
- package/client/plugos/manifest_cache.ts +0 -32
- package/client/plugos/plug.ts +0 -3
- package/client/plugos/plug_compile.ts +3 -13
- package/client/plugos/sandboxes/worker_sandbox.ts +19 -9
- package/client/plugos/syscalls/asset.ts +61 -23
- package/client/plugos/syscalls/clientStore.ts +32 -6
- package/client/plugos/syscalls/client_code_widget.ts +9 -4
- package/client/plugos/syscalls/code_widget.ts +26 -11
- package/client/plugos/syscalls/config.ts +146 -30
- package/client/plugos/syscalls/datastore.ts +146 -40
- package/client/plugos/syscalls/editor.ts +1518 -722
- package/client/plugos/syscalls/event.ts +44 -13
- package/client/plugos/syscalls/fetch.ts +120 -83
- package/client/plugos/syscalls/icon.ts +46 -0
- package/client/plugos/syscalls/index.ts +267 -121
- package/client/plugos/syscalls/jsonschema.ts +107 -9
- package/client/plugos/syscalls/language.ts +38 -12
- package/client/plugos/syscalls/markdown.ts +166 -50
- package/client/plugos/syscalls/mq.ts +92 -20
- package/client/plugos/syscalls/navigator.ts +143 -0
- package/client/plugos/syscalls/schema_introspection.ts +33 -0
- package/client/plugos/syscalls/search.ts +44 -0
- package/client/plugos/syscalls/service_registry.ts +74 -20
- package/client/plugos/syscalls/shell.ts +51 -21
- package/client/plugos/syscalls/space.ts +412 -113
- package/client/plugos/syscalls/sync.ts +103 -20
- package/client/plugos/syscalls/system.ts +274 -139
- package/client/plugos/system.ts +28 -10
- package/client/plugos/worker_runtime.ts +0 -2
- package/client/space_lua/aggregates.ts +0 -16
- package/client/space_lua/api_documentation.ts +107 -0
- package/client/space_lua/ast.ts +13 -5
- package/client/space_lua/ast_narrow.ts +0 -5
- package/client/space_lua/budget.ts +113 -0
- package/client/space_lua/budget_ui.ts +49 -0
- package/client/space_lua/eval.ts +125 -92
- package/client/space_lua/labels.ts +0 -4
- package/client/space_lua/numeric.ts +0 -5
- package/client/space_lua/parse.ts +243 -125
- package/client/space_lua/pretty_print.ts +128 -54
- package/client/space_lua/quarantine.ts +127 -0
- package/client/space_lua/query_collection.ts +7 -51
- package/client/space_lua/query_env.ts +0 -2
- package/client/space_lua/render_lua_markdown.ts +2 -7
- package/client/space_lua/render_widget.ts +173 -0
- package/client/space_lua/runtime.ts +133 -92
- package/client/space_lua/stdlib/crypto.ts +8 -3
- package/client/space_lua/stdlib/encoding.ts +27 -9
- package/client/space_lua/stdlib/format.ts +0 -12
- package/client/space_lua/stdlib/js.ts +136 -23
- package/client/space_lua/stdlib/load.ts +24 -16
- package/client/space_lua/stdlib/math.ts +331 -133
- package/client/space_lua/stdlib/net.ts +57 -13
- package/client/space_lua/stdlib/os.ts +111 -54
- package/client/space_lua/stdlib/pattern.ts +10 -15
- package/client/space_lua/stdlib/space_lua.ts +344 -21
- package/client/space_lua/stdlib/string.ts +282 -104
- package/client/space_lua/stdlib/string_pack.ts +58 -22
- package/client/space_lua/stdlib/table.ts +195 -45
- package/client/space_lua/stdlib.ts +354 -125
- package/client/space_lua/syscalls.ts +270 -0
- package/client/space_lua/tonumber.ts +0 -9
- package/dist/plug-compile.js +2 -3
- package/package.json +15 -5
- package/plug-api/lib/async.ts +1 -6
- package/plug-api/lib/collation.ts +22 -0
- package/plug-api/lib/crypto.ts +16 -15
- package/plug-api/lib/dates.ts +33 -0
- package/plug-api/lib/fuzzy.ts +282 -0
- package/plug-api/lib/json.ts +0 -6
- package/plug-api/lib/limited_map.ts +0 -2
- package/plug-api/lib/link_write.ts +27 -0
- package/plug-api/{ui → lib}/panel_styles.ts +4 -3
- package/plug-api/lib/ref.ts +124 -5
- package/plug-api/lib/resolve.ts +0 -1
- package/plug-api/lib/resolve_path.ts +290 -0
- package/plug-api/lib/shortcut.ts +17 -1
- package/plug-api/lib/tags.ts +0 -3
- package/plug-api/lib/transclusion.ts +28 -1
- package/plug-api/lib/tree.ts +0 -5
- package/plug-api/lib/yaml.ts +3 -42
- package/plug-api/syscall.ts +1 -5
- package/plug-api/syscalls/config.ts +7 -5
- package/plug-api/syscalls/editor.ts +70 -208
- package/plug-api/syscalls/icon.ts +18 -0
- package/plug-api/syscalls/index.ts +39 -5
- package/plug-api/syscalls/jsonschema.ts +9 -0
- package/plug-api/syscalls/lua.ts +7 -0
- package/plug-api/syscalls/search.ts +11 -0
- package/plug-api/syscalls/shell.ts +0 -1
- package/plug-api/syscalls/space.ts +122 -5
- package/plug-api/syscalls/sync.ts +23 -2
- package/plug-api/syscalls/system.ts +53 -0
- package/plug-api/syscalls.ts +2 -0
- package/plug-api/system_mock.ts +15 -2
- package/plug-api/types/client.ts +4 -3
- package/plug-api/types/datastore.ts +11 -2
- package/plug-api/types/index.ts +62 -1
- package/plug-api/types/manifest.ts +22 -4
- package/plug-api/types/profile.ts +13 -0
- package/plug-api/types/revisions.ts +67 -0
- package/plug-api/ui/cx.ts +1 -3
- package/plug-api/ui/description.ts +64 -0
- package/plug-api/ui/hover.ts +69 -0
- package/plug-api/ui/index.ts +61 -2
- package/plug-api/ui/scroll.ts +30 -0
- package/plug-api/ui/slugify.ts +44 -0
- package/plug-api/ui/tree_model.ts +263 -0
- package/plug-api/ui/tree_types.ts +66 -0
- package/plug-api/ui/use_fit_collapse.ts +67 -0
- package/plugs/builtin_plugs.ts +0 -1
- package/plugs/index/types.ts +9 -0
- package/client/plugos/syscalls/lua.ts +0 -81
- package/plug-api/lib/memory_cache.ts +0 -21
package/client/space_lua/eval.ts
CHANGED
|
@@ -7,7 +7,9 @@ import type {
|
|
|
7
7
|
LuaTableField,
|
|
8
8
|
NumericType,
|
|
9
9
|
} from "./ast.ts";
|
|
10
|
+
import type { Config } from "../config.ts";
|
|
10
11
|
import { LuaAttribute } from "./ast.ts";
|
|
12
|
+
import { budgetTick, LuaBudgetStopped } from "./budget.ts";
|
|
11
13
|
import { evalPromiseValues } from "./util.ts";
|
|
12
14
|
import {
|
|
13
15
|
getMetatable,
|
|
@@ -91,6 +93,11 @@ import { getBlockGotoMeta } from "./labels.ts";
|
|
|
91
93
|
|
|
92
94
|
const astNumberKindCache = new WeakMap<LuaExpression, NumericType>();
|
|
93
95
|
|
|
96
|
+
function getClientConfig(): Config | undefined {
|
|
97
|
+
return (globalThis as typeof globalThis & { client?: { config: Config } })
|
|
98
|
+
.client?.config;
|
|
99
|
+
}
|
|
100
|
+
|
|
94
101
|
function astNumberKind(e: LuaExpression | undefined): NumericType | undefined {
|
|
95
102
|
if (!e) return undefined;
|
|
96
103
|
|
|
@@ -258,7 +265,6 @@ export function luaOp(
|
|
|
258
265
|
): any {
|
|
259
266
|
switch (op) {
|
|
260
267
|
case "+": {
|
|
261
|
-
// Ultra-fast path: both plain numbers with no float type annotation (int + int)
|
|
262
268
|
if (
|
|
263
269
|
typeof left === "number" &&
|
|
264
270
|
typeof right === "number" &&
|
|
@@ -308,11 +314,9 @@ export function luaOp(
|
|
|
308
314
|
);
|
|
309
315
|
}
|
|
310
316
|
case "..": {
|
|
311
|
-
// Fast path: string .. string (most common in SilverBullet — key building, templates)
|
|
312
317
|
if (typeof left === "string" && typeof right === "string") {
|
|
313
318
|
return left + right;
|
|
314
319
|
}
|
|
315
|
-
// Fast path: string .. number or number .. string
|
|
316
320
|
if (typeof left === "string" && typeof right === "number") {
|
|
317
321
|
return left + luaFormatNumber(right);
|
|
318
322
|
}
|
|
@@ -352,7 +356,6 @@ export function luaOp(
|
|
|
352
356
|
}
|
|
353
357
|
}
|
|
354
358
|
case "==": {
|
|
355
|
-
// Fast path for same-type primitives
|
|
356
359
|
if (typeof left === typeof right && typeof left !== "object") {
|
|
357
360
|
return left === right;
|
|
358
361
|
}
|
|
@@ -370,11 +373,9 @@ export function luaOp(
|
|
|
370
373
|
return !luaEqWithMetamethod(left, right, ctx, sf);
|
|
371
374
|
}
|
|
372
375
|
case "<": {
|
|
373
|
-
// Fast path: both plain numbers
|
|
374
376
|
if (typeof left === "number" && typeof right === "number") {
|
|
375
377
|
return left < right;
|
|
376
378
|
}
|
|
377
|
-
// Fast path: both strings
|
|
378
379
|
if (typeof left === "string" && typeof right === "string") {
|
|
379
380
|
return left < right;
|
|
380
381
|
}
|
|
@@ -404,7 +405,6 @@ export function luaOp(
|
|
|
404
405
|
}
|
|
405
406
|
}
|
|
406
407
|
|
|
407
|
-
// Remaining operators: //, %, bitwise
|
|
408
408
|
const handler = operatorsMetaMethods[op];
|
|
409
409
|
if (!handler) {
|
|
410
410
|
throw new LuaRuntimeError(`Unknown operator ${op}`, sf.withCtx(ctx));
|
|
@@ -820,7 +820,6 @@ async function evalCrossJoinSources(
|
|
|
820
820
|
sf: LuaStackFrame,
|
|
821
821
|
ctx: ASTCtx,
|
|
822
822
|
): Promise<LuaTable[]> {
|
|
823
|
-
// Evaluate each source and normalize to arrays
|
|
824
823
|
const arrays: { name: string; items: any[] }[] = [];
|
|
825
824
|
for (const src of sources) {
|
|
826
825
|
const val = await evalExpression(src.expression, env, sf);
|
|
@@ -834,7 +833,6 @@ async function evalCrossJoinSources(
|
|
|
834
833
|
arrays.push({ name: src.name, items });
|
|
835
834
|
}
|
|
836
835
|
|
|
837
|
-
// Cartesian product
|
|
838
836
|
let product: Record<string, any>[] = [{}];
|
|
839
837
|
for (const { name, items } of arrays) {
|
|
840
838
|
const newProduct: Record<string, any>[] = [];
|
|
@@ -846,7 +844,6 @@ async function evalCrossJoinSources(
|
|
|
846
844
|
product = newProduct;
|
|
847
845
|
}
|
|
848
846
|
|
|
849
|
-
// Convert each combination to a `LuaTable` row
|
|
850
847
|
return product.map((combo) => {
|
|
851
848
|
const row = new LuaTable();
|
|
852
849
|
for (const key in combo) {
|
|
@@ -894,7 +891,6 @@ export function evalExpression(
|
|
|
894
891
|
case "Unary": {
|
|
895
892
|
const u = asUnary(e);
|
|
896
893
|
|
|
897
|
-
// Fast path: negation of numeric literal
|
|
898
894
|
if (u.operator === "-" && u.argument.type === "Number") {
|
|
899
895
|
const num = u.argument;
|
|
900
896
|
if (num.value === 0) {
|
|
@@ -914,7 +910,6 @@ export function evalExpression(
|
|
|
914
910
|
const arg = singleResult(typed.value);
|
|
915
911
|
|
|
916
912
|
return unaryWithMeta(arg, "__unm", u.ctx, sf, () => {
|
|
917
|
-
// Numeric-string coercion for unary minus
|
|
918
913
|
if (typeof arg === "string") {
|
|
919
914
|
const n = coerceToNumber(arg);
|
|
920
915
|
if (n === null) {
|
|
@@ -1015,7 +1010,8 @@ export function evalExpression(
|
|
|
1015
1010
|
case "Variable":
|
|
1016
1011
|
case "FunctionCall":
|
|
1017
1012
|
case "TableAccess":
|
|
1018
|
-
case "PropertyAccess":
|
|
1013
|
+
case "PropertyAccess":
|
|
1014
|
+
case "Parenthesized": {
|
|
1019
1015
|
return evalPrefixExpression(e, env, sf);
|
|
1020
1016
|
}
|
|
1021
1017
|
case "TableConstructor": {
|
|
@@ -1108,7 +1104,6 @@ export function evalExpression(
|
|
|
1108
1104
|
);
|
|
1109
1105
|
|
|
1110
1106
|
if (fromSource.kind === "cross") {
|
|
1111
|
-
// Materialize Cartesian product, then query
|
|
1112
1107
|
return (async () => {
|
|
1113
1108
|
const rows = await evalCrossJoinSources(
|
|
1114
1109
|
fromSource.sources,
|
|
@@ -1118,13 +1113,11 @@ export function evalExpression(
|
|
|
1118
1113
|
);
|
|
1119
1114
|
const collection: any = toCollection(rows);
|
|
1120
1115
|
|
|
1121
|
-
// Build up query object
|
|
1122
1116
|
const query: LuaCollectionQuery = {
|
|
1123
1117
|
objectVariable: undefined,
|
|
1124
1118
|
distinct: true,
|
|
1125
1119
|
};
|
|
1126
1120
|
|
|
1127
|
-
// Map clauses to query parameters
|
|
1128
1121
|
for (const clause of q.clauses) {
|
|
1129
1122
|
switch (clause.type) {
|
|
1130
1123
|
case "Where": {
|
|
@@ -1178,12 +1171,11 @@ export function evalExpression(
|
|
|
1178
1171
|
}
|
|
1179
1172
|
|
|
1180
1173
|
return (collection as any)
|
|
1181
|
-
.query(query, env, sf,
|
|
1174
|
+
.query(query, env, sf, getClientConfig())
|
|
1182
1175
|
.then(jsToLuaValue);
|
|
1183
1176
|
})();
|
|
1184
1177
|
}
|
|
1185
1178
|
|
|
1186
|
-
// Single-source
|
|
1187
1179
|
const { objectVariable, expression: objectExpression } = fromSource;
|
|
1188
1180
|
return Promise.resolve(evalExpression(objectExpression, env, sf)).then(
|
|
1189
1181
|
async (collection: LuaValue) => {
|
|
@@ -1199,33 +1191,27 @@ export function evalExpression(
|
|
|
1199
1191
|
"query" in collection &&
|
|
1200
1192
|
typeof (collection as any).query === "function"
|
|
1201
1193
|
) {
|
|
1202
|
-
// Already queryable, use as-is
|
|
1203
1194
|
} else if (collection instanceof LuaTable && collection.empty()) {
|
|
1204
|
-
// Empty table → empty array
|
|
1205
1195
|
collection = toCollection([]);
|
|
1206
1196
|
} else if (collection instanceof LuaTable) {
|
|
1207
1197
|
if (collection.length > 0) {
|
|
1208
|
-
// Array-like table: extract array items, keep as LuaTables
|
|
1209
1198
|
const arr: any[] = [];
|
|
1210
1199
|
for (let i = 1; i <= collection.length; i++) {
|
|
1211
1200
|
arr.push(collection.rawGet(i));
|
|
1212
1201
|
}
|
|
1213
1202
|
collection = toCollection(arr);
|
|
1214
1203
|
} else {
|
|
1215
|
-
// Record-like table (no array part): treat as singleton
|
|
1216
1204
|
collection = toCollection([collection]);
|
|
1217
1205
|
}
|
|
1218
1206
|
} else {
|
|
1219
1207
|
collection = toCollection(luaValueToJS(collection, sf));
|
|
1220
1208
|
}
|
|
1221
1209
|
|
|
1222
|
-
// Build up query object
|
|
1223
1210
|
const query: LuaCollectionQuery = {
|
|
1224
1211
|
objectVariable,
|
|
1225
1212
|
distinct: true,
|
|
1226
1213
|
};
|
|
1227
1214
|
|
|
1228
|
-
// Map clauses to query parameters
|
|
1229
1215
|
for (const clause of q.clauses) {
|
|
1230
1216
|
switch (clause.type) {
|
|
1231
1217
|
case "Where": {
|
|
@@ -1278,9 +1264,8 @@ export function evalExpression(
|
|
|
1278
1264
|
}
|
|
1279
1265
|
}
|
|
1280
1266
|
|
|
1281
|
-
// Always use the possibly-wrapped collection
|
|
1282
1267
|
return (collection as any)
|
|
1283
|
-
.query(query, env, sf,
|
|
1268
|
+
.query(query, env, sf, getClientConfig())
|
|
1284
1269
|
.then(jsToLuaValue);
|
|
1285
1270
|
},
|
|
1286
1271
|
);
|
|
@@ -1292,12 +1277,12 @@ export function evalExpression(
|
|
|
1292
1277
|
);
|
|
1293
1278
|
}
|
|
1294
1279
|
} catch (err: any) {
|
|
1295
|
-
//
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
} else {
|
|
1280
|
+
// instanceof, not constructor.name: the production build minifies class
|
|
1281
|
+
// names, so a name-based test silently fails in the shipped client.
|
|
1282
|
+
if (err instanceof LuaRuntimeError || err instanceof LuaBudgetStopped) {
|
|
1299
1283
|
throw err;
|
|
1300
1284
|
}
|
|
1285
|
+
throw new LuaRuntimeError(err.message, sf.withCtx(e.ctx), err);
|
|
1301
1286
|
}
|
|
1302
1287
|
}
|
|
1303
1288
|
|
|
@@ -1318,10 +1303,9 @@ function evalPrefixExpression(
|
|
|
1318
1303
|
|
|
1319
1304
|
case "Parenthesized": {
|
|
1320
1305
|
const p = asParenthesized(e);
|
|
1321
|
-
return evalExpression(p.expression, env, sf);
|
|
1306
|
+
return rpThen(evalExpression(p.expression, env, sf), singleResult);
|
|
1322
1307
|
}
|
|
1323
1308
|
|
|
1324
|
-
// <<expr>>[<<expr>>]
|
|
1325
1309
|
case "TableAccess": {
|
|
1326
1310
|
const ta = asTableAccess(e);
|
|
1327
1311
|
// Sync-first: evaluate object and key without allocating Promise when both are sync.
|
|
@@ -1341,7 +1325,6 @@ function evalPrefixExpression(
|
|
|
1341
1325
|
);
|
|
1342
1326
|
}
|
|
1343
1327
|
|
|
1344
|
-
// <expr>.property
|
|
1345
1328
|
case "PropertyAccess": {
|
|
1346
1329
|
const pa = asPropertyAccess(e);
|
|
1347
1330
|
// Sync-first: evaluate object; avoid Promise when object is sync.
|
|
@@ -1377,7 +1360,6 @@ function evalPrefixExpression(
|
|
|
1377
1360
|
throw new LuaRuntimeError(nilMsg, sf.withCtx(fc.prefix.ctx));
|
|
1378
1361
|
}
|
|
1379
1362
|
|
|
1380
|
-
// Fast path: non-method call with sync prefix
|
|
1381
1363
|
if (!fc.name && !isPromise(prefixValue)) {
|
|
1382
1364
|
const argsVal = evalExpressions(fc.args, env, sf);
|
|
1383
1365
|
if (!isPromise(argsVal)) {
|
|
@@ -1392,7 +1374,6 @@ function evalPrefixExpression(
|
|
|
1392
1374
|
calleeVal: LuaValue,
|
|
1393
1375
|
selfArgs: LuaValue[],
|
|
1394
1376
|
): LuaValue | Promise<LuaValue> => {
|
|
1395
|
-
// Normal argument handling for hello:there(a, b, c) type calls
|
|
1396
1377
|
if (fc.name) {
|
|
1397
1378
|
const self = calleeVal;
|
|
1398
1379
|
calleeVal = luaIndexValue(calleeVal, fc.name, sf);
|
|
@@ -1440,7 +1421,6 @@ function evalPrefixExpression(
|
|
|
1440
1421
|
}
|
|
1441
1422
|
}
|
|
1442
1423
|
|
|
1443
|
-
// Helper functions to reduce duplication
|
|
1444
1424
|
function evalMetamethod(
|
|
1445
1425
|
left: any,
|
|
1446
1426
|
right: any,
|
|
@@ -1465,7 +1445,6 @@ function evalMetamethod(
|
|
|
1465
1445
|
}
|
|
1466
1446
|
}
|
|
1467
1447
|
|
|
1468
|
-
// Unary metamethod lookup and call
|
|
1469
1448
|
function evalUnaryMetamethod(
|
|
1470
1449
|
value: any,
|
|
1471
1450
|
metaMethod: "__unm" | "__bnot",
|
|
@@ -1483,7 +1462,6 @@ function evalUnaryMetamethod(
|
|
|
1483
1462
|
return luaCall(fn, [value], ctx, sf);
|
|
1484
1463
|
}
|
|
1485
1464
|
|
|
1486
|
-
// Unary metamethod handling (with fallback)
|
|
1487
1465
|
function unaryWithMeta(
|
|
1488
1466
|
arg: any,
|
|
1489
1467
|
meta: "__unm" | "__bnot",
|
|
@@ -1501,7 +1479,6 @@ function unaryWithMeta(
|
|
|
1501
1479
|
return fallback();
|
|
1502
1480
|
}
|
|
1503
1481
|
|
|
1504
|
-
// Logical short-circuit evaluation
|
|
1505
1482
|
function evalLogical(
|
|
1506
1483
|
op: "and" | "or",
|
|
1507
1484
|
leftExpr: LuaExpression,
|
|
@@ -1770,12 +1747,10 @@ function luaRelWithMetamethod(
|
|
|
1770
1747
|
* - throw error otherwise.
|
|
1771
1748
|
*/
|
|
1772
1749
|
function luaLengthOp(val: any, ctx: ASTCtx, sf: LuaStackFrame): LuaValue {
|
|
1773
|
-
// Strings: ignore `__len`
|
|
1774
1750
|
if (typeof val === "string") {
|
|
1775
1751
|
return val.length;
|
|
1776
1752
|
}
|
|
1777
1753
|
|
|
1778
|
-
// Tables: prefer metatable `__len` to raw length
|
|
1779
1754
|
if (val instanceof LuaTable) {
|
|
1780
1755
|
const mt = getMetatable(val, sf);
|
|
1781
1756
|
if (mt) {
|
|
@@ -1787,7 +1762,6 @@ function luaLengthOp(val: any, ctx: ASTCtx, sf: LuaStackFrame): LuaValue {
|
|
|
1787
1762
|
return val.length;
|
|
1788
1763
|
}
|
|
1789
1764
|
|
|
1790
|
-
// Other values: allow metatable `__len` first
|
|
1791
1765
|
{
|
|
1792
1766
|
const mt = getMetatable(val, sf);
|
|
1793
1767
|
if (mt) {
|
|
@@ -1798,12 +1772,10 @@ function luaLengthOp(val: any, ctx: ASTCtx, sf: LuaStackFrame): LuaValue {
|
|
|
1798
1772
|
}
|
|
1799
1773
|
}
|
|
1800
1774
|
|
|
1801
|
-
// JS arrays (interop): length if no `__len` override
|
|
1802
1775
|
if (Array.isArray(val)) {
|
|
1803
1776
|
return val.length;
|
|
1804
1777
|
}
|
|
1805
1778
|
|
|
1806
|
-
// Otherwise error with type
|
|
1807
1779
|
const t = luaTypeOf(val) as LuaType;
|
|
1808
1780
|
throw new LuaRuntimeError(
|
|
1809
1781
|
`attempt to get length of a ${t} value`,
|
|
@@ -1819,7 +1791,6 @@ function evalExpressions(
|
|
|
1819
1791
|
const len = es.length;
|
|
1820
1792
|
if (len === 0) return [];
|
|
1821
1793
|
|
|
1822
|
-
// Evaluate all arguments (sync-first); avoid .map() closure overhead
|
|
1823
1794
|
const parts = new Array(len);
|
|
1824
1795
|
for (let i = 0; i < len; i++) {
|
|
1825
1796
|
parts[i] = evalExpression(es[i], env, sf);
|
|
@@ -1830,11 +1801,9 @@ function evalExpressions(
|
|
|
1830
1801
|
const finalize = (argsResolved: any[]) => {
|
|
1831
1802
|
const out: LuaValue[] = [];
|
|
1832
1803
|
const lastIdx = argsResolved.length - 1;
|
|
1833
|
-
// All but last expression produce a single value
|
|
1834
1804
|
for (let i = 0; i < lastIdx; i++) {
|
|
1835
1805
|
out.push(singleResult(argsResolved[i]));
|
|
1836
1806
|
}
|
|
1837
|
-
// Last expression preserves multiple results
|
|
1838
1807
|
const last = argsResolved[lastIdx];
|
|
1839
1808
|
if (last instanceof LuaMultiRes) {
|
|
1840
1809
|
out.push(...last.flatten().values);
|
|
@@ -1866,6 +1835,8 @@ function runStatementsNoGoto(
|
|
|
1866
1835
|
const result = evalStatement(stmts[i], execEnv, sf, returnOnReturn);
|
|
1867
1836
|
if (isPromise(result)) {
|
|
1868
1837
|
return (result as Promise<any>).then((res) => {
|
|
1838
|
+
const budget = sf.threadState.budget;
|
|
1839
|
+
if (budget !== undefined) budget.awaited = true;
|
|
1869
1840
|
if (res !== undefined) {
|
|
1870
1841
|
if (isGotoSignal(res)) {
|
|
1871
1842
|
throw new LuaRuntimeError(
|
|
@@ -1894,6 +1865,35 @@ function runStatementsNoGoto(
|
|
|
1894
1865
|
return processFrom(startIdx);
|
|
1895
1866
|
}
|
|
1896
1867
|
|
|
1868
|
+
function closeThenRethrow(
|
|
1869
|
+
sf: LuaStackFrame,
|
|
1870
|
+
mark: number,
|
|
1871
|
+
e: any,
|
|
1872
|
+
): never | Promise<never> {
|
|
1873
|
+
const errObj: LuaValue =
|
|
1874
|
+
e instanceof LuaRuntimeError ? e.message : (e?.message ?? String(e));
|
|
1875
|
+
// Lua 5.4 lets an error from __close replace the one being unwound, but a
|
|
1876
|
+
// user-initiated stop is not a Lua error and outranks it.
|
|
1877
|
+
const preferOriginal = (closeErr: any): never => {
|
|
1878
|
+
if (e instanceof LuaBudgetStopped) {
|
|
1879
|
+
throw e;
|
|
1880
|
+
}
|
|
1881
|
+
throw closeErr;
|
|
1882
|
+
};
|
|
1883
|
+
let r: void | Promise<void>;
|
|
1884
|
+
try {
|
|
1885
|
+
r = luaCloseFromMark(sf, mark, errObj);
|
|
1886
|
+
} catch (closeErr: any) {
|
|
1887
|
+
return preferOriginal(closeErr);
|
|
1888
|
+
}
|
|
1889
|
+
if (isPromise(r)) {
|
|
1890
|
+
return (r as Promise<void>).then(() => {
|
|
1891
|
+
throw e;
|
|
1892
|
+
}, preferOriginal);
|
|
1893
|
+
}
|
|
1894
|
+
throw e;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
1897
|
function withCloseBoundary(
|
|
1898
1898
|
sf: LuaStackFrame,
|
|
1899
1899
|
mark: number,
|
|
@@ -1914,17 +1914,7 @@ function withCloseBoundary(
|
|
|
1914
1914
|
return isPromise(r) ? (r as Promise<void>).then(() => res) : res;
|
|
1915
1915
|
};
|
|
1916
1916
|
|
|
1917
|
-
const onRejected = (e: any) =>
|
|
1918
|
-
const errObj: LuaValue =
|
|
1919
|
-
e instanceof LuaRuntimeError ? e.message : (e?.message ?? String(e));
|
|
1920
|
-
const r = luaCloseFromMark(sf, mark, errObj);
|
|
1921
|
-
if (isPromise(r)) {
|
|
1922
|
-
return (r as Promise<void>).then(() => {
|
|
1923
|
-
throw e;
|
|
1924
|
-
});
|
|
1925
|
-
}
|
|
1926
|
-
throw e;
|
|
1927
|
-
};
|
|
1917
|
+
const onRejected = (e: any) => closeThenRethrow(sf, mark, e);
|
|
1928
1918
|
|
|
1929
1919
|
return p.then(onFulfilled, onRejected);
|
|
1930
1920
|
}
|
|
@@ -1964,6 +1954,8 @@ function evalBlockNoClose(
|
|
|
1964
1954
|
const r = evalStatement(stmts[i], execEnv, sf, returnOnReturn);
|
|
1965
1955
|
if (isPromise(r)) {
|
|
1966
1956
|
return (r as Promise<any>).then((res) => {
|
|
1957
|
+
const budget = sf.threadState.budget;
|
|
1958
|
+
if (budget !== undefined) budget.awaited = true;
|
|
1967
1959
|
if (isGotoSignal(res)) return res;
|
|
1968
1960
|
if (res !== undefined) return res;
|
|
1969
1961
|
return runFrom(i + 1);
|
|
@@ -2004,11 +1996,13 @@ function evalBlockNoClose(
|
|
|
2004
1996
|
const execEnv = b.needsEnv === true ? new LuaEnv(env) : env;
|
|
2005
1997
|
const stmts = b.statements;
|
|
2006
1998
|
|
|
1999
|
+
const budget = sf.threadState.budget;
|
|
2007
2000
|
const runFrom = (i: number): EvalBlockResult => {
|
|
2008
2001
|
for (; i < stmts.length; i++) {
|
|
2009
2002
|
const r = evalStatement(stmts[i], execEnv, sf, returnOnReturn);
|
|
2010
2003
|
if (isPromise(r)) {
|
|
2011
2004
|
return (r as Promise<any>).then((res) => {
|
|
2005
|
+
if (budget !== undefined) budget.awaited = true;
|
|
2012
2006
|
const consumed = consumeGotoInBlock(res, meta!.labels);
|
|
2013
2007
|
if (typeof consumed === "number") {
|
|
2014
2008
|
return runFrom(consumed);
|
|
@@ -2021,6 +2015,13 @@ function evalBlockNoClose(
|
|
|
2021
2015
|
}
|
|
2022
2016
|
const consumed = consumeGotoInBlock(r, meta.labels);
|
|
2023
2017
|
if (typeof consumed === "number") {
|
|
2018
|
+
if (budget !== undefined && --budget.ticks <= 0) {
|
|
2019
|
+
const y = budgetTick(budget);
|
|
2020
|
+
if (y !== undefined) {
|
|
2021
|
+
const resumeAt = consumed;
|
|
2022
|
+
return rpThen(y, () => runFrom(resumeAt));
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2024
2025
|
i = consumed - 1;
|
|
2025
2026
|
continue;
|
|
2026
2027
|
}
|
|
@@ -2059,10 +2060,15 @@ export function evalStatement(
|
|
|
2059
2060
|
);
|
|
2060
2061
|
|
|
2061
2062
|
const apply = (values: LuaValue[], lvalues: { env: any; key: any }[]) => {
|
|
2062
|
-
// Create the error-reporting frame once, not per-lvalue
|
|
2063
2063
|
let errSf: LuaStackFrame | undefined;
|
|
2064
2064
|
const ps: Promise<any>[] = [];
|
|
2065
2065
|
for (let i = 0; i < lvalues.length; i++) {
|
|
2066
|
+
if (
|
|
2067
|
+
values[i] instanceof LuaFunction &&
|
|
2068
|
+
typeof lvalues[i].key === "string"
|
|
2069
|
+
) {
|
|
2070
|
+
values[i].info.name ??= lvalues[i].key;
|
|
2071
|
+
}
|
|
2066
2072
|
const r = luaSet(
|
|
2067
2073
|
lvalues[i].env,
|
|
2068
2074
|
lvalues[i].key,
|
|
@@ -2115,6 +2121,7 @@ export function evalStatement(
|
|
|
2115
2121
|
}
|
|
2116
2122
|
|
|
2117
2123
|
const bindOne = (name: any, v: LuaValue) => {
|
|
2124
|
+
if (v instanceof LuaFunction) v.info.name ??= name.name;
|
|
2118
2125
|
const isConst = name.attributes?.includes(LuaAttribute.Const) === true;
|
|
2119
2126
|
const isClose = name.attributes?.includes(LuaAttribute.Close) === true;
|
|
2120
2127
|
|
|
@@ -2225,22 +2232,13 @@ export function evalStatement(
|
|
|
2225
2232
|
try {
|
|
2226
2233
|
out = evalBlockNoClose(b, env, sf, returnOnReturn);
|
|
2227
2234
|
} catch (e: any) {
|
|
2228
|
-
|
|
2229
|
-
e instanceof LuaRuntimeError ? e.message : (e?.message ?? String(e));
|
|
2230
|
-
const r = luaCloseFromMark(sf, mark, errObj);
|
|
2231
|
-
if (isPromise(r)) {
|
|
2232
|
-
return (r as Promise<void>).then(() => {
|
|
2233
|
-
throw e;
|
|
2234
|
-
});
|
|
2235
|
-
}
|
|
2236
|
-
throw e;
|
|
2235
|
+
return closeThenRethrow(sf, mark, e);
|
|
2237
2236
|
}
|
|
2238
2237
|
|
|
2239
2238
|
return withCloseBoundary(sf, mark, out);
|
|
2240
2239
|
}
|
|
2241
2240
|
case "If": {
|
|
2242
2241
|
const iff = asIf(s);
|
|
2243
|
-
// Evaluate conditions in order; avoid awaiting when not necessary
|
|
2244
2242
|
const conds = iff.conditions;
|
|
2245
2243
|
|
|
2246
2244
|
const runFrom = (
|
|
@@ -2271,6 +2269,7 @@ export function evalStatement(
|
|
|
2271
2269
|
}
|
|
2272
2270
|
case "While": {
|
|
2273
2271
|
const w = asWhile(s);
|
|
2272
|
+
const budget = sf.threadState.budget;
|
|
2274
2273
|
|
|
2275
2274
|
// Sync-first loop that re-enters sync mode after each async iteration
|
|
2276
2275
|
const runSyncFirst = ():
|
|
@@ -2278,9 +2277,14 @@ export function evalStatement(
|
|
|
2278
2277
|
| ControlSignal
|
|
2279
2278
|
| Promise<undefined | ControlSignal> => {
|
|
2280
2279
|
while (true) {
|
|
2280
|
+
if (budget !== undefined && --budget.ticks <= 0) {
|
|
2281
|
+
const y = budgetTick(budget);
|
|
2282
|
+
if (y !== undefined) return rpThen(y, () => runSyncFirst());
|
|
2283
|
+
}
|
|
2281
2284
|
const c = evalExpression(w.condition, env, sf);
|
|
2282
2285
|
if (isPromise(c)) {
|
|
2283
2286
|
return (c as Promise<any>).then((cv) => {
|
|
2287
|
+
if (budget !== undefined) budget.awaited = true;
|
|
2284
2288
|
if (!luaTruthy(cv)) return;
|
|
2285
2289
|
return rpThen(
|
|
2286
2290
|
evalStatement(w.block, env, sf, returnOnReturn),
|
|
@@ -2297,6 +2301,7 @@ export function evalStatement(
|
|
|
2297
2301
|
const r = evalStatement(w.block, env, sf, returnOnReturn);
|
|
2298
2302
|
if (isPromise(r)) {
|
|
2299
2303
|
return (r as Promise<any>).then((res) => {
|
|
2304
|
+
if (budget !== undefined) budget.awaited = true;
|
|
2300
2305
|
if (res !== undefined) {
|
|
2301
2306
|
return isBreakSignal(res) ? undefined : res;
|
|
2302
2307
|
}
|
|
@@ -2315,6 +2320,7 @@ export function evalStatement(
|
|
|
2315
2320
|
}
|
|
2316
2321
|
case "Repeat": {
|
|
2317
2322
|
const rep = asRepeat(s);
|
|
2323
|
+
const budget = sf.threadState.budget;
|
|
2318
2324
|
|
|
2319
2325
|
// Sync-first loop that re-enters sync mode after each async iteration
|
|
2320
2326
|
const runSyncFirst = ():
|
|
@@ -2322,9 +2328,14 @@ export function evalStatement(
|
|
|
2322
2328
|
| ControlSignal
|
|
2323
2329
|
| Promise<undefined | ControlSignal> => {
|
|
2324
2330
|
while (true) {
|
|
2331
|
+
if (budget !== undefined && --budget.ticks <= 0) {
|
|
2332
|
+
const y = budgetTick(budget);
|
|
2333
|
+
if (y !== undefined) return rpThen(y, () => runSyncFirst());
|
|
2334
|
+
}
|
|
2325
2335
|
const rr = evalStatement(rep.block, env, sf, returnOnReturn);
|
|
2326
2336
|
if (isPromise(rr)) {
|
|
2327
2337
|
return (rr as Promise<any>).then((res) => {
|
|
2338
|
+
if (budget !== undefined) budget.awaited = true;
|
|
2328
2339
|
if (res !== undefined) {
|
|
2329
2340
|
return isBreakSignal(res) ? undefined : res;
|
|
2330
2341
|
}
|
|
@@ -2340,9 +2351,10 @@ export function evalStatement(
|
|
|
2340
2351
|
|
|
2341
2352
|
const c = evalExpression(rep.condition, env, sf);
|
|
2342
2353
|
if (isPromise(c)) {
|
|
2343
|
-
return (c as Promise<any>).then((cv) =>
|
|
2344
|
-
|
|
2345
|
-
|
|
2354
|
+
return (c as Promise<any>).then((cv) => {
|
|
2355
|
+
if (budget !== undefined) budget.awaited = true;
|
|
2356
|
+
return luaTruthy(cv) ? undefined : runSyncFirst();
|
|
2357
|
+
});
|
|
2346
2358
|
}
|
|
2347
2359
|
if (luaTruthy(c)) break;
|
|
2348
2360
|
}
|
|
@@ -2367,7 +2379,6 @@ export function evalStatement(
|
|
|
2367
2379
|
let body = fn.body;
|
|
2368
2380
|
let propNames = fn.name.propNames;
|
|
2369
2381
|
if (fn.name.colonName) {
|
|
2370
|
-
// function hello:there() -> function hello.there(self) transformation
|
|
2371
2382
|
body = {
|
|
2372
2383
|
...fn.body,
|
|
2373
2384
|
parameters: ["self", ...fn.body.parameters],
|
|
@@ -2386,13 +2397,13 @@ export function evalStatement(
|
|
|
2386
2397
|
}
|
|
2387
2398
|
(settable as any).set(
|
|
2388
2399
|
propNames[propNames.length - 1],
|
|
2389
|
-
new LuaFunction(body, env),
|
|
2400
|
+
new LuaFunction(body, env, { name: propNames.join(".") }),
|
|
2390
2401
|
);
|
|
2391
2402
|
return;
|
|
2392
2403
|
}
|
|
2393
2404
|
case "LocalFunction": {
|
|
2394
2405
|
const lf = asLocalFunction(s);
|
|
2395
|
-
env.setLocal(lf.name, new LuaFunction(lf.body, env));
|
|
2406
|
+
env.setLocal(lf.name, new LuaFunction(lf.body, env, { name: lf.name }));
|
|
2396
2407
|
return;
|
|
2397
2408
|
}
|
|
2398
2409
|
case "Return": {
|
|
@@ -2494,10 +2505,22 @@ export function evalStatement(
|
|
|
2494
2505
|
const shouldContinue =
|
|
2495
2506
|
step > 0 ? (i: number) => i <= end : (i: number) => i >= end;
|
|
2496
2507
|
|
|
2508
|
+
const budget = sf.threadState.budget;
|
|
2509
|
+
|
|
2497
2510
|
for (let i = startIndex; shouldContinue(i); i += step) {
|
|
2511
|
+
if (budget !== undefined && --budget.ticks <= 0) {
|
|
2512
|
+
const y = budgetTick(budget);
|
|
2513
|
+
if (y !== undefined) {
|
|
2514
|
+
const resumeAt = i;
|
|
2515
|
+
return rpThen(y, () =>
|
|
2516
|
+
runFromIndex(loopEnv, end, step, resumeAt, loopType),
|
|
2517
|
+
);
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2498
2520
|
const r = executeIteration(loopEnv, i, loopType);
|
|
2499
2521
|
if (isPromise(r)) {
|
|
2500
2522
|
return (r as Promise<any>).then((res) => {
|
|
2523
|
+
if (budget !== undefined) budget.awaited = true;
|
|
2501
2524
|
if (res !== undefined) {
|
|
2502
2525
|
return isBreakSignal(res) ? undefined : res;
|
|
2503
2526
|
}
|
|
@@ -2526,11 +2549,22 @@ export function evalStatement(
|
|
|
2526
2549
|
step > 0 ? (i: number) => i <= end : (i: number) => i >= end;
|
|
2527
2550
|
|
|
2528
2551
|
const loopEnv = new LuaEnv(env);
|
|
2552
|
+
const budget = sf.threadState.budget;
|
|
2529
2553
|
|
|
2530
2554
|
for (let i = start; shouldContinue(i); i += step) {
|
|
2555
|
+
if (budget !== undefined && --budget.ticks <= 0) {
|
|
2556
|
+
const y = budgetTick(budget);
|
|
2557
|
+
if (y !== undefined) {
|
|
2558
|
+
const resumeAt = i;
|
|
2559
|
+
return rpThen(y, () =>
|
|
2560
|
+
runSyncFirst(resumeAt, end, step, loopType),
|
|
2561
|
+
);
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2531
2564
|
const r = executeIteration(loopEnv, i, loopType);
|
|
2532
2565
|
if (isPromise(r)) {
|
|
2533
2566
|
return (r as Promise<any>).then((res) => {
|
|
2567
|
+
if (budget !== undefined) budget.awaited = true;
|
|
2534
2568
|
if (res !== undefined) {
|
|
2535
2569
|
if (isBreakSignal(res)) {
|
|
2536
2570
|
return;
|
|
@@ -2565,6 +2599,8 @@ export function evalStatement(
|
|
|
2565
2599
|
isPromise(endV) ? endV : Promise.resolve(endV),
|
|
2566
2600
|
isPromise(stepV) ? stepV : Promise.resolve(stepV),
|
|
2567
2601
|
]).then(([start, end, step]) => {
|
|
2602
|
+
const budget = sf.threadState.budget;
|
|
2603
|
+
if (budget !== undefined) budget.awaited = true;
|
|
2568
2604
|
return runSyncFirst(
|
|
2569
2605
|
untagNumber(start) as number,
|
|
2570
2606
|
untagNumber(end) as number,
|
|
@@ -2594,8 +2630,6 @@ export function evalStatement(
|
|
|
2594
2630
|
const afterExprs = (resolved: any[]) => {
|
|
2595
2631
|
const iteratorMultiRes = new LuaMultiRes(resolved).flatten();
|
|
2596
2632
|
let iteratorValue: ILuaFunction | any = iteratorMultiRes.values[0];
|
|
2597
|
-
// Handle the case where the iterator is a table and we need
|
|
2598
|
-
// to call the `each` function.
|
|
2599
2633
|
if (Array.isArray(iteratorValue) || iteratorValue instanceof LuaTable) {
|
|
2600
2634
|
iteratorValue = (env.get("each") as ILuaFunction).call(
|
|
2601
2635
|
sf,
|
|
@@ -2622,26 +2656,14 @@ export function evalStatement(
|
|
|
2622
2656
|
luaMarkToBeClosed(sf, closing, fi.ctx);
|
|
2623
2657
|
}
|
|
2624
2658
|
|
|
2625
|
-
const errObjFrom = (e: any): LuaValue =>
|
|
2626
|
-
e instanceof LuaRuntimeError ? e.message : (e?.message ?? String(e));
|
|
2627
|
-
|
|
2628
2659
|
const finish = (res: any) => {
|
|
2629
2660
|
const r = luaCloseFromMark(sf, mark, null);
|
|
2630
2661
|
return isPromise(r) ? (r as Promise<void>).then(() => res) : res;
|
|
2631
2662
|
};
|
|
2632
2663
|
|
|
2633
|
-
const finishErr = (e: any): Promise<never> | never =>
|
|
2634
|
-
|
|
2635
|
-
const r = luaCloseFromMark(sf, mark, errObj);
|
|
2636
|
-
if (isPromise(r)) {
|
|
2637
|
-
return (r as Promise<void>).then(() => {
|
|
2638
|
-
throw e;
|
|
2639
|
-
});
|
|
2640
|
-
}
|
|
2641
|
-
throw e;
|
|
2642
|
-
};
|
|
2664
|
+
const finishErr = (e: any): Promise<never> | never =>
|
|
2665
|
+
closeThenRethrow(sf, mark, e);
|
|
2643
2666
|
|
|
2644
|
-
// Allocate the reusable env once before the loop
|
|
2645
2667
|
const loopEnv = canReuseEnv ? new LuaEnv(env) : null;
|
|
2646
2668
|
|
|
2647
2669
|
const makeIterEnv = (): LuaEnv => {
|
|
@@ -2651,9 +2673,14 @@ export function evalStatement(
|
|
|
2651
2673
|
return new LuaEnv(env);
|
|
2652
2674
|
};
|
|
2653
2675
|
|
|
2676
|
+
const budget = sf.threadState.budget;
|
|
2654
2677
|
// Sync-first loop that re-enters sync mode after each async iteration
|
|
2655
2678
|
const runSyncFirst = (): any => {
|
|
2656
2679
|
while (true) {
|
|
2680
|
+
if (budget !== undefined && --budget.ticks <= 0) {
|
|
2681
|
+
const y = budgetTick(budget);
|
|
2682
|
+
if (y !== undefined) return rpThen(y, () => runSyncFirst());
|
|
2683
|
+
}
|
|
2657
2684
|
const iterCall = luaCall(
|
|
2658
2685
|
iteratorValue,
|
|
2659
2686
|
[state, control],
|
|
@@ -2662,6 +2689,7 @@ export function evalStatement(
|
|
|
2662
2689
|
);
|
|
2663
2690
|
|
|
2664
2691
|
const afterIterCall = (itv: any): any => {
|
|
2692
|
+
if (budget !== undefined) budget.awaited = true;
|
|
2665
2693
|
const iterResult = new LuaMultiRes(itv).flatten();
|
|
2666
2694
|
const nextControl = iterResult.values[0];
|
|
2667
2695
|
if (nextControl === null || nextControl === undefined) {
|
|
@@ -2704,6 +2732,7 @@ export function evalStatement(
|
|
|
2704
2732
|
if (isPromise(r)) {
|
|
2705
2733
|
return (r as Promise<any>)
|
|
2706
2734
|
.then((res) => {
|
|
2735
|
+
if (budget !== undefined) budget.awaited = true;
|
|
2707
2736
|
if (res !== undefined) {
|
|
2708
2737
|
if (isBreakSignal(res)) {
|
|
2709
2738
|
return finish(undefined);
|
|
@@ -2731,7 +2760,11 @@ export function evalStatement(
|
|
|
2731
2760
|
};
|
|
2732
2761
|
|
|
2733
2762
|
if (isPromise(exprVals)) {
|
|
2734
|
-
return (exprVals as Promise<any[]>).then(
|
|
2763
|
+
return (exprVals as Promise<any[]>).then((v) => {
|
|
2764
|
+
const budget = sf.threadState.budget;
|
|
2765
|
+
if (budget !== undefined) budget.awaited = true;
|
|
2766
|
+
return afterExprs(v);
|
|
2767
|
+
});
|
|
2735
2768
|
}
|
|
2736
2769
|
return afterExprs(exprVals as any[]);
|
|
2737
2770
|
}
|