@silverbulletmd/silverbullet 2.4.2 → 2.6.1
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 +19 -4
- package/client/asset_bundle/bundle.ts +3 -9
- package/client/data/datastore.ts +4 -5
- package/client/markdown_parser/constants.ts +5 -4
- package/client/plugos/hooks/code_widget.ts +3 -8
- package/client/plugos/hooks/command.ts +8 -8
- package/client/plugos/hooks/document_editor.ts +10 -15
- package/client/plugos/hooks/event.ts +33 -36
- package/client/plugos/hooks/mq.ts +17 -17
- package/client/plugos/hooks/plug_namespace.ts +3 -8
- package/client/plugos/hooks/slash_command.ts +13 -28
- package/client/plugos/hooks/syscall.ts +3 -3
- package/client/plugos/manifest_cache.ts +22 -15
- package/client/plugos/plug.ts +2 -6
- package/client/plugos/plug_compile.ts +79 -78
- package/client/plugos/protocol.ts +28 -28
- package/client/plugos/proxy_fetch.ts +7 -6
- package/client/plugos/sandboxes/web_worker_sandbox.ts +1 -1
- package/client/plugos/sandboxes/worker_sandbox.ts +18 -18
- package/client/plugos/syscalls/asset.ts +1 -3
- package/client/plugos/syscalls/code_widget.ts +1 -3
- package/client/plugos/syscalls/config.ts +1 -5
- package/client/plugos/syscalls/datastore.ts +1 -1
- package/client/plugos/syscalls/editor.ts +72 -69
- package/client/plugos/syscalls/event.ts +9 -12
- package/client/plugos/syscalls/fetch.ts +31 -23
- package/client/plugos/syscalls/index.ts +10 -1
- package/client/plugos/syscalls/jsonschema.ts +72 -32
- package/client/plugos/syscalls/language.ts +9 -5
- package/client/plugos/syscalls/markdown.ts +29 -7
- package/client/plugos/syscalls/mq.ts +4 -12
- package/client/plugos/syscalls/service_registry.ts +1 -4
- package/client/plugos/syscalls/shell.ts +2 -5
- package/client/plugos/syscalls/space.ts +1 -1
- package/client/plugos/syscalls/sync.ts +69 -60
- package/client/plugos/syscalls/system.ts +2 -3
- package/client/plugos/system.ts +6 -12
- package/client/plugos/worker_runtime.ts +12 -33
- package/client/space_lua/aggregates.ts +782 -0
- package/client/space_lua/ast.ts +42 -8
- package/client/space_lua/ast_narrow.ts +4 -2
- package/client/space_lua/eval.ts +886 -575
- package/client/space_lua/labels.ts +7 -12
- package/client/space_lua/liq_null.ts +6 -0
- package/client/space_lua/numeric.ts +5 -8
- package/client/space_lua/parse.ts +346 -120
- package/client/space_lua/query_collection.ts +926 -82
- package/client/space_lua/query_env.ts +26 -0
- package/client/space_lua/render_lua_markdown.ts +369 -0
- package/client/space_lua/rp.ts +5 -4
- package/client/space_lua/runtime.ts +288 -155
- package/client/space_lua/stdlib/format.ts +53 -39
- package/client/space_lua/stdlib/js.ts +3 -7
- package/client/space_lua/stdlib/load.ts +1 -3
- package/client/space_lua/stdlib/math.ts +84 -58
- package/client/space_lua/stdlib/net.ts +27 -17
- package/client/space_lua/stdlib/os.ts +81 -85
- package/client/space_lua/stdlib/pattern.ts +695 -0
- package/client/space_lua/stdlib/prng.ts +148 -0
- package/client/space_lua/stdlib/space_lua.ts +17 -23
- package/client/space_lua/stdlib/string.ts +102 -190
- package/client/space_lua/stdlib/string_pack.ts +490 -0
- package/client/space_lua/stdlib/table.ts +76 -16
- package/client/space_lua/stdlib.ts +53 -39
- package/client/space_lua/tonumber.ts +82 -42
- package/client/space_lua/util.ts +53 -15
- package/dist/plug-compile.js +55 -98
- package/package.json +27 -20
- package/plug-api/constants.ts +0 -32
- package/plug-api/lib/async.ts +20 -7
- package/plug-api/lib/crypto.ts +16 -17
- package/plug-api/lib/dates.ts +15 -7
- package/plug-api/lib/json.ts +11 -5
- package/plug-api/lib/limited_map.ts +1 -1
- package/plug-api/lib/native_fetch.ts +2 -0
- package/plug-api/lib/ref.ts +23 -23
- package/plug-api/lib/resolve.ts +7 -11
- package/plug-api/lib/tags.ts +13 -4
- package/plug-api/lib/transclusion.ts +10 -21
- package/plug-api/lib/tree.ts +165 -45
- package/plug-api/lib/yaml.ts +35 -25
- package/plug-api/syscalls/asset.ts +1 -1
- package/plug-api/syscalls/config.ts +1 -4
- package/plug-api/syscalls/editor.ts +15 -15
- package/plug-api/syscalls/jsonschema.ts +1 -3
- package/plug-api/syscalls/lua.ts +3 -9
- package/plug-api/syscalls/mq.ts +1 -4
- package/plug-api/syscalls/shell.ts +4 -1
- package/plug-api/syscalls/space.ts +3 -10
- package/plug-api/syscalls/system.ts +1 -4
- package/plug-api/syscalls/yaml.ts +2 -6
- package/plug-api/system_mock.ts +0 -1
- package/plug-api/types/client.ts +16 -1
- package/plug-api/types/event.ts +6 -4
- package/plug-api/types/manifest.ts +8 -9
- package/plugs/builtin_plugs.ts +2 -2
- package/client/plugos/sandboxes/deno_worker_sandbox.ts +0 -6
|
@@ -3,6 +3,7 @@ import { evalStatement } from "./eval.ts";
|
|
|
3
3
|
import { asyncQuickSort } from "./util.ts";
|
|
4
4
|
import { isPromise, rpAll } from "./rp.ts";
|
|
5
5
|
import { isNegativeZero, isTaggedFloat } from "./numeric.ts";
|
|
6
|
+
import { isSqlNull } from "./liq_null.ts";
|
|
6
7
|
import { luaFormat } from "./stdlib/format.ts";
|
|
7
8
|
|
|
8
9
|
export type LuaType =
|
|
@@ -79,7 +80,7 @@ function isLuaNumber(v: any): boolean {
|
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
export function luaTypeName(val: any): LuaType {
|
|
82
|
-
if (val === null || val === undefined) {
|
|
83
|
+
if (val === null || val === undefined || isSqlNull(val)) {
|
|
83
84
|
return "nil";
|
|
84
85
|
}
|
|
85
86
|
|
|
@@ -113,10 +114,7 @@ export function luaTypeName(val: any): LuaType {
|
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
// Check whether a value is callable without invoking it.
|
|
116
|
-
export function luaIsCallable(
|
|
117
|
-
v: LuaValue,
|
|
118
|
-
sf: LuaStackFrame,
|
|
119
|
-
): boolean {
|
|
117
|
+
export function luaIsCallable(v: LuaValue, sf: LuaStackFrame): boolean {
|
|
120
118
|
if (v === null || v === undefined) {
|
|
121
119
|
return false;
|
|
122
120
|
}
|
|
@@ -128,7 +126,7 @@ export function luaIsCallable(
|
|
|
128
126
|
}
|
|
129
127
|
if (v instanceof LuaTable) {
|
|
130
128
|
const mt = getMetatable(v, sf);
|
|
131
|
-
if (mt
|
|
129
|
+
if (mt?.has("__call")) {
|
|
132
130
|
const mm = mt.get("__call", sf);
|
|
133
131
|
return !!mm && (typeof mm === "function" || isILuaFunction(mm));
|
|
134
132
|
}
|
|
@@ -137,9 +135,7 @@ export function luaIsCallable(
|
|
|
137
135
|
}
|
|
138
136
|
|
|
139
137
|
// In Lua, `__close` must be a function (no `__call` fallback).
|
|
140
|
-
function luaIsCloseMethod(
|
|
141
|
-
v: LuaValue,
|
|
142
|
-
): boolean {
|
|
138
|
+
function luaIsCloseMethod(v: LuaValue): boolean {
|
|
143
139
|
return typeof v === "function" || isILuaFunction(v);
|
|
144
140
|
}
|
|
145
141
|
|
|
@@ -261,14 +257,13 @@ export class LuaEnv implements ILuaSettable, ILuaGettable {
|
|
|
261
257
|
private readonly consts = new Set<string>();
|
|
262
258
|
private readonly numericTypes = new Map<string, NumericType>();
|
|
263
259
|
|
|
264
|
-
constructor(readonly parent?: LuaEnv) {
|
|
265
|
-
}
|
|
260
|
+
constructor(readonly parent?: LuaEnv) {}
|
|
266
261
|
|
|
267
262
|
setLocal(name: string, value: LuaValue, numType?: NumericType) {
|
|
268
263
|
this.variables.set(name, value);
|
|
269
|
-
if (
|
|
264
|
+
if (numType) {
|
|
270
265
|
this.numericTypes.set(name, numType);
|
|
271
|
-
} else {
|
|
266
|
+
} else if (this.numericTypes.size > 0) {
|
|
272
267
|
this.numericTypes.delete(name);
|
|
273
268
|
}
|
|
274
269
|
}
|
|
@@ -276,9 +271,9 @@ export class LuaEnv implements ILuaSettable, ILuaGettable {
|
|
|
276
271
|
setLocalConst(name: string, value: LuaValue, numType?: NumericType) {
|
|
277
272
|
this.variables.set(name, value);
|
|
278
273
|
this.consts.add(name);
|
|
279
|
-
if (
|
|
274
|
+
if (numType) {
|
|
280
275
|
this.numericTypes.set(name, numType);
|
|
281
|
-
} else {
|
|
276
|
+
} else if (this.numericTypes.size > 0) {
|
|
282
277
|
this.numericTypes.delete(name);
|
|
283
278
|
}
|
|
284
279
|
}
|
|
@@ -297,9 +292,9 @@ export class LuaEnv implements ILuaSettable, ILuaGettable {
|
|
|
297
292
|
);
|
|
298
293
|
}
|
|
299
294
|
this.variables.set(key, value);
|
|
300
|
-
if (
|
|
295
|
+
if (numType) {
|
|
301
296
|
this.numericTypes.set(key, numType);
|
|
302
|
-
} else {
|
|
297
|
+
} else if (this.numericTypes.size > 0) {
|
|
303
298
|
this.numericTypes.delete(key);
|
|
304
299
|
}
|
|
305
300
|
} else {
|
|
@@ -327,13 +322,15 @@ export class LuaEnv implements ILuaSettable, ILuaGettable {
|
|
|
327
322
|
return false;
|
|
328
323
|
}
|
|
329
324
|
|
|
330
|
-
get(
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
if (
|
|
335
|
-
return
|
|
325
|
+
get(name: string, _sf?: LuaStackFrame): Promise<LuaValue> | LuaValue | null {
|
|
326
|
+
// Fast path: single Map.get() instead of has() + get() for the common case
|
|
327
|
+
// where the variable exists and has a non-undefined value (null = Lua nil is fine).
|
|
328
|
+
const v = this.variables.get(name);
|
|
329
|
+
if (v !== undefined) {
|
|
330
|
+
return v;
|
|
336
331
|
}
|
|
332
|
+
// Variable set to null (Lua nil) returns null from Map.get() which is !== undefined,
|
|
333
|
+
// so it's handled above. Only fall through when the key is truly absent.
|
|
337
334
|
if (this.parent) {
|
|
338
335
|
return this.parent.get(name, _sf);
|
|
339
336
|
}
|
|
@@ -378,8 +375,7 @@ export class LuaStackFrame {
|
|
|
378
375
|
readonly parent?: LuaStackFrame,
|
|
379
376
|
readonly currentFunction?: LuaFunction,
|
|
380
377
|
readonly threadState: LuaThreadState = { closeStack: undefined },
|
|
381
|
-
) {
|
|
382
|
-
}
|
|
378
|
+
) {}
|
|
383
379
|
|
|
384
380
|
static createWithGlobalEnv(
|
|
385
381
|
globalEnv: LuaEnv,
|
|
@@ -458,46 +454,71 @@ export class LuaFunction implements ILuaFunction {
|
|
|
458
454
|
private capturedEnv: LuaEnv;
|
|
459
455
|
funcHasGotos?: boolean;
|
|
460
456
|
|
|
461
|
-
constructor(
|
|
457
|
+
constructor(
|
|
458
|
+
readonly body: LuaFunctionBody,
|
|
459
|
+
closure: LuaEnv,
|
|
460
|
+
) {
|
|
462
461
|
this.capturedEnv = closure;
|
|
463
462
|
}
|
|
464
463
|
|
|
465
464
|
call(sf: LuaStackFrame, ...args: LuaValue[]): Promise<LuaValue> | LuaValue {
|
|
466
465
|
// Create a new environment that chains to the captured environment
|
|
467
466
|
const env = new LuaEnv(this.capturedEnv);
|
|
468
|
-
if (!sf) {
|
|
469
|
-
console.trace(sf);
|
|
470
|
-
}
|
|
471
467
|
// Set _CTX to the thread local environment from the stack frame
|
|
472
468
|
env.setLocal("_CTX", sf.threadLocal);
|
|
473
469
|
|
|
474
470
|
// Eval using a stack frame that knows the current function
|
|
475
471
|
const sfWithFn = sf.currentFunction === this ? sf : sf.withFunction(this);
|
|
476
472
|
|
|
473
|
+
const params = this.body.parameters;
|
|
474
|
+
const hasVarargs = params.length > 0 && params[params.length - 1] === "...";
|
|
475
|
+
|
|
477
476
|
// Resolve args (sync-first)
|
|
478
477
|
const argsRP = rpAll(args as any[]);
|
|
479
478
|
const resolveArgs = (resolvedArgs: any[]) => {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
479
|
+
if (hasVarargs) {
|
|
480
|
+
// Variadic function: bind named params, collect rest into varargs
|
|
481
|
+
const varargStart = params.length - 1;
|
|
482
|
+
for (let i = 0; i < varargStart; i++) {
|
|
483
|
+
env.setLocal(params[i], resolvedArgs[i] ?? null);
|
|
484
|
+
}
|
|
485
|
+
env.setLocal(
|
|
486
|
+
"...",
|
|
487
|
+
new LuaMultiRes(resolvedArgs.slice(varargStart)),
|
|
488
|
+
);
|
|
489
|
+
} else {
|
|
490
|
+
// Non-variadic: bind all named params directly
|
|
491
|
+
for (let i = 0; i < params.length; i++) {
|
|
492
|
+
env.setLocal(params[i], resolvedArgs[i] ?? null);
|
|
489
493
|
}
|
|
490
|
-
env.setLocal(paramName, resolvedArgs[i] ?? null);
|
|
491
494
|
}
|
|
492
|
-
env.setLocal("...", new LuaMultiRes(varargs));
|
|
493
495
|
|
|
494
496
|
// Evaluate the function body with returnOnReturn set to true
|
|
495
497
|
const r = evalStatement(this.body.block, env, sfWithFn, true);
|
|
496
498
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
499
|
+
if (!isPromise(r)) {
|
|
500
|
+
// Fast path: synchronous result
|
|
501
|
+
if (r === undefined) return;
|
|
502
|
+
if (r && typeof r === "object" && r.ctrl === "return") {
|
|
503
|
+
return mapFunctionReturnValue(r.values);
|
|
504
|
+
}
|
|
505
|
+
if (r && typeof r === "object" && r.ctrl === "break") {
|
|
506
|
+
throw new LuaRuntimeError(
|
|
507
|
+
"break outside loop",
|
|
508
|
+
sfWithFn.withCtx(this.body.block.ctx),
|
|
509
|
+
);
|
|
500
510
|
}
|
|
511
|
+
if (r && typeof r === "object" && r.ctrl === "goto") {
|
|
512
|
+
throw new LuaRuntimeError(
|
|
513
|
+
"unexpected goto signal",
|
|
514
|
+
sfWithFn.withCtx(this.body.block.ctx),
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return r.then((val: any) => {
|
|
521
|
+
if (val === undefined) return;
|
|
501
522
|
if (val && typeof val === "object" && val.ctrl === "return") {
|
|
502
523
|
return mapFunctionReturnValue(val.values);
|
|
503
524
|
}
|
|
@@ -513,12 +534,7 @@ export class LuaFunction implements ILuaFunction {
|
|
|
513
534
|
sfWithFn.withCtx(this.body.block.ctx),
|
|
514
535
|
);
|
|
515
536
|
}
|
|
516
|
-
};
|
|
517
|
-
|
|
518
|
-
if (isPromise(r)) {
|
|
519
|
-
return r.then(map);
|
|
520
|
-
}
|
|
521
|
-
return map(r);
|
|
537
|
+
});
|
|
522
538
|
};
|
|
523
539
|
|
|
524
540
|
if (isPromise(argsRP)) {
|
|
@@ -549,17 +565,24 @@ function mapFunctionReturnValue(values: any[]): any {
|
|
|
549
565
|
}
|
|
550
566
|
|
|
551
567
|
export class LuaNativeJSFunction implements ILuaFunction {
|
|
552
|
-
constructor(readonly fn: (...args: JSValue[]) => JSValue) {
|
|
553
|
-
}
|
|
568
|
+
constructor(readonly fn: (...args: JSValue[]) => JSValue) {}
|
|
554
569
|
|
|
555
570
|
// Performs automatic conversion between Lua and JS values for arguments, but not for return values
|
|
556
571
|
call(sf: LuaStackFrame, ...args: LuaValue[]): Promise<LuaValue> | LuaValue {
|
|
557
|
-
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
572
|
+
// Avoid .map() allocation: convert in-place and check for promises
|
|
573
|
+
const len = args.length;
|
|
574
|
+
let hasAsync = false;
|
|
575
|
+
for (let i = 0; i < len; i++) {
|
|
576
|
+
const converted = luaValueToJS(args[i], sf);
|
|
577
|
+
args[i] = converted;
|
|
578
|
+
if (!hasAsync && isPromise(converted)) {
|
|
579
|
+
hasAsync = true;
|
|
580
|
+
}
|
|
561
581
|
}
|
|
562
|
-
|
|
582
|
+
if (hasAsync) {
|
|
583
|
+
return Promise.all(args).then((jsArgs) => this.fn(...jsArgs));
|
|
584
|
+
}
|
|
585
|
+
return this.fn(...args);
|
|
563
586
|
}
|
|
564
587
|
|
|
565
588
|
asString(): string {
|
|
@@ -574,8 +597,7 @@ export class LuaNativeJSFunction implements ILuaFunction {
|
|
|
574
597
|
export class LuaBuiltinFunction implements ILuaFunction {
|
|
575
598
|
constructor(
|
|
576
599
|
readonly fn: (sf: LuaStackFrame, ...args: LuaValue[]) => LuaValue,
|
|
577
|
-
) {
|
|
578
|
-
}
|
|
600
|
+
) {}
|
|
579
601
|
|
|
580
602
|
call(sf: LuaStackFrame, ...args: LuaValue[]): Promise<LuaValue> | LuaValue {
|
|
581
603
|
// _CTX is already available via the stack frame
|
|
@@ -602,24 +624,16 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
602
624
|
// When tables are used as arrays, we use a native JavaScript array for that
|
|
603
625
|
private arrayPart: any[];
|
|
604
626
|
|
|
605
|
-
// Numeric type metadata at storage boundaries
|
|
606
|
-
private
|
|
627
|
+
// Numeric type metadata at storage boundaries (lazily allocated)
|
|
628
|
+
private stringKeyTypes: Map<string, NumericType> | null = null;
|
|
607
629
|
private otherKeyTypes: Map<any, NumericType> | null = null;
|
|
608
|
-
private
|
|
630
|
+
private arrayTypes: (NumericType | undefined)[] | null = null;
|
|
609
631
|
|
|
610
632
|
constructor(init?: any[] | Record<string, any>) {
|
|
611
633
|
// For efficiency and performance reasons we pre-allocate these (modern JS engines are very good at optimizing this)
|
|
612
634
|
this.arrayPart = Array.isArray(init) ? init : [];
|
|
613
635
|
this.stringKeys = init && !Array.isArray(init) ? init : {};
|
|
614
|
-
|
|
615
|
-
if (init && !Array.isArray(init)) {
|
|
616
|
-
for (const k in init) {
|
|
617
|
-
if (Object.prototype.hasOwnProperty.call(init, k)) {
|
|
618
|
-
this.stringKeys[k] = (init as any)[k];
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
this.otherKeys = null; // Only create this when needed
|
|
636
|
+
this.otherKeys = null;
|
|
623
637
|
this.metatable = null;
|
|
624
638
|
}
|
|
625
639
|
|
|
@@ -689,7 +703,7 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
689
703
|
keys(): any[] {
|
|
690
704
|
const keys: any[] = [];
|
|
691
705
|
for (const k in this.stringKeys) {
|
|
692
|
-
if (Object.
|
|
706
|
+
if (Object.hasOwn(this.stringKeys, k)) {
|
|
693
707
|
keys.push(k);
|
|
694
708
|
}
|
|
695
709
|
}
|
|
@@ -706,7 +720,7 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
706
720
|
|
|
707
721
|
empty(): boolean {
|
|
708
722
|
for (const k in this.stringKeys) {
|
|
709
|
-
if (Object.
|
|
723
|
+
if (Object.hasOwn(this.stringKeys, k)) {
|
|
710
724
|
return false;
|
|
711
725
|
}
|
|
712
726
|
}
|
|
@@ -724,10 +738,20 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
724
738
|
return this.stringKeys[key] !== undefined;
|
|
725
739
|
}
|
|
726
740
|
|
|
741
|
+
// Fast path for plain integer keys (common in for loops)
|
|
742
|
+
if (typeof key === "number") {
|
|
743
|
+
if (key >= 1 && (key | 0) === key) {
|
|
744
|
+
if (this.arrayPart[key - 1] !== undefined) return true;
|
|
745
|
+
return this.otherKeys ? this.otherKeys.has(key) : false;
|
|
746
|
+
}
|
|
747
|
+
return this.otherKeys ? this.otherKeys.has(key) : false;
|
|
748
|
+
}
|
|
749
|
+
|
|
727
750
|
const normalizedKey = LuaTable.normalizeNumericKey(key);
|
|
728
751
|
|
|
729
752
|
if (
|
|
730
|
-
typeof normalizedKey === "number" &&
|
|
753
|
+
typeof normalizedKey === "number" &&
|
|
754
|
+
Number.isInteger(normalizedKey) &&
|
|
731
755
|
normalizedKey >= 1
|
|
732
756
|
) {
|
|
733
757
|
const idx = normalizedKey - 1;
|
|
@@ -759,8 +783,9 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
759
783
|
|
|
760
784
|
this.arrayPart[idx] = value;
|
|
761
785
|
if (isLuaNumber(value) && numType) {
|
|
786
|
+
if (!this.arrayTypes) this.arrayTypes = [];
|
|
762
787
|
this.arrayTypes[idx] = numType;
|
|
763
|
-
} else {
|
|
788
|
+
} else if (this.arrayTypes) {
|
|
764
789
|
this.arrayTypes[idx] = undefined;
|
|
765
790
|
}
|
|
766
791
|
}
|
|
@@ -785,7 +810,12 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
785
810
|
}
|
|
786
811
|
|
|
787
812
|
this.arrayPart.push(v);
|
|
788
|
-
|
|
813
|
+
if (nt) {
|
|
814
|
+
if (!this.arrayTypes) this.arrayTypes = [];
|
|
815
|
+
this.arrayTypes.push(nt);
|
|
816
|
+
} else if (this.arrayTypes) {
|
|
817
|
+
this.arrayTypes.push(undefined);
|
|
818
|
+
}
|
|
789
819
|
}
|
|
790
820
|
}
|
|
791
821
|
|
|
@@ -805,12 +835,13 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
805
835
|
if (typeof key === "string") {
|
|
806
836
|
if (value === null || value === undefined) {
|
|
807
837
|
delete this.stringKeys[key];
|
|
808
|
-
this.stringKeyTypes.delete(key);
|
|
838
|
+
if (this.stringKeyTypes) this.stringKeyTypes.delete(key);
|
|
809
839
|
} else {
|
|
810
840
|
this.stringKeys[key] = value;
|
|
811
|
-
if (
|
|
841
|
+
if (numType) {
|
|
842
|
+
if (!this.stringKeyTypes) this.stringKeyTypes = new Map();
|
|
812
843
|
this.stringKeyTypes.set(key, numType);
|
|
813
|
-
} else {
|
|
844
|
+
} else if (this.stringKeyTypes) {
|
|
814
845
|
this.stringKeyTypes.delete(key);
|
|
815
846
|
}
|
|
816
847
|
}
|
|
@@ -822,12 +853,13 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
822
853
|
if (typeof normalizedKey === "string") {
|
|
823
854
|
if (value === null || value === undefined) {
|
|
824
855
|
delete this.stringKeys[normalizedKey];
|
|
825
|
-
this.stringKeyTypes.delete(normalizedKey);
|
|
856
|
+
if (this.stringKeyTypes) this.stringKeyTypes.delete(normalizedKey);
|
|
826
857
|
} else {
|
|
827
858
|
this.stringKeys[normalizedKey] = value;
|
|
828
859
|
if (isLuaNumber(value) && numType) {
|
|
860
|
+
if (!this.stringKeyTypes) this.stringKeyTypes = new Map();
|
|
829
861
|
this.stringKeyTypes.set(normalizedKey, numType);
|
|
830
|
-
} else {
|
|
862
|
+
} else if (this.stringKeyTypes) {
|
|
831
863
|
this.stringKeyTypes.delete(normalizedKey);
|
|
832
864
|
}
|
|
833
865
|
}
|
|
@@ -835,7 +867,8 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
835
867
|
}
|
|
836
868
|
|
|
837
869
|
if (
|
|
838
|
-
typeof normalizedKey === "number" &&
|
|
870
|
+
typeof normalizedKey === "number" &&
|
|
871
|
+
Number.isInteger(normalizedKey) &&
|
|
839
872
|
normalizedKey >= 1
|
|
840
873
|
) {
|
|
841
874
|
const idx = normalizedKey - 1;
|
|
@@ -845,8 +878,9 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
845
878
|
if (idx <= this.arrayPart.length) {
|
|
846
879
|
this.arrayPart[idx] = value;
|
|
847
880
|
if (isLuaNumber(value) && numType) {
|
|
881
|
+
if (!this.arrayTypes) this.arrayTypes = [];
|
|
848
882
|
this.arrayTypes[idx] = numType;
|
|
849
|
-
} else {
|
|
883
|
+
} else if (this.arrayTypes) {
|
|
850
884
|
this.arrayTypes[idx] = undefined;
|
|
851
885
|
}
|
|
852
886
|
|
|
@@ -869,7 +903,7 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
869
903
|
}
|
|
870
904
|
if (n !== this.arrayPart.length) {
|
|
871
905
|
this.arrayPart.length = n;
|
|
872
|
-
this.arrayTypes.length = n;
|
|
906
|
+
if (this.arrayTypes) this.arrayTypes.length = n;
|
|
873
907
|
}
|
|
874
908
|
}
|
|
875
909
|
|
|
@@ -924,23 +958,39 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
924
958
|
sf?: LuaStackFrame,
|
|
925
959
|
numType?: NumericType,
|
|
926
960
|
): Promise<void> | void {
|
|
927
|
-
|
|
928
|
-
|
|
961
|
+
if (key === null || key === undefined) {
|
|
962
|
+
throw new LuaRuntimeError(
|
|
963
|
+
"table index is nil",
|
|
964
|
+
sf || LuaStackFrame.lostFrame,
|
|
965
|
+
);
|
|
966
|
+
}
|
|
929
967
|
|
|
930
|
-
if (
|
|
931
|
-
|
|
968
|
+
if (typeof key === "number" && Number.isNaN(key)) {
|
|
969
|
+
throw new LuaRuntimeError(
|
|
970
|
+
"table index is NaN",
|
|
971
|
+
sf || LuaStackFrame.lostFrame,
|
|
972
|
+
);
|
|
932
973
|
}
|
|
933
974
|
|
|
975
|
+
// Fast path: no metatable — skip has() check and metamethod machinery
|
|
934
976
|
if (this.metatable === null) {
|
|
935
977
|
return this.rawSet(key, value, numType);
|
|
936
978
|
}
|
|
937
979
|
|
|
980
|
+
// Key exists — rawSet directly, no metamethod needed
|
|
981
|
+
if (this.has(key)) {
|
|
982
|
+
return this.rawSet(key, value, numType);
|
|
983
|
+
}
|
|
984
|
+
|
|
938
985
|
const newIndexMM = this.metatable.rawGet("__newindex");
|
|
939
986
|
|
|
940
987
|
if (newIndexMM === undefined || newIndexMM === null) {
|
|
941
988
|
return this.rawSet(key, value, numType);
|
|
942
989
|
}
|
|
943
990
|
|
|
991
|
+
// Slow path: __newindex metamethod chain (rare)
|
|
992
|
+
const errSf = sf || LuaStackFrame.lostFrame;
|
|
993
|
+
const ctx = sf?.astCtx ?? EMPTY_CTX;
|
|
944
994
|
const k: LuaValue = key;
|
|
945
995
|
const v: LuaValue = value;
|
|
946
996
|
const nt: NumericType | undefined = numType;
|
|
@@ -1000,10 +1050,19 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1000
1050
|
|
|
1001
1051
|
getNumericType(key: LuaValue): NumericType | undefined {
|
|
1002
1052
|
if (typeof key === "string") {
|
|
1003
|
-
return this.stringKeyTypes.get(key);
|
|
1053
|
+
return this.stringKeyTypes ? this.stringKeyTypes.get(key) : undefined;
|
|
1054
|
+
}
|
|
1055
|
+
// Fast path for plain integer keys
|
|
1056
|
+
if (typeof key === "number") {
|
|
1057
|
+
if (key >= 1 && (key | 0) === key) {
|
|
1058
|
+
return this.arrayTypes ? this.arrayTypes[key - 1] : undefined;
|
|
1059
|
+
}
|
|
1060
|
+
return this.otherKeyTypes ? this.otherKeyTypes.get(key) : undefined;
|
|
1004
1061
|
}
|
|
1005
1062
|
if (LuaTable.isIntegerKey(key)) {
|
|
1006
|
-
return this.arrayTypes
|
|
1063
|
+
return this.arrayTypes
|
|
1064
|
+
? this.arrayTypes[LuaTable.toIndex(key)]
|
|
1065
|
+
: undefined;
|
|
1007
1066
|
}
|
|
1008
1067
|
if (this.otherKeyTypes) {
|
|
1009
1068
|
return this.otherKeyTypes.get(key);
|
|
@@ -1016,6 +1075,19 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1016
1075
|
return this.stringKeys[key];
|
|
1017
1076
|
}
|
|
1018
1077
|
|
|
1078
|
+
// Fast path for plain integer keys (common in for loops)
|
|
1079
|
+
if (typeof key === "number") {
|
|
1080
|
+
if (key >= 1 && (key | 0) === key) {
|
|
1081
|
+
const v = this.arrayPart[key - 1];
|
|
1082
|
+
if (v !== undefined) return v;
|
|
1083
|
+
if (this.otherKeys) return this.otherKeys.get(key);
|
|
1084
|
+
return undefined;
|
|
1085
|
+
}
|
|
1086
|
+
// Non-integer or zero/negative number keys
|
|
1087
|
+
if (this.otherKeys) return this.otherKeys.get(key);
|
|
1088
|
+
return undefined;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1019
1091
|
const normalizedKey = LuaTable.normalizeNumericKey(key);
|
|
1020
1092
|
|
|
1021
1093
|
if (typeof normalizedKey === "string") {
|
|
@@ -1023,7 +1095,8 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1023
1095
|
}
|
|
1024
1096
|
|
|
1025
1097
|
if (
|
|
1026
|
-
typeof normalizedKey === "number" &&
|
|
1098
|
+
typeof normalizedKey === "number" &&
|
|
1099
|
+
Number.isInteger(normalizedKey) &&
|
|
1027
1100
|
normalizedKey >= 1
|
|
1028
1101
|
) {
|
|
1029
1102
|
const idx = normalizedKey - 1;
|
|
@@ -1044,6 +1117,11 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1044
1117
|
}
|
|
1045
1118
|
|
|
1046
1119
|
get(key: LuaValue, sf?: LuaStackFrame): LuaValue | Promise<LuaValue> | null {
|
|
1120
|
+
// Fast path: no metatable means rawGet is the final answer (most tables in SilverBullet)
|
|
1121
|
+
if (this.metatable === null) {
|
|
1122
|
+
const raw = this.rawGet(key);
|
|
1123
|
+
return raw !== undefined ? raw : null;
|
|
1124
|
+
}
|
|
1047
1125
|
return luaIndexValue(this, key, sf);
|
|
1048
1126
|
}
|
|
1049
1127
|
|
|
@@ -1067,8 +1145,21 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1067
1145
|
|
|
1068
1146
|
toJSObject(sf = LuaStackFrame.lostFrame): Record<string, any> {
|
|
1069
1147
|
const result: Record<string, any> = {};
|
|
1070
|
-
|
|
1071
|
-
|
|
1148
|
+
// Direct access to stringKeys avoids keys() allocation and get() metatable checks
|
|
1149
|
+
for (const k in this.stringKeys) {
|
|
1150
|
+
if (Object.hasOwn(this.stringKeys, k)) {
|
|
1151
|
+
result[k] = luaValueToJS(this.stringKeys[k], sf);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
// Include array part with 1-based keys
|
|
1155
|
+
for (let i = 0; i < this.arrayPart.length; i++) {
|
|
1156
|
+
result[i + 1] = luaValueToJS(this.arrayPart[i], sf);
|
|
1157
|
+
}
|
|
1158
|
+
// Include other keys
|
|
1159
|
+
if (this.otherKeys) {
|
|
1160
|
+
for (const [key, val] of this.otherKeys) {
|
|
1161
|
+
result[key] = luaValueToJS(val, sf);
|
|
1162
|
+
}
|
|
1072
1163
|
}
|
|
1073
1164
|
return result;
|
|
1074
1165
|
}
|
|
@@ -1097,10 +1188,7 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1097
1188
|
|
|
1098
1189
|
const s = singleResult(v);
|
|
1099
1190
|
if (typeof s !== "string") {
|
|
1100
|
-
throw new LuaRuntimeError(
|
|
1101
|
-
"'__tostring' must return a string",
|
|
1102
|
-
sf,
|
|
1103
|
-
);
|
|
1191
|
+
throw new LuaRuntimeError("'__tostring' must return a string", sf);
|
|
1104
1192
|
}
|
|
1105
1193
|
return s;
|
|
1106
1194
|
}
|
|
@@ -1121,9 +1209,9 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1121
1209
|
if (typeof key === "string") {
|
|
1122
1210
|
result += key;
|
|
1123
1211
|
} else {
|
|
1124
|
-
result +=
|
|
1212
|
+
result += `[${key}]`;
|
|
1125
1213
|
}
|
|
1126
|
-
result +=
|
|
1214
|
+
result += ` = ${await luaToString(this.get(key))}`;
|
|
1127
1215
|
}
|
|
1128
1216
|
result += "}";
|
|
1129
1217
|
return result;
|
|
@@ -1154,6 +1242,7 @@ export function luaIndexValue(
|
|
|
1154
1242
|
if (t instanceof LuaTable) {
|
|
1155
1243
|
const raw = t.rawGet(key);
|
|
1156
1244
|
if (raw !== undefined) {
|
|
1245
|
+
if (isSqlNull(raw)) return null;
|
|
1157
1246
|
return raw;
|
|
1158
1247
|
}
|
|
1159
1248
|
// If no metatable, raw miss => nil
|
|
@@ -1205,27 +1294,32 @@ export function luaIndexValue(
|
|
|
1205
1294
|
|
|
1206
1295
|
export type LuaLValueContainer = { env: ILuaSettable; key: LuaValue };
|
|
1207
1296
|
|
|
1208
|
-
export
|
|
1297
|
+
export function luaSet(
|
|
1209
1298
|
obj: any,
|
|
1210
1299
|
key: any,
|
|
1211
1300
|
value: any,
|
|
1212
1301
|
sf: LuaStackFrame,
|
|
1213
1302
|
numType?: NumericType,
|
|
1214
|
-
): Promise<void> {
|
|
1303
|
+
): void | Promise<void> {
|
|
1215
1304
|
if (!obj) {
|
|
1216
|
-
throw new LuaRuntimeError(
|
|
1217
|
-
`Not a settable object: nil`,
|
|
1218
|
-
sf,
|
|
1219
|
-
);
|
|
1305
|
+
throw new LuaRuntimeError(`Not a settable object: nil`, sf);
|
|
1220
1306
|
}
|
|
1221
1307
|
|
|
1222
1308
|
const normKey = isTaggedFloat(key) ? key.value : key;
|
|
1223
1309
|
|
|
1224
1310
|
if (obj instanceof LuaTable || obj instanceof LuaEnv) {
|
|
1225
|
-
|
|
1311
|
+
return obj.set(normKey, value, sf, numType);
|
|
1226
1312
|
} else {
|
|
1227
1313
|
const k = toNumKey(normKey);
|
|
1228
|
-
|
|
1314
|
+
const jsVal = luaValueToJS(value, sf);
|
|
1315
|
+
if (isPromise(jsVal)) {
|
|
1316
|
+
return (jsVal as Promise<any>).then(
|
|
1317
|
+
(v) => {
|
|
1318
|
+
(obj as Record<string | number, any>)[k] = v;
|
|
1319
|
+
},
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
(obj as Record<string | number, any>)[k] = jsVal;
|
|
1229
1323
|
}
|
|
1230
1324
|
}
|
|
1231
1325
|
|
|
@@ -1235,29 +1329,30 @@ export function luaGet(
|
|
|
1235
1329
|
ctx: ASTCtx | null,
|
|
1236
1330
|
sf: LuaStackFrame,
|
|
1237
1331
|
): Promise<any> | any {
|
|
1238
|
-
const errSf = ctx ? sf.withCtx(ctx) : sf;
|
|
1239
|
-
|
|
1240
1332
|
if (obj === null || obj === undefined) {
|
|
1241
1333
|
throw new LuaRuntimeError(
|
|
1242
1334
|
`attempt to index a nil value`,
|
|
1243
|
-
|
|
1335
|
+
ctx ? sf.withCtx(ctx) : sf,
|
|
1244
1336
|
);
|
|
1245
1337
|
}
|
|
1338
|
+
|
|
1339
|
+
// In Lua reading with a nil key returns nil silently
|
|
1246
1340
|
if (key === null || key === undefined) {
|
|
1247
|
-
|
|
1248
|
-
`attempt to index with a nil key`,
|
|
1249
|
-
errSf,
|
|
1250
|
-
);
|
|
1341
|
+
return null;
|
|
1251
1342
|
}
|
|
1252
1343
|
|
|
1253
1344
|
if (obj instanceof LuaTable || obj instanceof LuaEnv) {
|
|
1254
1345
|
return obj.get(key, sf);
|
|
1255
1346
|
}
|
|
1347
|
+
// Native JS array access: normalize undefined → null (Lua nil).
|
|
1348
|
+
// Without this, accessing an out-of-bounds index on a JS array
|
|
1349
|
+
// leaks JS undefined into the Lua runtime, breaking nil checks
|
|
1350
|
+
// such as `tags[1] ~= nil` on an empty array.
|
|
1256
1351
|
if (typeof key === "number") {
|
|
1257
|
-
return (obj as any[])[key - 1];
|
|
1352
|
+
return (obj as any[])[key - 1] ?? null;
|
|
1258
1353
|
}
|
|
1259
1354
|
if (isTaggedFloat(key)) {
|
|
1260
|
-
return (obj as any[])[key.value - 1];
|
|
1355
|
+
return (obj as any[])[key.value - 1] ?? null;
|
|
1261
1356
|
}
|
|
1262
1357
|
// Native JS object
|
|
1263
1358
|
const k = toNumKey(key);
|
|
@@ -1275,7 +1370,8 @@ export function luaGet(
|
|
|
1275
1370
|
export function luaLen(
|
|
1276
1371
|
obj: any,
|
|
1277
1372
|
sf?: LuaStackFrame,
|
|
1278
|
-
|
|
1373
|
+
raw = false,
|
|
1374
|
+
): number | Promise<number> {
|
|
1279
1375
|
if (typeof obj === "string") {
|
|
1280
1376
|
return obj.length;
|
|
1281
1377
|
}
|
|
@@ -1283,6 +1379,18 @@ export function luaLen(
|
|
|
1283
1379
|
return obj.length;
|
|
1284
1380
|
}
|
|
1285
1381
|
if (obj instanceof LuaTable) {
|
|
1382
|
+
// Check __len metamethod unless raw access is requested
|
|
1383
|
+
if (!raw) {
|
|
1384
|
+
const mt = getMetatable(obj, sf || LuaStackFrame.lostFrame);
|
|
1385
|
+
const mm = mt ? mt.rawGet("__len") : null;
|
|
1386
|
+
if (mm !== undefined && mm !== null) {
|
|
1387
|
+
const r = luaCall(mm, [obj], (sf?.astCtx ?? {}) as ASTCtx, sf);
|
|
1388
|
+
if (isPromise(r)) {
|
|
1389
|
+
return (r as Promise<any>).then((v: any) => Number(singleResult(v)));
|
|
1390
|
+
}
|
|
1391
|
+
return Number(singleResult(r));
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1286
1394
|
return obj.rawLength;
|
|
1287
1395
|
}
|
|
1288
1396
|
|
|
@@ -1308,16 +1416,23 @@ export function luaCall(
|
|
|
1308
1416
|
|
|
1309
1417
|
// Fast path: native JS function
|
|
1310
1418
|
if (typeof callee === "function") {
|
|
1311
|
-
const
|
|
1312
|
-
|
|
1313
|
-
|
|
1419
|
+
const baseSf = sf || LuaStackFrame.lostFrame;
|
|
1420
|
+
const len = args.length;
|
|
1421
|
+
let hasAsync = false;
|
|
1422
|
+
for (let i = 0; i < len; i++) {
|
|
1423
|
+
const converted = luaValueToJS(args[i], baseSf);
|
|
1424
|
+
args[i] = converted;
|
|
1425
|
+
if (!hasAsync && isPromise(converted)) {
|
|
1426
|
+
hasAsync = true;
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1314
1429
|
|
|
1315
|
-
if (
|
|
1316
|
-
return
|
|
1317
|
-
(callee as (...a: any[]) => any)(...resolved)
|
|
1430
|
+
if (hasAsync) {
|
|
1431
|
+
return Promise.all(args).then((resolved) =>
|
|
1432
|
+
(callee as (...a: any[]) => any)(...resolved),
|
|
1318
1433
|
);
|
|
1319
1434
|
}
|
|
1320
|
-
return (callee as (...a: any[]) => any)(...
|
|
1435
|
+
return (callee as (...a: any[]) => any)(...args);
|
|
1321
1436
|
}
|
|
1322
1437
|
|
|
1323
1438
|
// Lua table: may be callable via __call metamethod
|
|
@@ -1353,14 +1468,20 @@ export function luaCall(
|
|
|
1353
1468
|
|
|
1354
1469
|
// ILuaFunction (LuaFunction/LuaBuiltinFunction/LuaNativeJSFunction/etc.)
|
|
1355
1470
|
if (isILuaFunction(callee)) {
|
|
1471
|
+
// Fast path for builtins: skip withFunction, defer withCtx
|
|
1472
|
+
if (callee instanceof LuaBuiltinFunction) {
|
|
1473
|
+
const base = sf || LuaStackFrame.lostFrame;
|
|
1474
|
+
return callee.call(base, ...args);
|
|
1475
|
+
}
|
|
1476
|
+
// Fast path for native JS functions: skip withCtx/withFunction
|
|
1477
|
+
if (callee instanceof LuaNativeJSFunction) {
|
|
1478
|
+
const base = sf || LuaStackFrame.lostFrame;
|
|
1479
|
+
return callee.call(base, ...args);
|
|
1480
|
+
}
|
|
1356
1481
|
const base = (sf || LuaStackFrame.lostFrame).withCtx(ctx);
|
|
1357
|
-
const frameForCall =
|
|
1358
|
-
? base.withFunction(callee)
|
|
1359
|
-
|
|
1360
|
-
return callee.call(
|
|
1361
|
-
frameForCall,
|
|
1362
|
-
...args,
|
|
1363
|
-
);
|
|
1482
|
+
const frameForCall =
|
|
1483
|
+
callee instanceof LuaFunction ? base.withFunction(callee) : base;
|
|
1484
|
+
return callee.call(frameForCall, ...args);
|
|
1364
1485
|
}
|
|
1365
1486
|
|
|
1366
1487
|
throw new LuaRuntimeError(
|
|
@@ -1370,8 +1491,17 @@ export function luaCall(
|
|
|
1370
1491
|
}
|
|
1371
1492
|
|
|
1372
1493
|
export function luaEquals(a: any, b: any): boolean {
|
|
1373
|
-
|
|
1374
|
-
const
|
|
1494
|
+
// Normalize nil variants (null, undefined, SQL NULL) to null
|
|
1495
|
+
const an = (a === null || a === undefined || isSqlNull(a))
|
|
1496
|
+
? null
|
|
1497
|
+
: isTaggedFloat(a)
|
|
1498
|
+
? a.value
|
|
1499
|
+
: a;
|
|
1500
|
+
const bn = (b === null || b === undefined || isSqlNull(b))
|
|
1501
|
+
? null
|
|
1502
|
+
: isTaggedFloat(b)
|
|
1503
|
+
? b.value
|
|
1504
|
+
: b;
|
|
1375
1505
|
return an === bn;
|
|
1376
1506
|
}
|
|
1377
1507
|
|
|
@@ -1386,7 +1516,7 @@ export function luaKeys(val: any): any[] {
|
|
|
1386
1516
|
}
|
|
1387
1517
|
|
|
1388
1518
|
export function luaTypeOf(val: any): LuaType | Promise<LuaType> {
|
|
1389
|
-
if (val === null || val === undefined) {
|
|
1519
|
+
if (val === null || val === undefined || isSqlNull(val)) {
|
|
1390
1520
|
return "nil";
|
|
1391
1521
|
}
|
|
1392
1522
|
if (isPromise(val)) {
|
|
@@ -1462,9 +1592,10 @@ export class LuaRuntimeError extends Error {
|
|
|
1462
1592
|
);
|
|
1463
1593
|
|
|
1464
1594
|
// Add position indicator
|
|
1465
|
-
const pointer = " ".repeat(column)
|
|
1595
|
+
const pointer = `${" ".repeat(column)}^`;
|
|
1466
1596
|
|
|
1467
|
-
traceStr +=
|
|
1597
|
+
traceStr +=
|
|
1598
|
+
`* ${ctx.ref || "(unknown source)"} @ ${line}:${column}:\n` +
|
|
1468
1599
|
` ${codeLine}\n` +
|
|
1469
1600
|
` ${pointer}\n`;
|
|
1470
1601
|
current = current.parent;
|
|
@@ -1497,7 +1628,7 @@ export function luaToString(
|
|
|
1497
1628
|
value: any,
|
|
1498
1629
|
visited: Set<any> = new Set(),
|
|
1499
1630
|
): string | Promise<string> {
|
|
1500
|
-
if (value === null || value === undefined) {
|
|
1631
|
+
if (value === null || value === undefined || isSqlNull(value)) {
|
|
1501
1632
|
return "nil";
|
|
1502
1633
|
}
|
|
1503
1634
|
if (isPromise(value)) {
|
|
@@ -1549,7 +1680,7 @@ export function luaToString(
|
|
|
1549
1680
|
const strVal = await luaToString(val, visited);
|
|
1550
1681
|
result += strVal;
|
|
1551
1682
|
}
|
|
1552
|
-
return result
|
|
1683
|
+
return `${result}}`;
|
|
1553
1684
|
}
|
|
1554
1685
|
|
|
1555
1686
|
// Handle objects
|
|
@@ -1576,20 +1707,20 @@ export function luaToString(
|
|
|
1576
1707
|
}
|
|
1577
1708
|
|
|
1578
1709
|
export function luaFormatNumber(n: number, kind?: "int" | "float"): string {
|
|
1579
|
-
if (kind !== "float" && Number.isInteger(n) && isFinite(n)) {
|
|
1710
|
+
if (kind !== "float" && Number.isInteger(n) && Number.isFinite(n)) {
|
|
1580
1711
|
return String(n);
|
|
1581
1712
|
}
|
|
1582
1713
|
if (n !== n) return "-nan";
|
|
1583
1714
|
if (n === Infinity) return "inf";
|
|
1584
1715
|
if (n === -Infinity) return "-inf";
|
|
1585
1716
|
if (n === 0) {
|
|
1586
|
-
return
|
|
1717
|
+
return 1 / n === -Infinity ? "-0.0" : "0.0";
|
|
1587
1718
|
}
|
|
1588
1719
|
// Delegate to luaFormat for `%.14g`
|
|
1589
1720
|
const s = luaFormat("%.14g", n);
|
|
1590
1721
|
// Guarantee `.01 suffix for integer-valued floats
|
|
1591
1722
|
if (s.indexOf(".") === -1 && s.indexOf("e") === -1) {
|
|
1592
|
-
return s
|
|
1723
|
+
return `${s}.0`;
|
|
1593
1724
|
}
|
|
1594
1725
|
return s;
|
|
1595
1726
|
}
|
|
@@ -1613,7 +1744,7 @@ export function getMetatable(
|
|
|
1613
1744
|
}
|
|
1614
1745
|
|
|
1615
1746
|
const stringMetatable = new LuaTable();
|
|
1616
|
-
stringMetatable.set("__index", (globalEnv as any).get("string"));
|
|
1747
|
+
void stringMetatable.set("__index", (globalEnv as any).get("string"));
|
|
1617
1748
|
thread.setLocal("_STRING_MT", stringMetatable);
|
|
1618
1749
|
|
|
1619
1750
|
return stringMetatable;
|
|
@@ -1629,6 +1760,9 @@ export function getMetatable(
|
|
|
1629
1760
|
}
|
|
1630
1761
|
|
|
1631
1762
|
export function jsToLuaValue(value: any): any {
|
|
1763
|
+
if (value === null || value === undefined) {
|
|
1764
|
+
return value;
|
|
1765
|
+
}
|
|
1632
1766
|
if (isPromise(value)) {
|
|
1633
1767
|
return (value as Promise<any>).then(jsToLuaValue);
|
|
1634
1768
|
}
|
|
@@ -1643,26 +1777,26 @@ export function jsToLuaValue(value: any): any {
|
|
|
1643
1777
|
const regexMatch = value as RegExpMatchArray;
|
|
1644
1778
|
const regexMatchTable = new LuaTable();
|
|
1645
1779
|
for (let i = 0; i < regexMatch.length; i++) {
|
|
1646
|
-
regexMatchTable.set(i + 1, regexMatch[i]);
|
|
1780
|
+
void regexMatchTable.set(i + 1, regexMatch[i]);
|
|
1647
1781
|
}
|
|
1648
|
-
regexMatchTable.set("index", regexMatch.index);
|
|
1649
|
-
regexMatchTable.set("input", regexMatch.input);
|
|
1650
|
-
regexMatchTable.set("groups", regexMatch.groups);
|
|
1782
|
+
void regexMatchTable.set("index", regexMatch.index);
|
|
1783
|
+
void regexMatchTable.set("input", regexMatch.input);
|
|
1784
|
+
void regexMatchTable.set("groups", regexMatch.groups);
|
|
1651
1785
|
return regexMatchTable;
|
|
1652
1786
|
}
|
|
1653
1787
|
if (Array.isArray(value)) {
|
|
1654
|
-
const
|
|
1788
|
+
const converted = new Array(value.length);
|
|
1655
1789
|
for (let i = 0; i < value.length; i++) {
|
|
1656
|
-
|
|
1790
|
+
converted[i] = jsToLuaValue(value[i]);
|
|
1657
1791
|
}
|
|
1658
|
-
return
|
|
1792
|
+
return new LuaTable(converted);
|
|
1659
1793
|
}
|
|
1660
1794
|
if (typeof value === "object") {
|
|
1661
|
-
const
|
|
1795
|
+
const converted: Record<string, any> = {};
|
|
1662
1796
|
for (const key in value) {
|
|
1663
|
-
|
|
1797
|
+
converted[key] = jsToLuaValue((value as any)[key]);
|
|
1664
1798
|
}
|
|
1665
|
-
return
|
|
1799
|
+
return new LuaTable(converted);
|
|
1666
1800
|
}
|
|
1667
1801
|
if (typeof value === "function") {
|
|
1668
1802
|
return new LuaNativeJSFunction(value);
|
|
@@ -1679,13 +1813,12 @@ export function luaValueToJS(value: any, sf: LuaStackFrame): any {
|
|
|
1679
1813
|
return value.toJS(sf);
|
|
1680
1814
|
}
|
|
1681
1815
|
if (
|
|
1682
|
-
value instanceof LuaNativeJSFunction ||
|
|
1816
|
+
value instanceof LuaNativeJSFunction ||
|
|
1817
|
+
value instanceof LuaFunction ||
|
|
1683
1818
|
value instanceof LuaBuiltinFunction
|
|
1684
1819
|
) {
|
|
1685
1820
|
return (...args: any[]) => {
|
|
1686
|
-
const jsArgs = rpAll(
|
|
1687
|
-
args.map((v) => luaValueToJS(v, sf)),
|
|
1688
|
-
);
|
|
1821
|
+
const jsArgs = rpAll(args.map((v) => luaValueToJS(v, sf)));
|
|
1689
1822
|
if (isPromise(jsArgs)) {
|
|
1690
1823
|
return luaValueToJS(
|
|
1691
1824
|
jsArgs.then((jsArgs) => (value as ILuaFunction).call(sf, ...jsArgs)),
|