From 3cdc7af9ff55461a3cb66be9507ba081808265fa Mon Sep 17 00:00:00 2001 From: Nigel Ihza Firdausy <106657905+Ni-zav@users.noreply.github.com> Date: Sun, 11 Oct 2026 21:41:21 +0700 Subject: [PATCH] fix(script): implement ExtendScript String.substr compatibility (#840) --- crates/script/src/prelude.js | 19 +++++++++++++++++++ crates/script/src/tests.rs | 22 ++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/crates/script/src/prelude.js b/crates/script/src/prelude.js index f50a968c..48ec1bb2 100644 --- a/crates/script/src/prelude.js +++ b/crates/script/src/prelude.js @@ -27,6 +27,25 @@ function __str(v) { return String(v); } +// ExtendScript includes the ES3 String.substr(start, length) method, which Boa +// does not currently provide. Follow its legacy length (not end-index) semantics. +if (typeof String.prototype.substr !== "function") { + Object.defineProperty(String.prototype, "substr", { + value: function (start, length) { + if (this === null || this === undefined) throw new TypeError("String.substr called on null or undefined"); + var text = String(this), n = text.length; + var from = Number(start); + if (isNaN(from)) from = 0; + // ToIntegerOrInfinity, then clamp positive or negative offsets. + from = from < 0 ? Math.max(n + Math.ceil(from), 0) : Math.min(Math.floor(from), n); + var count = length === undefined ? n - from : Number(length); + if (isNaN(count) || count <= 0) return ""; + return text.slice(from, from + count); + }, + writable: true, configurable: true + }); +} + // ------------------------------------------------------------------ enums function __enum(name, keys, base) { diff --git a/crates/script/src/tests.rs b/crates/script/src/tests.rs index b50a7bfb..ceb72e56 100644 --- a/crates/script/src/tests.rs +++ b/crates/script/src/tests.rs @@ -730,3 +730,25 @@ fn import_options_sequence_picks_one_still_or_the_run() { assert_eq!(kind_of(&s, "shot_[0001-0003].png"), FootageKind::Sequence); let _ = std::fs::remove_dir_all(&dir); } + +#[test] +fn extendscript_substr_keeps_start_and_length_semantics() { + let mut s = session(); + let o = ok( + &mut s, + r#"[ + "abcdef".substr(0, 2), + "abcdef".substr(2, 3), + "abcdef".substr(-2), + "abcdef".substr(-20, 2), + "abcdef".substr(2, 0), + "abcdef".substr(2, -3), + "abcdef".substr(2.9, 2.9), + "abcdef".substr(3), + "abcdef".substr(30, 2), + "abcdef".substr(NaN, 2), + "abcdef".substr(2, NaN) + ]"#, + ); + assert_eq!(o.result, json!(["ab", "cde", "ef", "ab", "", "", "cd", "def", "", "ab", ""])); +}