Skip to content

Commit 91e3758

Browse files
committed
[GR-30889] Ignore Error.stackTraceLimit if it's not a data property.
PullRequest: js/1982
2 parents b9bbeef + d776671 commit 91e3758

File tree

6 files changed

+227
-15
lines changed

6 files changed

+227
-15
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl.
6+
*/
7+
/*
8+
* Test that getting Error.stackTraceLimit is side-effect-free.
9+
*/
10+
11+
class MyError extends Error {
12+
}
13+
14+
function recurse(n) {
15+
if (n == 0) {
16+
throw new MyError("boom");
17+
} else {
18+
return recurse(n - 1);
19+
}
20+
}
21+
22+
function countOccurrences(str, search) {
23+
var count = 0;
24+
var pos = 0;
25+
while ((pos = str.indexOf(search, pos)) >= 0) {
26+
count++;
27+
pos++;
28+
}
29+
return count;
30+
}
31+
32+
Error.stackTraceLimit = 10;
33+
assertThrows(() => recurse(20), MyError, e => assertSame(10, countOccurrences(e.stack, 'recurse')));
34+
35+
var sideEffect = false;
36+
Object.defineProperty(Error, "stackTraceLimit", {get: function() { sideEffect = true; return 5; }, configurable: true})
37+
assertThrows(() => recurse(20), MyError);
38+
assertSame(false, sideEffect);
39+
40+
Object.defineProperty(Error, "stackTraceLimit", {get: function() { throw "oh no"; }, configurable: true})
41+
assertThrows(() => recurse(20), MyError);
42+
43+
Object.defineProperty(Error, "stackTraceLimit", {get: function() { throw new Error("oh no"); }, configurable: true})
44+
assertThrows(() => recurse(20), MyError);
45+
46+
Object.defineProperty(Error, "stackTraceLimit", {value: 5, configurable: true, writable: true})
47+
assertThrows(() => recurse(20), MyError, e => assertSame(5, countOccurrences(e.stack, 'recurse')));
48+
49+
// stackTraceLimit missing or not of type Number.
50+
var emptyStack = e => {assertSame(0, countOccurrences(String(e.stack), 'recurse')); assertSame(true, 'stack' in e);};
51+
52+
Error.stackTraceLimit = 0;
53+
assertThrows(() => recurse(20), MyError, emptyStack);
54+
55+
Error.stackTraceLimit = -2147483649;
56+
assertThrows(() => recurse(20), MyError, emptyStack);
57+
58+
// stackTraceLimit missing or not of type Number.
59+
var invalidLimit = e => {assertSame(0, countOccurrences(String(e.stack), 'recurse')); assertSame(true, 'stack' in e);};
60+
61+
delete Error.stackTraceLimit;
62+
assertSame(false, 'stackTraceLimit' in Error);
63+
assertThrows(() => recurse(20), MyError, invalidLimit);
64+
65+
Error.prototype.stackTraceLimit = 10;
66+
assertThrows(() => recurse(20), MyError, invalidLimit);
67+
68+
Error.stackTraceLimit = {valueOf() { return 10; }};
69+
assertThrows(() => recurse(20), MyError, invalidLimit);
70+
71+
Error.stackTraceLimit = "10";
72+
assertThrows(() => recurse(20), MyError, invalidLimit);
73+
74+
Error.stackTraceLimit = 10n;
75+
assertThrows(() => recurse(20), MyError, invalidLimit);
76+
77+
function assertThrows(fn, errorType, verifier) {
78+
try {
79+
fn();
80+
} catch (e) {
81+
if (errorType) {
82+
if (!(e instanceof errorType)) {
83+
throw new Error('expected ' + errorType.name + ', actual: ' + (e.name || e));
84+
}
85+
}
86+
if (verifier) {
87+
verifier(e);
88+
}
89+
return;
90+
}
91+
throw Error('should have thrown: ' + fn);
92+
}
93+
94+
function assertSame(expected, actual) {
95+
if (expected !== actual) {
96+
throw new Error('expected: [' + expected + '], actual: [' + actual +']');
97+
}
98+
}

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/nodes/access/ErrorStackTraceLimitNode.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -42,21 +42,28 @@
4242

