Merge pull request #6874 from dotCooCoo/fix/script-failed-run-keeps-no-place

Scripts: a named engine keeps no source map for a run whose script does not compile
This commit is contained in:
BFlat
2026-11-13 00:49:38 +07:00
committed by GitHub
4 changed files with 45 additions and 5 deletions
+5 -2
View File
@@ -723,7 +723,7 @@ struct Host {
/// order: the error's mark ([`HostError::Marked`]) is its index.
eval_failures: RefCell<Vec<(Arc<str>, u32, u32)>>,
/// The realm's tag for the script ([`Realm::script_tag`]) once the run has started it, and
/// [`vectorcraft_js::UNKNOWN_SOURCE`] before.
/// [`vectorcraft_js::UNKNOWN_SOURCE`] before or when the script did not compile.
script_tag: Cell<u32>,
/// The run's name as its report gives it, which [`Host::name`] shortens for console lines.
report_name: Arc<str>,
@@ -1865,7 +1865,10 @@ impl Session {
.and_then(|()| if fresh { files::install(&mut realm, &link) } else { realm.reset_folder_current() })
.and_then(|()| {
let r = realm.start(&expanded.source);
host.script_tag.set(realm.script_tag());
// Source that does not compile defines no code, so its map is not kept.
if !matches!(&r, Err(e) if e.kind == RunErrorKind::Syntax) {
host.script_tag.set(realm.script_tag());
}
r
})
.map_err(|e| located(&host, &realm, &e))
+36
View File
@@ -230,6 +230,42 @@ fn an_engine_keeps_the_newest_places_within_its_budget() {
assert_eq!((d_place.0, d_place.1, d_place.2), (None, 0, 0), "a map larger than the budget is not kept");
}
/// A run of a named engine whose script does not compile defines no code, so it keeps no place
/// and pushes out none of the places kept before. An included file without a preprocessor line is
/// first parsed with the script it joins, so its syntax error ends the run as it starts.
#[test]
fn a_run_that_does_not_compile_keeps_no_place() {
let base: PathBuf = std::env::temp_dir().join(format!("vc-script-engines-uncompiled-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&base);
std::fs::create_dir_all(&base).unwrap();
// A kept place counts the names of the files the run read: four places of the failed run's size
// would not fit in the budget.
let mut text = String::from("#targetengine e\n");
for k in 0..10 {
std::fs::write(base.join(format!("ok{k}.jsxinc")), "var ok = 1;").unwrap();
text.push_str(&format!("#include \"ok{k}.jsxinc\"\n"));
}
std::fs::write(base.join("bad.jsxinc"), "var = ;").unwrap();
text.push_str("#include \"bad.jsxinc\"");
let bad = base.join("bad.jsx");
std::fs::write(&bad, text).unwrap();
let mut s = Session::new();
run(&mut s, "#targetengine e\nvar x = 1;").unwrap();
TEST_KEPT_MAP_BYTES.with(|b| b.set(Some(3000)));
run(&mut s, "#targetengine e\n\n\nfunction c() { throw new Error('c'); }").unwrap();
let mut kinds = vec![];
for _ in 0..5 {
assert!(s.execute("script.run", &json!({"path": bad.to_string_lossy()})).is_err());
kinds.push(s.script_report().unwrap()["kind"].clone());
}
assert!(run(&mut s, "#targetengine e\nc();").is_err());
let c_place = place(&s);
TEST_KEPT_MAP_BYTES.with(|b| b.set(None));
let _ = std::fs::remove_dir_all(base);
assert!(kinds.iter().all(|k| k == "syntax"), "{kinds:?}");
assert_eq!((c_place.0.as_deref(), c_place.1), (Some("t.js"), 4));
}
/// A `$.evalFile` an earlier run kept reads files the way the run that calls it does: relative to
/// that run's script, and as `$.evalFile` does in a run given as text.
#[test]
+3 -2
View File
@@ -1956,8 +1956,9 @@ no engines, and the engines it left are dropped when their own thread next uses
in the earlier run's script, a file it included or a file its `$.evalFile` ran, and the report's `file` names that
file. For an earlier script given as `source`, `file` is that run's name. The report's `name` and the console line's
name stay the calling run's, as in `caller.js: /path/lib.jsx:12:5: Error: kept`. Each engine keeps these places
for 8 MiB of source maps, the oldest going first; an error in code whose place is gone, or in code that `eval` or
`Function` compiled, is reported without a line and column, and the code still runs.
for 8 MiB of source maps, the oldest going first, and a run whose script does not compile keeps none. An error in
code whose place is gone, or in code that `eval` or `Function` compiled, is reported without a line and column, and
the code still runs.
- `vc`, `$`, `alert`, `confirm` and `prompt` act for the run that calls them, including a copy an earlier run kept in a
global. `vc.args` is the calling run's `args`, through a `vc` object an earlier run kept too. An engine
holds no document: `vc.document` is a copy, read when it is called. What a run sets on `$` (`$.locale`,
+1 -1
View File
@@ -762,7 +762,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- Scripts: an error a `finally` block passes on is still reported where it was thrown when the block's allocations collect garbage under a small heap limit, and a `finally` block that `return`, `break` or `continue` leaves drops the saved location of its error as it leaves, so a collection no longer counts that code as live. VectorCraft's own rule. `crates/js/src/host_tests.rs`.
- Scripts: a run reads its preprocessor lines before any of it runs: `#include` and `//@include` put another file in place of the line, looked for beside the including file, in the `#includepath` folders and in `JSINCLUDE`; `#script` names the run; `#target` and `#strict off` are taken, `#strict on` turns on strict write checks (below), `#targetengine` names the engine (below), and `#engine` is refused. `$.evalFile` runs a file with the same globals and returns its last value, and `$.fileName` and `$.includePath` give the file being run and the include folders. A script run from a file reads these files only in its folder and below, without `..` names or links that lead out, and an error in one is reported at its own file, line and column ([preprocessor directives](https://extendscript.docsforadobe.dev/extendscript-tools-features/preprocessor-directives/), [the `$` object](https://extendscript.docsforadobe.dev/extendscript-tools-features/dollar-object/); `crates/engine/src/tests/script_include.rs`).
- Scripts: `#targetengine name` (and `//@targetengine name`) runs the script in a named engine that lasts for the session, and a later script that names it sees the globals and functions earlier ones left ([preprocessor directives](https://extendscript.docsforadobe.dev/extendscript-tools-features/preprocessor-directives/)); `$.engineName` gives the name ([the `$` object](https://extendscript.docsforadobe.dev/extendscript-tools-features/dollar-object/)). VectorCraft's own rules: a script without the line and `#targetengine main` get a new engine each run, the line comes once in the script itself before its code, a session keeps 8 named engines with 64 MiB of heap each, an error or a stop keeps the globals the run set while the documents go back, kept host functions act for the run that calls them, `script.engines` lists the engines and `script.engine.drop` drops one, and the web build has none. `crates/engine/src/tests/script_engines.rs`.
- Scripts: a runtime error is shown at the line that was running ([the help page](https://extendscript.docsforadobe.dev/extendscript-toolkit/debugging-in-the-toolkit/)). VectorCraft's own rules: an error in code an earlier run of a named engine compiled, such as a function it defined, is reported at that code's file, line and column (the earlier run's script, a file it included or a file its `$.evalFile` ran), while the report and its console line keep the calling run's name; a script given as text is named by its run's name, and each engine keeps these places for 8 MiB of source maps, the oldest going first. `crates/engine/src/tests/script_engines.rs`.
- Scripts: a runtime error is shown at the line that was running ([the help page](https://extendscript.docsforadobe.dev/extendscript-toolkit/debugging-in-the-toolkit/)). VectorCraft's own rules: an error in code an earlier run of a named engine compiled, such as a function it defined, is reported at that code's file, line and column (the earlier run's script, a file it included or a file its `$.evalFile` ran), while the report and its console line keep the calling run's name; a script given as text is named by its run's name, and each engine keeps these places for 8 MiB of source maps, the oldest going first. An error in code whose place is gone, or in code that `eval` or `Function` compiled, is reported without a line and column, and the code still runs. `crates/engine/src/tests/script_engines.rs`.
- Scripts: `File` and `Folder` ([File object](https://extendscript.docsforadobe.dev/file-system-access/file-object/), [Folder object](https://extendscript.docsforadobe.dev/file-system-access/folder-object/)) name files and folders by platform or URI path, with `name`, `fsName`, `absoluteURI`, `relativeURI`, `parent`, `Folder.current` and `encode`/`decode`, and read them: `exists`, `length`, the dates, `getFiles` with a mask or a function, and `open('r')` with `read`, `readln`, `readch`, `seek`, `tell` and `close`, decoding by byte order mark, `encoding`, UTF-8 or CP1252. VectorCraft's own rules: a script reads only in its own folder and below (and inside the automation read roots when they are set); any other path and a network or device path are refused before anything is looked up, and a link that leads out is refused as well (`false` or `null`, with `error` set); a file is read whole up to 8 MiB and a run reads up to 32 MiB; writing, the file dialogs and `execute` are not available yet. `crates/engine/src/tests/script_io.rs`.
- Scripts: `#strict on` (and `//@strict on`) runs the script with the checks the [`$` object](https://extendscript.docsforadobe.dev/extendscript-tools-features/dollar-object/) help page gives for `$.strict`: an assignment to a read-only property throws a `TypeError`, and so does a new property on an object that takes none. VectorCraft's own rules: a getter-only property counts as read-only, the objects that take no new properties are the non-extensible ones (`Object.preventExtensions`, `seal`, `freeze`, and the frozen `vc` and `$` objects), the last `#strict` line of the script and its includes sets the whole run, a file `$.evalFile` runs keeps its own line's checks for its code, and the rest of ES5 strict mode stays with `'use strict'`. Without the line, runs are unchanged. `crates/engine/src/tests/script_include.rs` (`strict_on_*`, `strict_mode_in_eval_file_is_scoped`), `crates/js/src/interp_tests.rs` (`strict_writes_are_separate_from_es5_strict`).
- Scripts folder: File › Scripts lists the `.js` and `.jsx` files of the `Scripts` folder next to the preferences, each named after its file without the extension, with each subfolder as a submenu, above Check Script… and Other Script… (https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/install-and-run-scripts.html). VectorCraft's own rules: the folder is read again when the window becomes active and when the File menu opens (in the macOS menu bar, when any of its menus opens, and the new listing shows the next time the menu opens), links are not followed, and one read stops at 2,048 entries, 512 scripts, 128 subfolders, 4 levels or 250 ms. `script.listInstalled` returns the listing. `crates/engine/src/tests/script_folder.rs`, `crates/ui-egui/src/tests/scripts_menu.rs`.