Compatibility

Does Obfuscation Break Modern String Methods?

String handling is the part of a codebase most visibly rewritten by protection, because moving literals into an encoded table is one of the headline transforms. That makes it a reasonable thing to worry about. We measured the whole modern string method surface against five configurations, including the string table itself, and the behaviour is identical.

Why this one deserves a measurement rather than a reassurance

With most compatibility questions the honest answer is that the transform does not go anywhere near the feature. String methods are different. The move strings into an array and encode strings transforms take every literal in your file, put it in a table, and replace the literal with a lookup call. Your string values genuinely are being relocated and rewritten.

So the question is not rhetorical: if a literal comes back from the table subtly different, string methods are exactly where you would see it. We ran the sample under the string-table preset alongside the four other configurations for that reason.

All five matched the original output line for line. The literals come back byte-identical, and every method operating on them behaves the same.

The modern method surface, measured

The trimming family holds its distinctions: from " Hello World ", trim() gives Hello World, trimStart() keeps the trailing spaces and trimEnd() keeps the leading ones. Padding is intact including its less obvious rules: "7".padStart(3, "0") gives 007, "abcdef".padStart(3, "0") returns the string unchanged because it is already longer than the target, and "x".padStart(5, "ab") gives ababx by repeating and truncating the pad.

Replacement keeps the distinction that catches people out: "a-b-c".replace("-", "+") replaces only the first occurrence and gives a+b-c, while replaceAll gives a+b+c. Function replacers and dollar-sign capture references both survive, so "john smith".replace(/(\w+) (\w+)/, "$2 $1") still produces smith john.

Indexed access is unchanged across the old and new spellings: at(0), at(-1), charAt, charCodeAt and codePointAt all returned their original values, with "abc".at(-1) correctly giving c.

Searching and splitting behave identically, including the arguments people forget exist. startsWith with a position offset still works. "a,b,c".split(",", 2) still honours the limit and returns two elements. Splitting on a regular expression and splitting on the empty string both produced their original arrays.

Strings are still immutable, and identity still works

Two properties underpin most string code, and both were checked explicitly rather than assumed.

Immutability holds: calling toUpperCase() and replaceAll() on a variable and discarding the results leaves the original variable unchanged. Every string method returns a new string after protection exactly as before.

Identity holds too, which is the one that interacts with the string table. "abc" === "ab" + "c" is still true after the literals have been moved into an encoded table and reassembled at runtime. Comparison operators still order strings the same way, and length is unchanged. If your code uses strings as keys, or compares them with ===, that keeps working. There is more on the comparison side in switch and string comparison.

The failure mode worth knowing about

As with number formatting, the one way to break this is a member-renaming pattern that is broader than you intended. The rename is driven by a regular expression matched against member names, and it cannot tell a built-in method from one of yours.

We measured it with a pattern of ^(padStart)$. Every call site was rewritten, so "7".padStart(3, "0") became "7"._0x1(3, "0"), and execution stopped there with TypeError: "7"._0x1 is not a function. The word padStart still appeared in the output, but only inside string literals, which are not rename sites.

The useful detail is that the failure is immediate and named. You do not get a subtly wrong string; you get a thrown TypeError on the first call, with the generated name visible in the message. Keep the pattern anchored and explicit and this cannot happen.

Practical guidance

You can enable the string table on code that does heavy string manipulation without a compatibility review. That is the transform people are most nervous about here, and it is the one we specifically included in the measurement.

What the string table does change is not correctness but readability of your literals to someone reading the bundle, which is the point. It does not make strings unreadable to a determined reader, since the table is present in the file and can be walked; it raises the effort. Our page on what obfuscation does not rename is the honest boundary on that.

If any string-adjacent behaviour does look different after protection, the first thing to check is the member-renaming pattern, and the second is whether the behaviour was actually different in the original too. Those two account for essentially everything reported in this area.

Frequently asked questions

Does the string array transform change my string values?

No. We ran a full string-method sample under the preset that moves every literal into an array and encodes it, and the output was identical to the unprotected original line for line. The literals are relocated and reassembled at runtime, but they come back byte-identical, so methods operating on them behave the same.

Do replaceAll and padStart work in protected code?

Yes, both were measured identical. replaceAll still replaces every occurrence where replace handles only the first, and padStart keeps its rules for over-length strings and multi-character pads. The same held for trimStart, trimEnd, at, repeat, codePointAt and split with a limit.

Is string identity preserved when literals are moved into a table?

Yes. The check that abc equals the concatenation of ab and c still returns true after the literals have been moved into an encoded table and reassembled at runtime. String comparison operators and length are also unchanged, so using strings as object keys or comparing them with strict equality keeps working.

Are strings still immutable after protection?

Yes. Calling methods such as toUpperCase or replaceAll and discarding the result leaves the original variable unchanged, exactly as before. Every string method still returns a new string rather than mutating in place.

What breaks string methods in protected code?

One thing: a member-renaming pattern broad enough to match a built-in method name. Because renaming is name-based rather than type-aware, a pattern matching padStart rewrites those call sites and the built-in is no longer reachable. We measured this and it throws TypeError on the first call, naming the generated identifier, so it surfaces immediately rather than corrupting output.

Should I test string handling separately before shipping?

Your existing tests run against the protected bundle are enough, because the behaviour is identical. The setting that deserves a manual review is the member-renaming pattern. Treat it like any regular expression that runs in production and scope it to names your own objects define.

Related reading