Commit ba5bac8c authored by Erik Luo's avatar Erik Luo Committed by Commit Bot

[debug] add tests, mark side-effect-free (Typed)Array, WeakMap/Set fns

Adds more whitelisted methods in debug-evaluate for:
Array, TypedArray, ArrayBuffer, DataView, WeakMap, WeakSet

Bug: chromium:810176
Change-Id: I502776ad3191ccc2a355e5684b5f885a5f58d186
Reviewed-on: https://chromium-review.googlesource.com/923414Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Erik Luo <luoe@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51456}
parent d3c883f0
...@@ -273,6 +273,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) { ...@@ -273,6 +273,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
V(ToString) \ V(ToString) \
V(ToLength) \ V(ToLength) \
V(ToNumber) \ V(ToNumber) \
V(ToBigInt) \
V(NumberToStringSkipCache) \ V(NumberToStringSkipCache) \
/* Type checks */ \ /* Type checks */ \
V(IsJSReceiver) \ V(IsJSReceiver) \
...@@ -296,6 +297,8 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) { ...@@ -296,6 +297,8 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
V(TrySliceSimpleNonFastElements) \ V(TrySliceSimpleNonFastElements) \
V(HasComplexElements) \ V(HasComplexElements) \
V(EstimateNumberOfElements) \ V(EstimateNumberOfElements) \
V(NewArray) \
V(TypedArrayGetBuffer) \
/* Errors */ \ /* Errors */ \
V(ReThrow) \ V(ReThrow) \
V(ThrowReferenceError) \ V(ThrowReferenceError) \
...@@ -303,6 +306,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) { ...@@ -303,6 +306,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
V(ThrowIteratorResultNotAnObject) \ V(ThrowIteratorResultNotAnObject) \
V(NewTypeError) \ V(NewTypeError) \
V(ThrowInvalidStringLength) \ V(ThrowInvalidStringLength) \
V(ThrowCalledNonCallable) \
/* Strings */ \ /* Strings */ \
V(StringIndexOf) \ V(StringIndexOf) \
V(StringIncludes) \ V(StringIncludes) \
...@@ -350,6 +354,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) { ...@@ -350,6 +354,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
V(ThrowRangeError) \ V(ThrowRangeError) \
V(ToName) \ V(ToName) \
V(GetOwnPropertyDescriptor) \ V(GetOwnPropertyDescriptor) \
V(HasProperty) \
V(StackGuard) \ V(StackGuard) \
/* Misc. */ \ /* Misc. */ \
V(Call) \ V(Call) \
...@@ -383,6 +388,42 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) { ...@@ -383,6 +388,42 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
#undef INTRINSIC_WHITELIST #undef INTRINSIC_WHITELIST
} }
#ifdef DEBUG
bool BuiltinToIntrinsicHasNoSideEffect(Builtins::Name builtin_id,
Runtime::FunctionId intrinsic_id) {
// First check the intrinsic whitelist.
if (IntrinsicHasNoSideEffect(intrinsic_id)) return true;
// Whitelist intrinsics called from specific builtins.
#define BUILTIN_INTRINSIC_WHITELIST(V, W) \
/* Arrays */ \
V(Builtins::kArrayFilter, W(CreateDataProperty)) \
V(Builtins::kArrayMap, W(CreateDataProperty)) \
V(Builtins::kArrayPrototypeSlice, W(CreateDataProperty) W(SetProperty)) \
/* TypedArrays */ \
V(Builtins::kTypedArrayPrototypeFilter, W(TypedArrayCopyElements)) \
V(Builtins::kTypedArrayPrototypeMap, W(SetProperty))
#define CASE(Builtin, ...) \
case Builtin: \
return (__VA_ARGS__ false);
#define MATCH(Intrinsic) \
intrinsic_id == Runtime::k##Intrinsic || \
intrinsic_id == Runtime::kInline##Intrinsic ||
switch (builtin_id) {
BUILTIN_INTRINSIC_WHITELIST(CASE, MATCH)
default:
return false;
}
#undef MATCH
#undef CASE
#undef BUILTIN_INTRINSIC_WHITELIST
}
#endif // DEBUG
bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) { bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) {
typedef interpreter::Bytecode Bytecode; typedef interpreter::Bytecode Bytecode;
typedef interpreter::Bytecodes Bytecodes; typedef interpreter::Bytecodes Bytecodes;
...@@ -512,6 +553,7 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) { ...@@ -512,6 +553,7 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) {
case Builtins::kObjectPrototypePropertyIsEnumerable: case Builtins::kObjectPrototypePropertyIsEnumerable:
case Builtins::kObjectPrototypeToString: case Builtins::kObjectPrototypeToString:
// Array builtins. // Array builtins.
case Builtins::kArrayIsArray:
case Builtins::kArrayConstructor: case Builtins::kArrayConstructor:
case Builtins::kArrayIndexOf: case Builtins::kArrayIndexOf:
case Builtins::kArrayPrototypeValues: case Builtins::kArrayPrototypeValues:
...@@ -520,11 +562,58 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) { ...@@ -520,11 +562,58 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) {
case Builtins::kArrayPrototypeFind: case Builtins::kArrayPrototypeFind:
case Builtins::kArrayPrototypeFindIndex: case Builtins::kArrayPrototypeFindIndex:
case Builtins::kArrayPrototypeKeys: case Builtins::kArrayPrototypeKeys:
case Builtins::kArrayPrototypeSlice:
case Builtins::kArrayForEach: case Builtins::kArrayForEach:
case Builtins::kArrayEvery: case Builtins::kArrayEvery:
case Builtins::kArraySome: case Builtins::kArraySome:
case Builtins::kArrayConcat:
case Builtins::kArraySlice:
case Builtins::kArrayFilter:
case Builtins::kArrayMap:
case Builtins::kArrayReduce: case Builtins::kArrayReduce:
case Builtins::kArrayReduceRight: case Builtins::kArrayReduceRight:
// TypedArray builtins.
case Builtins::kTypedArrayConstructor:
case Builtins::kTypedArrayPrototypeBuffer:
case Builtins::kTypedArrayPrototypeByteLength:
case Builtins::kTypedArrayPrototypeByteOffset:
case Builtins::kTypedArrayPrototypeLength:
case Builtins::kTypedArrayPrototypeEntries:
case Builtins::kTypedArrayPrototypeKeys:
case Builtins::kTypedArrayPrototypeValues:
case Builtins::kTypedArrayPrototypeFind:
case Builtins::kTypedArrayPrototypeFindIndex:
case Builtins::kTypedArrayPrototypeIncludes:
case Builtins::kTypedArrayPrototypeIndexOf:
case Builtins::kTypedArrayPrototypeLastIndexOf:
case Builtins::kTypedArrayPrototypeSlice:
case Builtins::kTypedArrayPrototypeSubArray:
case Builtins::kTypedArrayPrototypeEvery:
case Builtins::kTypedArrayPrototypeSome:
case Builtins::kTypedArrayPrototypeFilter:
case Builtins::kTypedArrayPrototypeMap:
case Builtins::kTypedArrayPrototypeReduce:
case Builtins::kTypedArrayPrototypeReduceRight:
case Builtins::kTypedArrayPrototypeForEach:
// ArrayBuffer builtins.
case Builtins::kArrayBufferConstructor:
case Builtins::kArrayBufferPrototypeGetByteLength:
case Builtins::kArrayBufferIsView:
case Builtins::kArrayBufferPrototypeSlice:
case Builtins::kReturnReceiver:
// DataView builtins.
case Builtins::kDataViewConstructor:
case Builtins::kDataViewPrototypeGetBuffer:
case Builtins::kDataViewPrototypeGetByteLength:
case Builtins::kDataViewPrototypeGetByteOffset:
case Builtins::kDataViewPrototypeGetInt8:
case Builtins::kDataViewPrototypeGetUint8:
case Builtins::kDataViewPrototypeGetInt16:
case Builtins::kDataViewPrototypeGetUint16:
case Builtins::kDataViewPrototypeGetInt32:
case Builtins::kDataViewPrototypeGetUint32:
case Builtins::kDataViewPrototypeGetFloat32:
case Builtins::kDataViewPrototypeGetFloat64:
// Boolean bulitins. // Boolean bulitins.
case Builtins::kBooleanConstructor: case Builtins::kBooleanConstructor:
case Builtins::kBooleanPrototypeToString: case Builtins::kBooleanPrototypeToString:
...@@ -562,11 +651,17 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) { ...@@ -562,11 +651,17 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) {
case Builtins::kDatePrototypeValueOf: case Builtins::kDatePrototypeValueOf:
// Map builtins. // Map builtins.
case Builtins::kMapConstructor: case Builtins::kMapConstructor:
case Builtins::kMapPrototypeForEach:
case Builtins::kMapPrototypeGet: case Builtins::kMapPrototypeGet:
case Builtins::kMapPrototypeHas:
case Builtins::kMapPrototypeEntries: case Builtins::kMapPrototypeEntries:
case Builtins::kMapPrototypeGetSize: case Builtins::kMapPrototypeGetSize:
case Builtins::kMapPrototypeKeys: case Builtins::kMapPrototypeKeys:
case Builtins::kMapPrototypeValues: case Builtins::kMapPrototypeValues:
// WeakMap builtins.
case Builtins::kWeakMapConstructor:
case Builtins::kWeakMapGet:
case Builtins::kWeakMapHas:
// Math builtins. // Math builtins.
case Builtins::kMathAbs: case Builtins::kMathAbs:
case Builtins::kMathAcos: case Builtins::kMathAcos:
...@@ -619,8 +714,13 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) { ...@@ -619,8 +714,13 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) {
// Set builtins. // Set builtins.
case Builtins::kSetConstructor: case Builtins::kSetConstructor:
case Builtins::kSetPrototypeEntries: case Builtins::kSetPrototypeEntries:
case Builtins::kSetPrototypeForEach:
case Builtins::kSetPrototypeGetSize: case Builtins::kSetPrototypeGetSize:
case Builtins::kSetPrototypeHas:
case Builtins::kSetPrototypeValues: case Builtins::kSetPrototypeValues:
// WeakSet builtins.
case Builtins::kWeakSetConstructor:
case Builtins::kWeakSetHas:
// String builtins. Strings are immutable. // String builtins. Strings are immutable.
case Builtins::kStringFromCharCode: case Builtins::kStringFromCharCode:
case Builtins::kStringFromCodePoint: case Builtins::kStringFromCodePoint:
...@@ -770,7 +870,9 @@ bool DebugEvaluate::FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info) { ...@@ -770,7 +870,9 @@ bool DebugEvaluate::FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info) {
Address address = rinfo->target_external_reference(); Address address = rinfo->target_external_reference();
const Runtime::Function* function = Runtime::FunctionForEntry(address); const Runtime::Function* function = Runtime::FunctionForEntry(address);
if (function == nullptr) continue; if (function == nullptr) continue;
if (!IntrinsicHasNoSideEffect(function->function_id)) { if (!BuiltinToIntrinsicHasNoSideEffect(
static_cast<Builtins::Name>(builtin_index),
function->function_id)) {
PrintF("Whitelisted builtin %s calls non-whitelisted intrinsic %s\n", PrintF("Whitelisted builtin %s calls non-whitelisted intrinsic %s\n",
Builtins::name(builtin_index), function->name); Builtins::name(builtin_index), function->name);
failed = true; failed = true;
......
...@@ -19,6 +19,8 @@ async function async() { ...@@ -19,6 +19,8 @@ async function async() {
var g = generator(); var g = generator();
var p = new Promise(() => {});
function listener(event, exec_state, event_data, data) { function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return; if (event != Debug.DebugEvent.Break) return;
try { try {
...@@ -30,6 +32,15 @@ function listener(event, exec_state, event_data, data) { ...@@ -30,6 +32,15 @@ function listener(event, exec_state, event_data, data) {
fail("generator()"); fail("generator()");
fail("g.next()"); fail("g.next()");
fail("async()"); fail("async()");
fail("Promise.resolve()");
fail("Promise.reject()");
fail("p.then(() => {})");
fail("p.catch(() => {})");
fail("p.finally(() => {})");
fail("Promise.all([p, p])");
fail("Promise.race([p, p])");
fail("(async function() {})()");
fail("(async function() { await 1; })()");
} catch (e) { } catch (e) {
exception = e; exception = e;
print(e, e.stack); print(e, e.stack);
......
...@@ -7,6 +7,10 @@ Debug = debug.Debug ...@@ -7,6 +7,10 @@ Debug = debug.Debug
var exception = null; var exception = null;
var date = new Date(); var date = new Date();
var map = new Map().set("a", "b").set("c", "d"); var map = new Map().set("a", "b").set("c", "d");
var set = new Set([1, 2]);
var weak_key = [];
var weak_map = new WeakMap().set(weak_key, "a").set({}, "b");
var weak_set = new WeakSet([weak_key, {}]);
function listener(event, exec_state, event_data, data) { function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return; if (event != Debug.DebugEvent.Break) return;
...@@ -64,12 +68,46 @@ function listener(event, exec_state, event_data, data) { ...@@ -64,12 +68,46 @@ function listener(event, exec_state, event_data, data) {
success(undefined, `map.entries()`); success(undefined, `map.entries()`);
success(undefined, `map.keys()`); success(undefined, `map.keys()`);
success(undefined, `map.values()`); success(undefined, `map.values()`);
success(undefined, `map.forEach(()=>1)`);
success(true, `map.has("c")`);
success(2, `map.size`); success(2, `map.size`);
fail(`map.has("c")`); // This sets a hash on the object. fail(`new Map([[1, 2]])`);
fail(`map.forEach(()=>1)`);
fail(`map.delete("a")`); fail(`map.delete("a")`);
fail(`map.clear()`); fail(`map.clear()`);
fail(`map.set("x", "y")`); fail(`map.set("x", "y")`);
// Test Set functions.
success(undefined, `new Set()`);
success("[object Set]", `set.toString()`);
success(undefined, `set.entries()`);
success(undefined, `set.keys()`);
success(undefined, `set.values()`);
success(undefined, `set.forEach(()=>1)`);
success(true, `set.has(1)`);
success(2, `set.size`);
fail(`new Set([1])`);
fail(`set.add(2)`);
fail(`set.delete(1)`);
fail(`set.clear()`);
// Test WeakMap functions.
success(undefined, `new WeakMap()`);
success("[object WeakMap]", `weak_map.toString()`);
success("a", `weak_map.get(weak_key)`);
success(true, `weak_map.get([]) === undefined`);
success(true, `weak_map.has(weak_key)`);
fail(`new WeakMap([[[], {}]])`);
fail(`weak_map.delete("a")`);
fail(`weak_map.set("x", "y")`);
// Test WeakSet functions.
success(undefined, `new WeakSet()`);
success("[object WeakSet]", `weak_set.toString()`);
success(true, `weak_set.has(weak_key)`);
fail(`new WeakSet([[], {}])`);
fail(`weak_set.add([])`);
fail(`weak_set.delete("a")`);
} catch (e) { } catch (e) {
exception = e; exception = e;
print(e, e.stack); print(e, e.stack);
......
...@@ -8,6 +8,9 @@ var exception = null; ...@@ -8,6 +8,9 @@ var exception = null;
var object_with_symbol_key = {[Symbol("a")]: 1}; var object_with_symbol_key = {[Symbol("a")]: 1};
var object_with_callbacks = { toString: () => "string", valueOf: () => 3}; var object_with_callbacks = { toString: () => "string", valueOf: () => 3};
var symbol_for_a = Symbol.for("a"); var symbol_for_a = Symbol.for("a");
var typed_array = new Uint8Array([1, 2, 3]);
var array_buffer = new ArrayBuffer(3);
var data_view = new DataView(new ArrayBuffer(8), 0, 8);
function listener(event, exec_state, event_data, data) { function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return; if (event != Debug.DebugEvent.Break) return;
...@@ -57,14 +60,18 @@ function listener(event, exec_state, event_data, data) { ...@@ -57,14 +60,18 @@ function listener(event, exec_state, event_data, data) {
success(3, `(object_with_callbacks).valueOf()`); success(3, `(object_with_callbacks).valueOf()`);
// Test Array functions. // Test Array functions.
success(true, `Array.isArray([1, 2, 3])`);
success([], `new Array()`); success([], `new Array()`);
success([undefined, undefined], `new Array(2)`);
success([1, 2], `new Array(1, 2)`);
fail(`Array.from([1, 2, 3])`);
fail(`Array.of(1, 2, 3)`);
var function_param = [ var function_param = [
"forEach", "every", "some", "reduce", "reduceRight", "find", "filter", "forEach", "every", "some", "reduce", "reduceRight", "find", "filter",
"map", "findIndex" "map", "findIndex"
]; ];
var fails = ["toString", "join", "toLocaleString", "pop", "push", var fails = ["toString", "join", "toLocaleString", "pop", "push", "reverse",
"reverse", "shift", "unshift", "slice", "splice", "sort", "filter", "shift", "unshift", "splice", "sort", "copyWithin", "fill"];
"map", "copyWithin", "fill", "concat"];
for (f of Object.getOwnPropertyNames(Array.prototype)) { for (f of Object.getOwnPropertyNames(Array.prototype)) {
if (typeof Array.prototype[f] === "function") { if (typeof Array.prototype[f] === "function") {
if (fails.includes(f)) { if (fails.includes(f)) {
...@@ -81,6 +88,52 @@ function listener(event, exec_state, event_data, data) { ...@@ -81,6 +88,52 @@ function listener(event, exec_state, event_data, data) {
} }
} }
// Test ArrayBuffer functions.
success(3, `array_buffer.byteLength`);
success(2, `array_buffer.slice(1, 3).byteLength`);
success(true, `ArrayBuffer.isView(typed_array)`);
// Test DataView functions.
success(undefined, `new DataView(array_buffer, 1, 2)`);
success(undefined, `data_view.buffer`);
success(undefined, `data_view.byteLength`);
success(undefined, `data_view.byteOffset`);
for (f of Object.getOwnPropertyNames(DataView.prototype)) {
if (typeof data_view[f] === 'function' && f.startsWith('get'))
success(0, `data_view.${f}()`);
}
// Test TypedArray functions.
success({}, `new Uint8Array()`);
success({0: 0, 1: 0}, `new Uint8Array(2)`);
success({0: 1, 1: 2, 2: 3}, `new Uint8Array(typed_array)`);
success(true, `!!typed_array.buffer`);
success(0, `typed_array.byteOffset`);
success(3, `typed_array.byteLength`);
fail(`Uint8Array.of(1, 2)`);
function_param = [
"forEach", "every", "some", "reduce", "reduceRight", "find", "filter",
"map", "findIndex"
];
fails = ["toString", "join", "toLocaleString", "reverse", "sort",
"copyWithin", "fill", "set"];
var typed_proto_proto = Object.getPrototypeOf(Object.getPrototypeOf(new Uint8Array()));
for (f of Object.getOwnPropertyNames(typed_proto_proto)) {
if (typeof typed_array[f] === "function" && f !== "constructor") {
if (fails.includes(f)) {
if (function_param.includes(f)) {
fail(`typed_array.${f}(()=>{});`);
} else {
fail(`typed_array.${f}();`);
}
} else if (function_param.includes(f)) {
exec_state.frame(0).evaluate(`typed_array.${f}(()=>{});`, true);
} else {
exec_state.frame(0).evaluate(`typed_array.${f}();`, true);
}
}
}
// Test Math functions. // Test Math functions.
for (f of Object.getOwnPropertyNames(Math)) { for (f of Object.getOwnPropertyNames(Math)) {
if (typeof Math[f] === "function") { if (typeof Math[f] === "function") {
...@@ -140,6 +193,12 @@ function listener(event, exec_state, event_data, data) { ...@@ -140,6 +193,12 @@ function listener(event, exec_state, event_data, data) {
fail("'abcd'.search(/a/)"); fail("'abcd'.search(/a/)");
fail("'abcd'.split(/a/)"); fail("'abcd'.split(/a/)");
// Test RegExp functions.
fail(`/a/.compile()`);
fail(`/a/.exec('abc')`);
fail(`/a/.test('abc')`);
fail(`/a/.toString()`);
// Test JSON functions. // Test JSON functions.
success('{"abc":[1,2]}', "JSON.stringify(JSON.parse('{\"abc\":[1,2]}'))"); success('{"abc":[1,2]}', "JSON.stringify(JSON.parse('{\"abc\":[1,2]}'))");
......
...@@ -14,6 +14,7 @@ var string2 = { toString() { print("x"); return "x"; } }; ...@@ -14,6 +14,7 @@ var string2 = { toString() { print("x"); return "x"; } };
var array = [4, 5]; var array = [4, 5];
var error = new Error(); var error = new Error();
function simple_return(x) { return x; }
function set_a() { a = 2; } function set_a() { a = 2; }
function get_a() { return a; } function get_a() { return a; }
var bound = get_a.bind(0); var bound = get_a.bind(0);
...@@ -61,6 +62,15 @@ function listener(event, exec_state, event_data, data) { ...@@ -61,6 +62,15 @@ function listener(event, exec_state, event_data, data) {
success("set_a", "set_a.name"); success("set_a", "set_a.name");
success(0, "bound.length"); success(0, "bound.length");
success("bound get_a", "bound.name"); success("bound get_a", "bound.name");
// Non-evaluated call.
success("abc", "['abc'].join('foo')");
// Constructed literals.
success([1], "[1]");
success({x: 1}, "({x: 1})");
fail("[a]");
fail("({x: a})");
// Test that template literal evaluation fails.
fail("simple_return`1`");
// Test that non-read-only code fails. // Test that non-read-only code fails.
fail("exception = 1"); fail("exception = 1");
// Test that calling a non-read-only function fails. // Test that calling a non-read-only function fails.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment