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) {
V(ToString) \
V(ToLength) \
V(ToNumber) \
V(ToBigInt) \
V(NumberToStringSkipCache) \
/* Type checks */ \
V(IsJSReceiver) \
......@@ -296,6 +297,8 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
V(TrySliceSimpleNonFastElements) \
V(HasComplexElements) \
V(EstimateNumberOfElements) \
V(NewArray) \
V(TypedArrayGetBuffer) \
/* Errors */ \
V(ReThrow) \
V(ThrowReferenceError) \
......@@ -303,6 +306,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
V(ThrowIteratorResultNotAnObject) \
V(NewTypeError) \
V(ThrowInvalidStringLength) \
V(ThrowCalledNonCallable) \
/* Strings */ \
V(StringIndexOf) \
V(StringIncludes) \
......@@ -350,6 +354,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
V(ThrowRangeError) \
V(ToName) \
V(GetOwnPropertyDescriptor) \
V(HasProperty) \
V(StackGuard) \
/* Misc. */ \
V(Call) \
......@@ -383,6 +388,42 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
#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) {
typedef interpreter::Bytecode Bytecode;
typedef interpreter::Bytecodes Bytecodes;
......@@ -512,6 +553,7 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) {
case Builtins::kObjectPrototypePropertyIsEnumerable:
case Builtins::kObjectPrototypeToString:
// Array builtins.
case Builtins::kArrayIsArray:
case Builtins::kArrayConstructor:
case Builtins::kArrayIndexOf:
case Builtins::kArrayPrototypeValues:
......@@ -520,11 +562,58 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) {
case Builtins::kArrayPrototypeFind:
case Builtins::kArrayPrototypeFindIndex:
case Builtins::kArrayPrototypeKeys:
case Builtins::kArrayPrototypeSlice:
case Builtins::kArrayForEach:
case Builtins::kArrayEvery:
case Builtins::kArraySome:
case Builtins::kArrayConcat:
case Builtins::kArraySlice:
case Builtins::kArrayFilter:
case Builtins::kArrayMap:
case Builtins::kArrayReduce:
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.
case Builtins::kBooleanConstructor:
case Builtins::kBooleanPrototypeToString:
......@@ -562,11 +651,17 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) {
case Builtins::kDatePrototypeValueOf:
// Map builtins.
case Builtins::kMapConstructor:
case Builtins::kMapPrototypeForEach:
case Builtins::kMapPrototypeGet:
case Builtins::kMapPrototypeHas:
case Builtins::kMapPrototypeEntries:
case Builtins::kMapPrototypeGetSize:
case Builtins::kMapPrototypeKeys:
case Builtins::kMapPrototypeValues:
// WeakMap builtins.
case Builtins::kWeakMapConstructor:
case Builtins::kWeakMapGet:
case Builtins::kWeakMapHas:
// Math builtins.
case Builtins::kMathAbs:
case Builtins::kMathAcos:
......@@ -619,8 +714,13 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) {
// Set builtins.
case Builtins::kSetConstructor:
case Builtins::kSetPrototypeEntries:
case Builtins::kSetPrototypeForEach:
case Builtins::kSetPrototypeGetSize:
case Builtins::kSetPrototypeHas:
case Builtins::kSetPrototypeValues:
// WeakSet builtins.
case Builtins::kWeakSetConstructor:
case Builtins::kWeakSetHas:
// String builtins. Strings are immutable.
case Builtins::kStringFromCharCode:
case Builtins::kStringFromCodePoint:
......@@ -770,7 +870,9 @@ bool DebugEvaluate::FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info) {
Address address = rinfo->target_external_reference();
const Runtime::Function* function = Runtime::FunctionForEntry(address);
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",
Builtins::name(builtin_index), function->name);
failed = true;
......
......@@ -19,6 +19,8 @@ async function async() {
var g = generator();
var p = new Promise(() => {});
function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return;
try {
......@@ -30,6 +32,15 @@ function listener(event, exec_state, event_data, data) {
fail("generator()");
fail("g.next()");
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) {
exception = e;
print(e, e.stack);
......
......@@ -7,6 +7,10 @@ Debug = debug.Debug
var exception = null;
var date = new Date();
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) {
if (event != Debug.DebugEvent.Break) return;
......@@ -64,12 +68,46 @@ function listener(event, exec_state, event_data, data) {
success(undefined, `map.entries()`);
success(undefined, `map.keys()`);
success(undefined, `map.values()`);
success(undefined, `map.forEach(()=>1)`);
success(true, `map.has("c")`);
success(2, `map.size`);
fail(`map.has("c")`); // This sets a hash on the object.
fail(`map.forEach(()=>1)`);
fail(`new Map([[1, 2]])`);
fail(`map.delete("a")`);
fail(`map.clear()`);
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) {
exception = e;
print(e, e.stack);
......
......@@ -8,6 +8,9 @@ var exception = null;
var object_with_symbol_key = {[Symbol("a")]: 1};
var object_with_callbacks = { toString: () => "string", valueOf: () => 3};
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) {
if (event != Debug.DebugEvent.Break) return;
......@@ -57,14 +60,18 @@ function listener(event, exec_state, event_data, data) {
success(3, `(object_with_callbacks).valueOf()`);
// Test Array functions.
success(true, `Array.isArray([1, 2, 3])`);
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 = [
"forEach", "every", "some", "reduce", "reduceRight", "find", "filter",
"map", "findIndex"
];
var fails = ["toString", "join", "toLocaleString", "pop", "push",
"reverse", "shift", "unshift", "slice", "splice", "sort", "filter",
"map", "copyWithin", "fill", "concat"];
var fails = ["toString", "join", "toLocaleString", "pop", "push", "reverse",
"shift", "unshift", "splice", "sort", "copyWithin", "fill"];
for (f of Object.getOwnPropertyNames(Array.prototype)) {
if (typeof Array.prototype[f] === "function") {
if (fails.includes(f)) {
......@@ -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.
for (f of Object.getOwnPropertyNames(Math)) {
if (typeof Math[f] === "function") {
......@@ -140,6 +193,12 @@ function listener(event, exec_state, event_data, data) {
fail("'abcd'.search(/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.
success('{"abc":[1,2]}', "JSON.stringify(JSON.parse('{\"abc\":[1,2]}'))");
......
......@@ -14,6 +14,7 @@ var string2 = { toString() { print("x"); return "x"; } };
var array = [4, 5];
var error = new Error();
function simple_return(x) { return x; }
function set_a() { a = 2; }
function get_a() { return a; }
var bound = get_a.bind(0);
......@@ -61,6 +62,15 @@ function listener(event, exec_state, event_data, data) {
success("set_a", "set_a.name");
success(0, "bound.length");
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.
fail("exception = 1");
// 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