4343
import com.oracle.truffle.api.dsl.Specialization;
4444
import com.oracle.truffle.api.object.DynamicObject;
45+
import com.oracle.truffle.api.object.DynamicObjectLibrary;
4546
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
46-
import com.oracle.truffle.js.nodes.cast.JSToIntegerAsIntNode;
47+
import com.oracle.truffle.js.nodes.cast.IsNumberNode;
48+
import com.oracle.truffle.js.nodes.cast.JSToIntegerAsLongNode;
4749
import com.oracle.truffle.js.runtime.JSContext;
4850
import com.oracle.truffle.js.runtime.JSErrorType;
4951
import com.oracle.truffle.js.runtime.builtins.JSError;
52+
import com.oracle.truffle.js.runtime.objects.JSObjectUtil;
53+
import com.oracle.truffle.js.runtime.objects.JSProperty;
54+
import com.oracle.truffle.js.runtime.objects.Undefined;
5055

5156
public abstract class ErrorStackTraceLimitNode extends JavaScriptBaseNode {
5257
private final JSContext context;
53-
@Child private PropertyGetNode getStackTraceLimit;
54-
@Child private JSToIntegerAsIntNode toInteger;
58+
@Child private DynamicObjectLibrary getStackTraceLimit;
59+
@Child private IsNumberNode isNumber;
60+
@Child private JSToIntegerAsLongNode toInteger;
5561

5662
protected ErrorStackTraceLimitNode(JSContext context) {
5763
this.context = context;
58-
this.getStackTraceLimit = PropertyGetNode.create(JSError.STACK_TRACE_LIMIT_PROPERTY_NAME, false, context);
59-
this.toInteger = JSToIntegerAsIntNode.create();
64+
this.getStackTraceLimit = JSObjectUtil.createDispatched(JSError.STACK_TRACE_LIMIT_PROPERTY_NAME);
65+
this.isNumber = IsNumberNode.create();
66+
this.toInteger = JSToIntegerAsLongNode.create();
6067
}
6168

6269
public static ErrorStackTraceLimitNode create(JSContext context) {
@@ -66,7 +73,14 @@ public static ErrorStackTraceLimitNode create(JSContext context) {
6673
@Specialization
6774
public int doInt() {
6875
DynamicObject errorConstructor = context.getRealm().getErrorConstructor(JSErrorType.Error);
69-
return Math.max(0, toInteger.executeInt(getStackTraceLimit.getValue(errorConstructor)));
76+
if (JSProperty.isData(getStackTraceLimit.getPropertyFlagsOrDefault(errorConstructor, JSError.STACK_TRACE_LIMIT_PROPERTY_NAME, JSProperty.ACCESSOR))) {
77+
Object value = getStackTraceLimit.getOrDefault(errorConstructor, JSError.STACK_TRACE_LIMIT_PROPERTY_NAME, Undefined.instance);
78+
if (isNumber.execute(value)) {
79+
long limit = toInteger.executeLong(value);
80+
return (int) Math.max(0, Math.min(limit, Integer.MAX_VALUE));
81+
}
82+
}
83+
return 0;
7084
}
7185

7286
public abstract int executeInt();
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.truffle.js.nodes.cast;
42+
43+
import com.oracle.truffle.api.dsl.Fallback;
44+
import com.oracle.truffle.api.dsl.Specialization;
45+
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
46+
import com.oracle.truffle.js.runtime.JSRuntime;
47+
import com.oracle.truffle.js.runtime.SafeInteger;
48+
49+
/**
50+
* @see JSRuntime#isNumber(Object)
51+
*/
52+
public abstract class IsNumberNode extends JavaScriptBaseNode {
53+
54+
public static IsNumberNode create() {
55+
return IsNumberNodeGen.create();
56+
}
57+
58+
public abstract boolean execute(Object value);
59+
60+
@Specialization
61+
protected static boolean doInt(@SuppressWarnings("unused") int value) {
62+
return true;
63+
}
64+
65+
@Specialization
66+
protected static boolean doLong(@SuppressWarnings("unused") long value) {
67+
return true;
68+
}
69+
70+
@Specialization
71+
protected static boolean doSafeInteger(@SuppressWarnings("unused") SafeInteger value) {
72+
return true;
73+
}
74+
75+
@Specialization
76+
protected static boolean doDouble(@SuppressWarnings("unused") double value) {
77+
return true;
78+
}
79+
80+
@Fallback
81+
protected static boolean doOther(@SuppressWarnings("unused") Object value) {
82+
return false;
83+
}
84+
}

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/nodes/control/TryCatchNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ public Object execute(Throwable ex) {
290290
return ((UserScriptException) ex).getErrorObject();
291291
} else if (ex instanceof StackOverflowError) {
292292
CompilerDirectives.transferToInterpreter();
293-
JSException rangeException = Errors.createRangeErrorStackOverflow(this);
293+
JSException rangeException = Errors.createRangeErrorStackOverflow(ex, this);
294294
return doJSException(rangeException);
295295
} else {
296296
truffleExceptionBranch.enter();

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/runtime/Errors.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,11 @@ public static JSException createRangeErrorStackOverflow(Node originatingNode) {
604604
return Errors.createRangeError("Maximum call stack size exceeded", originatingNode);
605605
}
606606

607+
@TruffleBoundary
608+
public static JSException createRangeErrorStackOverflow(Throwable cause, Node originatingNode) {
609+
return Errors.createRangeError("Maximum call stack size exceeded", cause, originatingNode);
610+
}
611+
607612
@TruffleBoundary
608613
public static JSException createRangeErrorInvalidStringLength() {
609614
return Errors.createRangeError("Invalid string length");
@@ -835,6 +840,11 @@ public static JSException createRangeError(Throwable cause, Node originatingNode
835840
return JSException.create(JSErrorType.RangeError, cause.getMessage(), cause, originatingNode);
836841
}
837842

843+
@TruffleBoundary
844+
public static JSException createRangeError(String message, Throwable cause, Node originatingNode) {
845+
return JSException.create(JSErrorType.RangeError, message, cause, originatingNode);
846+
}
847+
838848
@TruffleBoundary
839849
public static JSException createCompileError(String message, Node originatingNode) {
840850
return JSException.create(JSErrorType.CompileError, message, originatingNode);

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/runtime/JSException.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -42,8 +42,8 @@
4242

4343
import com.oracle.truffle.api.CompilerAsserts;
4444
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
45-
import com.oracle.truffle.api.dsl.ImportStatic;
4645
import com.oracle.truffle.api.dsl.Cached.Shared;
46+
import com.oracle.truffle.api.dsl.ImportStatic;
4747
import com.oracle.truffle.api.interop.ArityException;
4848
import com.oracle.truffle.api.interop.ExceptionType;
4949
import com.oracle.truffle.api.interop.InteropLibrary;
@@ -55,10 +55,11 @@
5555
import com.oracle.truffle.api.library.ExportMessage;
5656
import com.oracle.truffle.api.nodes.Node;
5757
import com.oracle.truffle.api.object.DynamicObject;
58+
import com.oracle.truffle.api.object.DynamicObjectLibrary;
5859
import com.oracle.truffle.api.source.SourceSection;
5960
import com.oracle.truffle.js.lang.JavaScriptLanguage;
6061
import com.oracle.truffle.js.runtime.builtins.JSError;
61-
import com.oracle.truffle.js.runtime.objects.JSObject;
62+
import com.oracle.truffle.js.runtime.objects.JSProperty;
6263
import com.oracle.truffle.js.runtime.objects.Undefined;
6364

6465
@ImportStatic({JSConfig.class})
@@ -140,10 +141,15 @@ public static JSException create(JSErrorType type, String message, SourceSection
140141

141142
public static int getStackTraceLimit(JSRealm realm) {
142143
DynamicObject errorConstructor = realm.getErrorConstructor(JSErrorType.Error);
143-
Object stackTraceLimit = JSObject.get(errorConstructor, JSError.STACK_TRACE_LIMIT_PROPERTY_NAME);
144-
final long limit = JSRuntime.toInteger(stackTraceLimit);
145-
final int intLimit = limit > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) limit;
146-
return Math.max(0, intLimit);
144+
DynamicObjectLibrary lib = DynamicObjectLibrary.getUncached();
145+
if (JSProperty.isData(lib.getPropertyFlagsOrDefault(errorConstructor, JSError.STACK_TRACE_LIMIT_PROPERTY_NAME, JSProperty.ACCESSOR))) {
146+
Object stackTraceLimit = lib.getOrDefault(errorConstructor, JSError.STACK_TRACE_LIMIT_PROPERTY_NAME, Undefined.instance);
147+
if (JSRuntime.isNumber(stackTraceLimit)) {
148+
final long limit = JSRuntime.toInteger(stackTraceLimit);
149+
return (int) Math.max(0, Math.min(limit, Integer.MAX_VALUE));
150+
}
151+
}
152+
return 0;
147153
}
148154

149155
@Override

0 commit comments

Comments
 (0)