hm-pt-core 1.0.1 → 1.0.3
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 +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/trace/decode_raw.js +5 -2
- package/dist/trace/parse.js +136 -40
- package/dist/trace/printf.d.ts +6 -2
- package/dist/trace/printf.js +42 -12
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -9,5 +9,5 @@ export type { Endian, BuildTraceParserRegistryOptions } from "./trace/parse.js";
|
|
|
9
9
|
export { TraceFieldDecodeError } from "./trace/parse.js";
|
|
10
10
|
export type { PrintfSpec } from "./trace/printf.js";
|
|
11
11
|
export { parseTraceFormat, parseCommonFieldsFromRaw, parseAllFieldsFromRaw, rawHexLinesToBuffer, bufferToRawHexLines, buildTraceParserRegistry, } from "./trace/parse.js";
|
|
12
|
-
export { formatPrintfValue, tokenizePrintfFormat } from "./trace/printf.js";
|
|
12
|
+
export { formatPrintfValue, tokenizePrintfFormat, renderPrintFmt, renderPrintFmtAsFieldDict } from "./trace/printf.js";
|
|
13
13
|
export { decodeRawByRegistry } from "./trace/decode_raw.js";
|
package/dist/index.js
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
export { isPerfDataLike } from "./perf/is_perf_data_like.js";
|
|
5
5
|
export { TraceFieldDecodeError } from "./trace/parse.js";
|
|
6
6
|
export { parseTraceFormat, parseCommonFieldsFromRaw, parseAllFieldsFromRaw, rawHexLinesToBuffer, bufferToRawHexLines, buildTraceParserRegistry, } from "./trace/parse.js";
|
|
7
|
-
export { formatPrintfValue, tokenizePrintfFormat } from "./trace/printf.js";
|
|
7
|
+
export { formatPrintfValue, tokenizePrintfFormat, renderPrintFmt, renderPrintFmtAsFieldDict } from "./trace/printf.js";
|
|
8
8
|
export { decodeRawByRegistry } from "./trace/decode_raw.js";
|
package/dist/trace/decode_raw.js
CHANGED
|
@@ -21,9 +21,10 @@ export function decodeRawByRegistry(raw, registry, options = {}) {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
const allFields = parseAllFieldsFromRaw(raw, fmt);
|
|
24
|
+
const renderContext = { eventName: fmt.eventName, eventId };
|
|
24
25
|
const mode = options.tracePrintMode ?? "printf";
|
|
25
26
|
if (fmt.printFmt && mode === "fieldDict") {
|
|
26
|
-
let renderedFieldDict = renderPrintFmtAsFieldDict(fmt.printFmt, fmt.printArgs, allFields);
|
|
27
|
+
let renderedFieldDict = renderPrintFmtAsFieldDict(fmt.printFmt, fmt.printArgs, allFields, renderContext);
|
|
27
28
|
const transform = registry.transformFieldDictByEventId?.get(eventId);
|
|
28
29
|
if (transform) {
|
|
29
30
|
renderedFieldDict = transform({
|
|
@@ -42,7 +43,9 @@ export function decodeRawByRegistry(raw, registry, options = {}) {
|
|
|
42
43
|
skipped: false,
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
|
-
const renderedText = fmt.printFmt
|
|
46
|
+
const renderedText = fmt.printFmt
|
|
47
|
+
? renderPrintFmt(fmt.printFmt, fmt.printArgs, allFields, renderContext)
|
|
48
|
+
: undefined;
|
|
46
49
|
return {
|
|
47
50
|
commonType: eventId,
|
|
48
51
|
commonFields,
|
package/dist/trace/parse.js
CHANGED
|
@@ -138,6 +138,24 @@ function scanBalancedPrintArgs(argsPart) {
|
|
|
138
138
|
}
|
|
139
139
|
return args;
|
|
140
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* 从 print fmt 逗号后参数段提取表达式。
|
|
143
|
+
* 内核 trace 常在首个 __data_loc 参数外套 ((char*)((void *)...)),括号平衡切分只得 1 段;
|
|
144
|
+
* 此时回退为按 `REC->` 切分(与 hiperf 1.x 行为一致)。
|
|
145
|
+
*/
|
|
146
|
+
function parsePrintArgExpressions(argsPart) {
|
|
147
|
+
const balanced = scanBalancedPrintArgs(argsPart);
|
|
148
|
+
const recExprs = argsPart.match(/REC->[^,]+/g)?.map((s) => s.trim().replace(/\)+$/, "")) ?? [];
|
|
149
|
+
if (recExprs.length > balanced.length) {
|
|
150
|
+
return recExprs;
|
|
151
|
+
}
|
|
152
|
+
if (recExprs.length === 1 &&
|
|
153
|
+
balanced.length === 1 &&
|
|
154
|
+
balanced[0] !== recExprs[0]) {
|
|
155
|
+
return recExprs;
|
|
156
|
+
}
|
|
157
|
+
return balanced;
|
|
158
|
+
}
|
|
141
159
|
function parsePrintFmtLine(line) {
|
|
142
160
|
const prefix = line.match(/^print\s+fmt\s*:\s*/i);
|
|
143
161
|
if (!prefix)
|
|
@@ -151,7 +169,7 @@ function parsePrintFmtLine(line) {
|
|
|
151
169
|
const argsMatch = rest.match(/^,\s*(.*)$/);
|
|
152
170
|
if (!argsMatch)
|
|
153
171
|
return undefined;
|
|
154
|
-
return { printFmt: lit.value, printArgs:
|
|
172
|
+
return { printFmt: lit.value, printArgs: parsePrintArgExpressions(argsMatch[1]) };
|
|
155
173
|
}
|
|
156
174
|
function splitTypeAndName(rest) {
|
|
157
175
|
const trimmed = rest.trim();
|
|
@@ -222,13 +240,89 @@ function isCharType(typeName) {
|
|
|
222
240
|
const t = normalizeType(typeName);
|
|
223
241
|
return t === "char" || t === "signed char" || t === "unsigned char";
|
|
224
242
|
}
|
|
243
|
+
function isDataLocType(typeName) {
|
|
244
|
+
return normalizeType(typeName).includes("__data_loc");
|
|
245
|
+
}
|
|
246
|
+
function parseDataLocElementType(typeName, signed) {
|
|
247
|
+
const t = normalizeType(typeName);
|
|
248
|
+
const m = t.match(/^__data_loc\s+(.+)\[\]$/);
|
|
249
|
+
if (!m)
|
|
250
|
+
return undefined;
|
|
251
|
+
const elemTypeName = m[1].trim();
|
|
252
|
+
const elemSize = inferScalarSize(elemTypeName, signed);
|
|
253
|
+
if (elemSize === undefined)
|
|
254
|
+
return undefined;
|
|
255
|
+
return { elemTypeName, elemSize, signed: isSignedElementType(elemTypeName, signed) };
|
|
256
|
+
}
|
|
257
|
+
function isSignedElementType(elemTypeName, fieldSigned) {
|
|
258
|
+
const t = normalizeType(elemTypeName);
|
|
259
|
+
if (t.includes("unsigned"))
|
|
260
|
+
return false;
|
|
261
|
+
if (t === "char" && !fieldSigned)
|
|
262
|
+
return false;
|
|
263
|
+
return fieldSigned || t === "signed char" || t === "short" || t === "int" || t === "long" || t === "long long" || t === "__s64";
|
|
264
|
+
}
|
|
265
|
+
function inferScalarSize(typeName, signed) {
|
|
266
|
+
const t = normalizeType(typeName);
|
|
267
|
+
if (t.includes("double"))
|
|
268
|
+
return 8;
|
|
269
|
+
if (t.includes("long long") || t === "__s64")
|
|
270
|
+
return 8;
|
|
271
|
+
if (t.includes("long"))
|
|
272
|
+
return 8;
|
|
273
|
+
if (t.includes("short"))
|
|
274
|
+
return 2;
|
|
275
|
+
if (t.includes("char") || t.includes("int") || t.includes("float"))
|
|
276
|
+
return 4;
|
|
277
|
+
if (t.includes("unsigned") || signed)
|
|
278
|
+
return 4;
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
|
281
|
+
function assertFieldInBounds(fieldName, offset, size, rawLength) {
|
|
282
|
+
if (offset < 0 || offset + size > rawLength) {
|
|
283
|
+
throw new TraceFieldDecodeError(`Field "${fieldName}" out of bounds: offset=${offset}, size=${size}, raw.length=${rawLength}`, fieldName, offset, size, rawLength);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function readLocatorField(view, field) {
|
|
287
|
+
assertFieldInBounds(field.name, field.offset, field.size, view.byteLength);
|
|
288
|
+
const locField = {
|
|
289
|
+
...field,
|
|
290
|
+
typeName: "unsigned int",
|
|
291
|
+
signed: false,
|
|
292
|
+
size: 4,
|
|
293
|
+
};
|
|
294
|
+
const loc = readFieldScalarUnchecked(view, locField);
|
|
295
|
+
if (typeof loc !== "number") {
|
|
296
|
+
throw new TraceFieldDecodeError(`Field "${field.name}" __data_loc locator is invalid`, field.name, field.offset, field.size, view.byteLength);
|
|
297
|
+
}
|
|
298
|
+
return loc;
|
|
299
|
+
}
|
|
300
|
+
function readDynamicRegionAsArray(raw, view, field, dataOffset, byteLen, elemTypeName, elemSize, elemSigned) {
|
|
301
|
+
if (byteLen === 0)
|
|
302
|
+
return [];
|
|
303
|
+
if (byteLen % elemSize !== 0) {
|
|
304
|
+
throw new TraceFieldDecodeError(`Field "${field.name}" __data_loc length ${byteLen} is not divisible by element size ${elemSize}`, field.name, dataOffset, byteLen, raw.length);
|
|
305
|
+
}
|
|
306
|
+
assertFieldInBounds(field.name, dataOffset, byteLen, raw.length);
|
|
307
|
+
const count = byteLen / elemSize;
|
|
308
|
+
const values = [];
|
|
309
|
+
for (let i = 0; i < count; i++) {
|
|
310
|
+
const elemField = {
|
|
311
|
+
typeName: elemTypeName,
|
|
312
|
+
name: field.name,
|
|
313
|
+
offset: dataOffset + i * elemSize,
|
|
314
|
+
size: elemSize,
|
|
315
|
+
signed: elemSigned,
|
|
316
|
+
};
|
|
317
|
+
values.push(readFieldScalarUnchecked(view, elemField));
|
|
318
|
+
}
|
|
319
|
+
return values;
|
|
320
|
+
}
|
|
225
321
|
/**
|
|
226
322
|
* 从 little-endian 原始缓冲区按字段描述读一个标量
|
|
227
323
|
*/
|
|
228
|
-
function
|
|
324
|
+
function readFieldScalarUnchecked(view, field) {
|
|
229
325
|
const { offset, size, signed, typeName } = field;
|
|
230
|
-
if (offset + size > view.byteLength)
|
|
231
|
-
return undefined;
|
|
232
326
|
const t = normalizeType(typeName);
|
|
233
327
|
if (size === 1) {
|
|
234
328
|
if (signed || t === "signed char") {
|
|
@@ -260,7 +354,14 @@ function readFieldScalar(view, field) {
|
|
|
260
354
|
}
|
|
261
355
|
return view.getBigUint64(offset, true);
|
|
262
356
|
}
|
|
263
|
-
|
|
357
|
+
throw new TraceFieldDecodeError(`Field "${field.name}" has unsupported scalar size ${size}`, field.name, offset, size, view.byteLength);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* 从 little-endian 原始缓冲区按字段描述读一个标量;越界时抛出 TraceFieldDecodeError。
|
|
361
|
+
*/
|
|
362
|
+
function readFieldScalar(view, field) {
|
|
363
|
+
assertFieldInBounds(field.name, field.offset, field.size, view.byteLength);
|
|
364
|
+
return readFieldScalarUnchecked(view, field);
|
|
264
365
|
}
|
|
265
366
|
function parseArrayName(name) {
|
|
266
367
|
const m = name.match(/^(\w+)\[(\d+)\]$/);
|
|
@@ -274,9 +375,10 @@ function readFieldValue(view, field) {
|
|
|
274
375
|
return readFieldScalar(view, field);
|
|
275
376
|
if (arr.len <= 0)
|
|
276
377
|
return [];
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
378
|
+
if (field.size % arr.len !== 0) {
|
|
379
|
+
throw new TraceFieldDecodeError(`Field "${field.name}" size ${field.size} is not divisible by array length ${arr.len}`, field.name, field.offset, field.size, view.byteLength);
|
|
380
|
+
}
|
|
381
|
+
const elemSize = field.size / arr.len;
|
|
280
382
|
const values = [];
|
|
281
383
|
for (let i = 0; i < arr.len; i++) {
|
|
282
384
|
const elemField = {
|
|
@@ -285,10 +387,7 @@ function readFieldValue(view, field) {
|
|
|
285
387
|
offset: field.offset + i * elemSize,
|
|
286
388
|
size: elemSize,
|
|
287
389
|
};
|
|
288
|
-
|
|
289
|
-
if (v === undefined)
|
|
290
|
-
return undefined;
|
|
291
|
-
values.push(v);
|
|
390
|
+
values.push(readFieldScalar(view, elemField));
|
|
292
391
|
}
|
|
293
392
|
return values;
|
|
294
393
|
}
|
|
@@ -301,10 +400,9 @@ export function parseCommonFieldsFromRaw(raw, format) {
|
|
|
301
400
|
for (const field of format.fields) {
|
|
302
401
|
if (!field.name.startsWith("common_"))
|
|
303
402
|
continue;
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
403
|
+
if (field.offset + field.size > view.byteLength)
|
|
404
|
+
continue;
|
|
405
|
+
out[field.name] = readFieldScalar(view, field);
|
|
308
406
|
}
|
|
309
407
|
return out;
|
|
310
408
|
}
|
|
@@ -318,26 +416,18 @@ export function parseAllFieldsFromRaw(raw, format) {
|
|
|
318
416
|
const arr = parseArrayName(field.name);
|
|
319
417
|
const key = arr ? arr.baseName : field.name;
|
|
320
418
|
const t = normalizeType(field.typeName);
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
const offset = loc & 0xffff;
|
|
334
|
-
const len = (loc >>> 16) & 0xffff;
|
|
335
|
-
if (len > 0 && (offset < 0 || offset + len > raw.length)) {
|
|
336
|
-
throw new TraceFieldDecodeError(`Field "${field.name}" __data_loc out of bounds: offset=${offset}, len=${len}, raw.length=${raw.length}`, field.name, offset, len, raw.length);
|
|
337
|
-
}
|
|
338
|
-
if (offset >= 0 && len > 0 && offset + len <= raw.length) {
|
|
339
|
-
const bytes = raw.subarray(offset, offset + len);
|
|
340
|
-
// 去掉末尾 \0
|
|
419
|
+
if (isDataLocType(field.typeName)) {
|
|
420
|
+
const loc = readLocatorField(view, field);
|
|
421
|
+
const locKey = `__data_loc_${field.name}`;
|
|
422
|
+
out[locKey] = loc;
|
|
423
|
+
const dataOffset = loc & 0xffff;
|
|
424
|
+
const byteLen = (loc >>> 16) & 0xffff;
|
|
425
|
+
if (byteLen > 0 && (dataOffset < 0 || dataOffset + byteLen > raw.length)) {
|
|
426
|
+
throw new TraceFieldDecodeError(`Field "${field.name}" __data_loc out of bounds: offset=${dataOffset}, len=${byteLen}, raw.length=${raw.length}`, field.name, dataOffset, byteLen, raw.length);
|
|
427
|
+
}
|
|
428
|
+
if (t.includes("char")) {
|
|
429
|
+
if (byteLen > 0) {
|
|
430
|
+
const bytes = raw.subarray(dataOffset, dataOffset + byteLen);
|
|
341
431
|
const nul = bytes.indexOf(0);
|
|
342
432
|
const slice = nul >= 0 ? bytes.subarray(0, nul) : bytes;
|
|
343
433
|
out[field.name] = UTF8_DECODER.decode(slice);
|
|
@@ -345,17 +435,20 @@ export function parseAllFieldsFromRaw(raw, format) {
|
|
|
345
435
|
else {
|
|
346
436
|
out[field.name] = "";
|
|
347
437
|
}
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
const elemInfo = parseDataLocElementType(field.typeName, field.signed);
|
|
441
|
+
if (!elemInfo) {
|
|
442
|
+
throw new TraceFieldDecodeError(`Field "${field.name}" uses unsupported __data_loc element type "${field.typeName}"`, field.name, field.offset, field.size, raw.length);
|
|
348
443
|
}
|
|
444
|
+
out[key] = readDynamicRegionAsArray(raw, view, field, dataOffset, byteLen, elemInfo.elemTypeName, elemInfo.elemSize, elemInfo.signed);
|
|
349
445
|
continue;
|
|
350
446
|
}
|
|
351
447
|
if (arr && isCharType(field.typeName)) {
|
|
352
448
|
out[key] = readCharArrayAsString(raw, field.offset, field.size, field.name);
|
|
353
449
|
continue;
|
|
354
450
|
}
|
|
355
|
-
|
|
356
|
-
if (v !== undefined) {
|
|
357
|
-
out[key] = v;
|
|
358
|
-
}
|
|
451
|
+
out[key] = readFieldValue(view, field);
|
|
359
452
|
}
|
|
360
453
|
return out;
|
|
361
454
|
}
|
|
@@ -450,6 +543,9 @@ export function buildTraceParserRegistry(formatTexts, options = {}) {
|
|
|
450
543
|
if (typeof src !== "string" && src.transformFieldDict) {
|
|
451
544
|
transformFieldDictByEventId.set(fmt.eventId, src.transformFieldDict);
|
|
452
545
|
}
|
|
546
|
+
else {
|
|
547
|
+
transformFieldDictByEventId.delete(fmt.eventId);
|
|
548
|
+
}
|
|
453
549
|
}
|
|
454
550
|
});
|
|
455
551
|
return {
|
package/dist/trace/printf.d.ts
CHANGED
|
@@ -29,6 +29,10 @@ export declare function tokenizePrintfFormat(printFmt: string): Array<{
|
|
|
29
29
|
kind: "spec";
|
|
30
30
|
spec: PrintfSpec;
|
|
31
31
|
}>;
|
|
32
|
+
export interface PrintFmtRenderContext {
|
|
33
|
+
eventName?: string;
|
|
34
|
+
eventId?: number;
|
|
35
|
+
}
|
|
32
36
|
/** 测试专用:重置 printf 解析缓存 */
|
|
33
37
|
export declare function resetPrintfCachesForTest(): void;
|
|
34
38
|
/** 测试专用:读取 printf 解析缓存条目数 */
|
|
@@ -36,6 +40,6 @@ export declare function getPrintfCacheSizesForTest(): {
|
|
|
36
40
|
tokens: number;
|
|
37
41
|
specs: number;
|
|
38
42
|
};
|
|
39
|
-
export declare function renderPrintFmtAsFieldDict(printFmt: string, printArgs: string[] | undefined, fieldMap: PrintFieldMap): Record<string, string>;
|
|
40
|
-
export declare function renderPrintFmt(printFmt: string, printArgs: string[] | undefined, fieldMap: PrintFieldMap): string;
|
|
43
|
+
export declare function renderPrintFmtAsFieldDict(printFmt: string, printArgs: string[] | undefined, fieldMap: PrintFieldMap, context?: PrintFmtRenderContext): Record<string, string>;
|
|
44
|
+
export declare function renderPrintFmt(printFmt: string, printArgs: string[] | undefined, fieldMap: PrintFieldMap, context?: PrintFmtRenderContext): string;
|
|
41
45
|
export {};
|
package/dist/trace/printf.js
CHANGED
|
@@ -158,11 +158,21 @@ function formatPrintfUnsignedRadix(unsigned, spec, radix, upper) {
|
|
|
158
158
|
: unsigned.toString(radix);
|
|
159
159
|
body = body.padStart(Math.max(prec, body.length), "0");
|
|
160
160
|
}
|
|
161
|
-
if (hasPrintfFlag(spec.flags, "#")
|
|
162
|
-
if (radix === 16)
|
|
161
|
+
if (hasPrintfFlag(spec.flags, "#")) {
|
|
162
|
+
if (radix === 16 && unsigned !== 0n) {
|
|
163
163
|
prefix = upper ? "0X" : "0x";
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
}
|
|
165
|
+
else if (radix === 8) {
|
|
166
|
+
if (unsigned === 0n && (prec === 0 || body === "")) {
|
|
167
|
+
body = "0";
|
|
168
|
+
}
|
|
169
|
+
else if (body === "") {
|
|
170
|
+
body = "0";
|
|
171
|
+
}
|
|
172
|
+
else if (!body.startsWith("0")) {
|
|
173
|
+
body = "0" + body;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
166
176
|
}
|
|
167
177
|
const zeroPad = hasPrintfFlag(spec.flags, "0") &&
|
|
168
178
|
!hasPrintfFlag(spec.flags, "-") &&
|
|
@@ -333,6 +343,16 @@ export function tokenizePrintfFormat(printFmt) {
|
|
|
333
343
|
flushText();
|
|
334
344
|
return out;
|
|
335
345
|
}
|
|
346
|
+
function assertPrintArgCount(printFmt, printArgs, context) {
|
|
347
|
+
const specCount = extractPrintfSpecs(printFmt).length;
|
|
348
|
+
const argCount = printArgs?.length ?? 0;
|
|
349
|
+
if (argCount < specCount) {
|
|
350
|
+
const eventLabel = context?.eventName !== undefined
|
|
351
|
+
? `"${context.eventName}"${context.eventId !== undefined ? ` (ID ${context.eventId})` : ""}`
|
|
352
|
+
: "unknown event";
|
|
353
|
+
throw new Error(`Print fmt for event ${eventLabel} requires ${specCount} args but got ${argCount} at spec index ${argCount}: "${printFmt}"`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
336
356
|
function extractPrintfSpecs(printFmt) {
|
|
337
357
|
const cached = PRINT_FMT_SPEC_CACHE.get(printFmt);
|
|
338
358
|
if (cached)
|
|
@@ -401,12 +421,20 @@ function evalPrintArgExpr(exprRaw, fieldMap) {
|
|
|
401
421
|
return v;
|
|
402
422
|
throw new Error(`__get_str(${m[1]}) requires a string field, got ${typeof v}`);
|
|
403
423
|
}
|
|
404
|
-
m = expr.match(/^__get_dynamic_array\((\w+)\)
|
|
424
|
+
m = expr.match(/^__get_dynamic_array\((\w+)\)(?:\[(\d+)])?$/);
|
|
405
425
|
if (m) {
|
|
406
426
|
const v = fieldMap[m[1]];
|
|
407
|
-
if (Array.isArray(v))
|
|
408
|
-
|
|
409
|
-
|
|
427
|
+
if (!Array.isArray(v)) {
|
|
428
|
+
throw new Error(`__get_dynamic_array(${m[1]}) requires an array field, got ${typeof v}`);
|
|
429
|
+
}
|
|
430
|
+
if (m[2] !== undefined) {
|
|
431
|
+
const idx = parseInt(m[2], 10);
|
|
432
|
+
if (idx < 0 || idx >= v.length) {
|
|
433
|
+
throw new Error(`Invalid print arg index for __get_dynamic_array(${m[1]})[${m[2]}]`);
|
|
434
|
+
}
|
|
435
|
+
return v[idx];
|
|
436
|
+
}
|
|
437
|
+
return v;
|
|
410
438
|
}
|
|
411
439
|
m = expr.match(/^REC->(\w+)&0xffff$/);
|
|
412
440
|
if (m) {
|
|
@@ -481,9 +509,9 @@ function fieldKeyFromNormalizedArg(normalizedArg, index) {
|
|
|
481
509
|
if (mGetStr) {
|
|
482
510
|
return mGetStr[1];
|
|
483
511
|
}
|
|
484
|
-
const mGetArr = k.match(/^__get_dynamic_array\((\w+)\)
|
|
512
|
+
const mGetArr = k.match(/^__get_dynamic_array\((\w+)\)(?:\[(\d+)])?$/);
|
|
485
513
|
if (mGetArr) {
|
|
486
|
-
return mGetArr[1];
|
|
514
|
+
return mGetArr[2] !== undefined ? `${mGetArr[1]}__${mGetArr[2]}` : mGetArr[1];
|
|
487
515
|
}
|
|
488
516
|
// REC->__data_loc_path&0xffff 在 fieldDict 中收敛为 path 作为键名
|
|
489
517
|
const mDataLocOff = k.match(/^__data_loc_(\w+)&0xffff$/);
|
|
@@ -492,7 +520,8 @@ function fieldKeyFromNormalizedArg(normalizedArg, index) {
|
|
|
492
520
|
}
|
|
493
521
|
return k.length > 0 ? k : `arg${index}`;
|
|
494
522
|
}
|
|
495
|
-
export function renderPrintFmtAsFieldDict(printFmt, printArgs, fieldMap) {
|
|
523
|
+
export function renderPrintFmtAsFieldDict(printFmt, printArgs, fieldMap, context) {
|
|
524
|
+
assertPrintArgCount(printFmt, printArgs, context);
|
|
496
525
|
const { normalizedArgs, values } = buildPrintArgValues(printArgs, fieldMap);
|
|
497
526
|
const specs = extractPrintfSpecs(printFmt);
|
|
498
527
|
const out = {};
|
|
@@ -506,7 +535,8 @@ export function renderPrintFmtAsFieldDict(printFmt, printArgs, fieldMap) {
|
|
|
506
535
|
}
|
|
507
536
|
return out;
|
|
508
537
|
}
|
|
509
|
-
export function renderPrintFmt(printFmt, printArgs, fieldMap) {
|
|
538
|
+
export function renderPrintFmt(printFmt, printArgs, fieldMap, context) {
|
|
539
|
+
assertPrintArgCount(printFmt, printArgs, context);
|
|
510
540
|
const { normalizedArgs, values } = buildPrintArgValues(printArgs, fieldMap);
|
|
511
541
|
const tokens = getCachedPrintfTokens(printFmt);
|
|
512
542
|
let argIdx = 0;
|