From 901e42a41be2f5b85366d29c946b977b6c5addc1 Mon Sep 17 00:00:00 2001 From: peima Date: Sun, 2 Aug 2026 00:13:33 +0800 Subject: [PATCH] =?UTF-8?q?fix(runtime):=20onGenerateRoute=20pathElements?= =?UTF-8?q?=20=E8=B6=8A=E7=95=8C=20RangeError=20-=20buy-service/renew-mini?= =?UTF-8?q?office=20=E5=8A=A0=E9=95=BF=E5=BA=A6=E6=A3=80=E6=9F=A5=20(pathE?= =?UTF-8?q?lements.length=20>=3D=204/3)=20-=20=E6=A0=B9=E5=9B=A0:=20Flutte?= =?UTF-8?q?r=20web=20=E6=B7=B1=E9=93=BE=E6=8E=A5=E6=B8=90=E8=BF=9B?= =?UTF-8?q?=E5=BC=8F=E8=B7=AF=E5=BE=84=E8=A7=A3=E6=9E=90,=20onGenerateRout?= =?UTF-8?q?e=20=E8=A2=AB=E8=B0=83=E7=94=A8=E5=A4=9A=E6=AC=A1,=20=20=20path?= =?UTF-8?q?Elements=20=E9=80=92=E5=A2=9E=20([,buy-service]=20->=20[,buy-se?= =?UTF-8?q?rvice,1]=20->=20=E5=AE=8C=E6=95=B4),=20=20=20=E5=89=8D=E5=87=A0?= =?UTF-8?q?=E6=AC=A1=E8=B7=AF=E5=BE=84=E7=9F=AD=E4=BD=86=20pathElements[1]?= =?UTF-8?q?=E5=B7=B2=E5=8C=B9=E9=85=8D=20->=20int.parse([2])/[3]=20?= =?UTF-8?q?=E8=B6=8A=E7=95=8C=20-=20=E7=9B=B4=E6=8E=A5=E8=AE=BF=E9=97=AE/?= =?UTF-8?q?=E5=88=B7=E6=96=B0=20/buy-service/x/y,=20/renew-minioffice/x=20?= =?UTF-8?q?=E6=97=B6=E5=B4=A9=E6=BA=83=20-=20=E6=81=A2=E5=A4=8D=20buy=5Fse?= =?UTF-8?q?rvice=20=E5=AE=9E=E9=AA=8C=E4=BB=A3=E7=A0=81=20(data=20?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=20+=20mounted=20=E4=BF=9D=E7=95=99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 6 ++--- tools/e2e/diag_ctx.js | 16 +++++++++++++ tools/e2e/diag_deep.js | 13 +++++++++++ tools/e2e/diag_exp.js | 14 +++++++++++ tools/e2e/diag_paths.js | 15 ++++++++++++ tools/e2e/diag_routes.js | 14 +++++++++++ tools/e2e/diag_seq.js | 13 +++++++++++ tools/e2e/diag_stack.js | 19 +++++++++++++++ tools/e2e/diag_then.js | 11 +++++++++ tools/e2e/main.dart.js | 46 +++++++++++++++++++++++++++++++++++++ tools/e2e/package-lock.json | 12 +++++++++- tools/e2e/package.json | 3 ++- tools/e2e/parse_map.js | 15 ++++++++++++ 13 files changed, 192 insertions(+), 5 deletions(-) create mode 100644 tools/e2e/diag_ctx.js create mode 100644 tools/e2e/diag_deep.js create mode 100644 tools/e2e/diag_exp.js create mode 100644 tools/e2e/diag_paths.js create mode 100644 tools/e2e/diag_routes.js create mode 100644 tools/e2e/diag_seq.js create mode 100644 tools/e2e/diag_stack.js create mode 100644 tools/e2e/diag_then.js create mode 100644 tools/e2e/main.dart.js create mode 100644 tools/e2e/parse_map.js diff --git a/lib/main.dart b/lib/main.dart index 93d6dfa..b2b92cd 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -36,7 +36,7 @@ void main() { FlutterError.onError = (details) { FlutterError.presentError(details); print('CK_ERROR: ${details.exceptionAsString()}'); - print('CK_CTX: ${details.context.toString()}'); + print('CK_CTX: ${details.context}'); print('CK_STACK: ${details.stack}'); }; // Enable configureApp will cause route problem. @@ -171,12 +171,12 @@ class MyApp extends StatelessWidget { ), ); } - if (pathElements[1] == 'renew-minioffice') { + if (pathElements[1] == 'renew-minioffice' && pathElements.length >= 3) { return MaterialPageRoute(builder: (BuildContext context) => RenewMiniOffice(int.parse(pathElements[2])) ); } - if (pathElements[1] == 'buy-service') { + if (pathElements[1] == 'buy-service' && pathElements.length >= 4) { return MaterialPageRoute(builder: (BuildContext context) => BuyService(int.parse(pathElements[2]), pathElements[3]) ); diff --git a/tools/e2e/diag_ctx.js b/tools/e2e/diag_ctx.js new file mode 100644 index 0000000..2d4f014 --- /dev/null +++ b/tools/e2e/diag_ctx.js @@ -0,0 +1,16 @@ +const { chromium } = require('playwright'); +(async () => { + const browser = await chromium.launch({ headless: true }); + const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage(); + const logs = []; + page.on('console', m => logs.push(m.text())); + await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{}); + await page.waitForTimeout(8000); + for (let i=0;il.startsWith('CK_CTX')); + if(ctx) console.log(' CTX:', ctx.slice(0,150)); + } + } + await browser.close(); +})(); diff --git a/tools/e2e/diag_deep.js b/tools/e2e/diag_deep.js new file mode 100644 index 0000000..d964be7 --- /dev/null +++ b/tools/e2e/diag_deep.js @@ -0,0 +1,13 @@ +const { chromium } = require('playwright'); +(async () => { + const browser = await chromium.launch({ headless: true }); + const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage(); + const logs = []; + page.on('console', m => logs.push(m.text())); + await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{}); + await page.waitForTimeout(7000); + const ctx = logs.filter(l => l.startsWith('CK_CTX')); + if (ctx.length) console.log(ctx[0].slice(0, 600)); + else console.log('无 CK_CTX'); + await browser.close(); +})(); diff --git a/tools/e2e/diag_exp.js b/tools/e2e/diag_exp.js new file mode 100644 index 0000000..e1cbf52 --- /dev/null +++ b/tools/e2e/diag_exp.js @@ -0,0 +1,14 @@ +const { chromium } = require('playwright'); +(async () => { + const browser = await chromium.launch({ headless: true }); + const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage(); + const logs = []; + page.on('console', m => logs.push(m.text())); + await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{}); + await page.waitForTimeout(7000); + const range = logs.filter(l => l.includes('RangeError')); + const err = logs.filter(l => l.includes('CK_BUY_ERR')); + console.log('RangeError 数:', range.length, err.length ? '| CK_BUY_ERR:'+err[0].slice(0,100) : ''); + if (range.length) console.log(range[0].slice(0,120)); + await browser.close(); +})(); diff --git a/tools/e2e/diag_paths.js b/tools/e2e/diag_paths.js new file mode 100644 index 0000000..633fe3b --- /dev/null +++ b/tools/e2e/diag_paths.js @@ -0,0 +1,15 @@ +const { chromium } = require('playwright'); +(async () => { + const browser = await chromium.launch({ headless: true }); + const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage(); + const logs = []; + page.on('console', m => logs.push(m.text())); + await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{}); + await page.waitForTimeout(7000); + console.log('=== path elements 打印(看是否有短路径)==='); + logs.filter(l => l.includes('path elements')).forEach(l => console.log(' ' + l.slice(0,80))); + console.log('=== RangeError 之前的最后几条 console ==='); + const idx = logs.findIndex(l => l.includes('RangeError')); + if (idx >= 0) logs.slice(Math.max(0,idx-4), idx).forEach(l => console.log(' ' + l.slice(0,90))); + await browser.close(); +})(); diff --git a/tools/e2e/diag_routes.js b/tools/e2e/diag_routes.js new file mode 100644 index 0000000..4b82891 --- /dev/null +++ b/tools/e2e/diag_routes.js @@ -0,0 +1,14 @@ +const { chromium } = require('playwright'); +(async () => { + const browser = await chromium.launch({ headless: true }); + for (const route of ['/download', '/contact-stores', '/me', '/orders', '/buy-service/1/ministore']) { + const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage(); + let range = 0; + page.on('console', m => { if (m.text().includes('RangeError')) range++; }); + await page.goto('http://127.0.0.1:8099'+route, { waitUntil: 'load' }).catch(()=>{}); + await page.waitForTimeout(6000); + console.log(`${range>0?'✗ '+range+' RangeError':'✓ OK'} ${route}`); + await page.close(); + } + await browser.close(); +})(); diff --git a/tools/e2e/diag_seq.js b/tools/e2e/diag_seq.js new file mode 100644 index 0000000..4cb1e1d --- /dev/null +++ b/tools/e2e/diag_seq.js @@ -0,0 +1,13 @@ +const { chromium } = require('playwright'); +(async () => { + const browser = await chromium.launch({ headless: true }); + const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage(); + const logs = []; + page.on('console', m => logs.push(m.text())); + await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{}); + await page.waitForTimeout(7000); + const seq = logs.filter(l => l.startsWith('CK_BS_') || l.includes('RangeError')); + console.log('时序:'); + seq.slice(0,10).forEach(l => console.log(' ' + l.slice(0,90))); + await browser.close(); +})(); diff --git a/tools/e2e/diag_stack.js b/tools/e2e/diag_stack.js new file mode 100644 index 0000000..f5f9fa4 --- /dev/null +++ b/tools/e2e/diag_stack.js @@ -0,0 +1,19 @@ +const { chromium } = require('playwright'); +(async () => { + const browser = await chromium.launch({ headless: true }); + const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage(); + const logs = []; + page.on('console', m => logs.push(m.text())); + await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{}); + await page.waitForTimeout(7000); + const stack = logs.filter(l => l.startsWith('CK_STACK')); + if (stack.length) { + // 找 .dart 源文件帧(非 main.dart.js) + const dartFrames = stack[0].split('\n').filter(l => l.includes('.dart') && !l.includes('main.dart.js')); + console.log('源文件帧:', dartFrames.length ? dartFrames.slice(0,5).join('\n ') : '(无,全是 main.dart.js)'); + // 也看前 3 个 main.dart.js 帧 + const jsFrames = stack[0].split('\n').filter(l => l.includes('main.dart.js')).slice(0,4); + console.log('main.dart.js 帧:', jsFrames.join('\n ')); + } + await browser.close(); +})(); diff --git a/tools/e2e/diag_then.js b/tools/e2e/diag_then.js new file mode 100644 index 0000000..9cb5b37 --- /dev/null +++ b/tools/e2e/diag_then.js @@ -0,0 +1,11 @@ +const { chromium } = require('playwright'); +(async () => { + const browser = await chromium.launch({ headless: true }); + const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage(); + const logs = []; + page.on('console', m => logs.push(m.text())); + await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{}); + await page.waitForTimeout(8000); + logs.filter(l => l.includes('CK_BUY_THEN') || l.includes('RangeError') || l.includes('CK_ERROR')).slice(0,5).forEach(l => console.log(l.slice(0,160))); + await browser.close(); +})(); diff --git a/tools/e2e/main.dart.js b/tools/e2e/main.dart.js new file mode 100644 index 0000000..bddcade --- /dev/null +++ b/tools/e2e/main.dart.js @@ -0,0 +1,46 @@ +"use strict"; + + + +// A map containing the URLs for the bootstrap scripts in debug. +let _scriptUrls = { + "mapper": "stack_trace_mapper.js", + "requireJs": "require.js" +}; + +// Create a TrustedTypes policy so we can attach Scripts... +let _ttPolicy; +if (window.trustedTypes) { + _ttPolicy = trustedTypes.createPolicy("flutter-tools-bootstrap", { + createScriptURL: (url) => { + let scriptUrl = _scriptUrls[url]; + if (!scriptUrl) { + console.error("Unknown Flutter Web bootstrap resource!", url); + } + return scriptUrl; + } + }); +} + +// Creates a TrustedScriptURL for a given `scriptName`. +// See `_scriptUrls` and `_ttPolicy` above. +function getTTScriptUrl(scriptName) { + let defaultUrl = _scriptUrls[scriptName]; + return _ttPolicy ? _ttPolicy.createScriptURL(scriptName) : defaultUrl; +} + +// Attach source mapping. +var mapperEl = document.createElement("script"); +mapperEl.defer = true; +mapperEl.async = false; +mapperEl.src = getTTScriptUrl("mapper"); +document.head.appendChild(mapperEl); + +// Attach require JS. +var requireEl = document.createElement("script"); +requireEl.defer = true; +requireEl.async = false; +requireEl.src = getTTScriptUrl("requireJs"); +// This attribute tells require JS what to load as main (defined below). +requireEl.setAttribute("data-main", "main_module.bootstrap"); +document.head.appendChild(requireEl); diff --git a/tools/e2e/package-lock.json b/tools/e2e/package-lock.json index b2b0cfe..bb4d6ae 100644 --- a/tools/e2e/package-lock.json +++ b/tools/e2e/package-lock.json @@ -9,7 +9,8 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "playwright": "^1.62.1" + "playwright": "^1.62.1", + "source-map": "^0.8.0" } }, "node_modules/fsevents": { @@ -55,6 +56,15 @@ "engines": { "node": ">=20" } + }, + "node_modules/source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0.tgz", + "integrity": "sha512-d8EqvL+k/SOXCreS/SUzg2ciyHqBBLcN/yuRjFsbvVhHTE2pgei7oAhmPM7kWFbkX6OSMQfUq4KbkF3au9lhYQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } } } } diff --git a/tools/e2e/package.json b/tools/e2e/package.json index 74e80f8..d210161 100644 --- a/tools/e2e/package.json +++ b/tools/e2e/package.json @@ -11,6 +11,7 @@ "license": "ISC", "type": "commonjs", "dependencies": { - "playwright": "^1.62.1" + "playwright": "^1.62.1", + "source-map": "^0.8.0" } } diff --git a/tools/e2e/parse_map.js b/tools/e2e/parse_map.js new file mode 100644 index 0000000..891f2b7 --- /dev/null +++ b/tools/e2e/parse_map.js @@ -0,0 +1,15 @@ +const fs = require('fs'); +const { SourceMapConsumer } = require('source-map'); +(async () => { + const mapJson = await (await fetch('http://127.0.0.1:8099/main.dart.js.map')).text().catch(()=>null); + if (!mapJson) { console.log('无 sourcemap'); return; } + const smc = await new SourceMapConsumer(mapJson); + // 解析 299:254 (line:col) + const pos = smc.originalPositionFor({ line: 299, column: 254 }); + console.log('299:254 →', pos.source, pos.line + ':' + pos.column, '|', pos.name); + // 也试 299 的前后几行 + for (const c of [200, 100, 50]) { + const p = smc.originalPositionFor({ line: 299, column: c }); + if (p.source) console.log(`299:${c} →`, p.source.split('/').slice(-2).join('/') + ':' + p.line); + } +})();