Skip to content

Commit

Permalink
Changes the implementation of Error.captureStackTrace so that it do…
Browse files Browse the repository at this point in the history
…es not need to create `new Error()` in order to provide the stack trace; this fixes a problem where a stack overflow can occur when the global `Error` class has been replaced, such as in the recent implementation of `core-js` polyfill (see https://github.com/zloirock/core-js)
  • Loading branch information
johnspackman committed Jan 11, 2024
1 parent ef18cb9 commit 6fe307f
Showing 1 changed file with 18 additions and 8 deletions.
26 changes: 18 additions & 8 deletions src/org/mozilla/javascript/NativeError.java
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ public Object getStackDelegated() {
stack = value;
return value;
}

public void setStackDelegated(Object value) {
stackProvider = null;
stack = value;
Expand Down Expand Up @@ -284,34 +284,44 @@ private static String js_toSource(Context cx, Scriptable scope, Scriptable thisO
return sb.toString();
}

private static int s_captureStackTraceRecursion = 0;
private static void js_captureStackTrace(
Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {

if (s_captureStackTraceRecursion > 10) {
throw new IllegalStateException("Too much recursion when capturing stack trace");
}
s_captureStackTraceRecursion++;
try {
ScriptableObject obj = (ScriptableObject) ScriptRuntime.toObject(cx, scope, args[0]);
Function func = null;
if (args.length > 1) {
func = (Function) ScriptRuntime.toObjectOrNull(cx, args[1], scope);
}

// Create a new error that will have the correct prototype so we can re-use "getStackTrace"
NativeError err = (NativeError) cx.newObject(thisObj, "Error");
// Wire it up so that it will have an actual exception with a stack trace
err.setStackProvider(new EvaluatorException("[object Object]"));

// Figure out if they passed a function used to hide part of the stack
String hideFunction = null;
if (func != null) {
Object funcName = func.get("name", func);
if ((funcName != null) && !Undefined.isUndefined(funcName)) {
err.associateValue(STACK_HIDE_KEY, Context.toString(funcName));
hideFunction = Context.toString(funcName);
}
}

RhinoException stackProvider = new EvaluatorException("[object Object]");
ScriptStackElement[] stackTrace = stackProvider.getScriptStack(DEFAULT_STACK_LIMIT, hideFunction);
Object value = RhinoException.formatStackTrace(stackTrace, stackProvider.details());

// from https://v8.dev/docs/stack-trace-api
// Error.captureStackTrace(error, constructorOpt)
// adds a stack property to the given error object that yields the stack trace
// at the time captureStackTrace was called. Stack traces collected through
// Error.captureStackTrace are immediately collected, formatted,
// and attached to the given error object.
obj.defineProperty(STACK_TAG, err.get(STACK_TAG), ScriptableObject.DONTENUM);
obj.defineProperty(STACK_TAG, value, ScriptableObject.DONTENUM);
}finally {
s_captureStackTraceRecursion--;
}
}

@Override
Expand Down

0 comments on commit 6fe307f

Please sign in to comment.