Chromium Code Reviews| Index: tests/lib/mirrors/invoke_natives_fuzz_test.dart |
| diff --git a/tests/lib/mirrors/invoke_natives_fuzz_test.dart b/tests/lib/mirrors/invoke_natives_fuzz_test.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f9967eeffe2ea0d8ff438f705648019aed469898 |
| --- /dev/null |
| +++ b/tests/lib/mirrors/invoke_natives_fuzz_test.dart |
| @@ -0,0 +1,135 @@ |
| +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +// This test reflectively enumerates all the methods in the system and tries to |
| +// invoke them will all nulls. This may result in Dart exceptions or hangs, but |
| +// should never result in crashes or JavaScript exceptions. |
| + |
| +library test.invoke_natives; |
| + |
| +import 'dart:mirrors'; |
| +import 'dart:async'; |
| +import 'package:expect/expect.dart'; |
| + |
| +// Methods to be skipped, by qualified name. |
| +var blacklist = [ |
| + // These prevent the test from exiting, typically by spawning another isolate. |
| + 'dart.async._scheduleAsyncCallback', |
| + 'dart.io._IOService.dispatch', |
| + 'dart.isolate.RawReceivePort.RawReceivePort', |
| + 'dart.isolate.ReceivePort.ReceivePort', |
| + 'dart.isolate.ReceivePort.ReceivePort.fromRawReceivePort', |
| + 'dart.isolate.ReceivePort.sendPort', |
| + 'dart.isolate.ReceivePort.close', |
| + 'dart.isolate.ReceivePort.listen', |
| + 'dart.isolate.RawReceivePort.sendPort', |
| + 'dart.isolate.RawReceivePort.close', |
| + 'dart.isolate.RawReceivePort.handler=', |
| + |
| + // These "crash" the VM (throw uncatchable API errors). |
| + // TODO(15274): Fill in this list to make the test pass and provide coverage |
| + // against addition of new natives. |
| +]; |
| + |
| +class Task { |
| + var name; |
| + var action; |
| +} |
| +var queue = new List(); |
| + |
| +checkMethod(MethodMirror m, ObjectMirror target, [origin]) { |
| + if (blacklist.contains(MirrorSystem.getName(m.qualifiedName))) return; |
| + |
| + var task = new Task(); |
| + task.name = '${MirrorSystem.getName(m.qualifiedName)} from $origin'; |
| + |
| + if (m.isRegularMethod) { |
| + task.action = |
| + () => target.invoke(m.simpleName, new List(m.parameters.length)); |
| + } else if (m.isGetter) { |
| + task.action = |
| + () => target.getField(m.simpleName); |
| + } else if (m.isSetter) { |
| + task.action = |
| + () => target.setField(m.simpleName, null); |
| + } else if (m.isConstructor) { |
| + return; |
| + } else { |
| + throw "Unexpected method kind"; |
| + } |
| + |
| + queue.add(task); |
| +} |
| + |
| +checkInstance(instanceMirror, origin) { |
| + instanceMirror.type.declarations.values |
| + .where((d) => d is MethodMirror) |
| + .forEach((m) => checkMethod(m, instanceMirror, origin)); |
| +} |
| + |
| +checkClass(classMirror) { |
| + classMirror.declarations.values |
| + .where((d) => d is MethodMirror) |
| + .forEach((m) => checkMethod(m, classMirror)); |
| + |
| + classMirror.declarations.values |
| + .where((d) => d is MethodMirror) |
| + .forEach((m) { |
| + if (blacklist.contains(MirrorSystem.getName(m.qualifiedName))) return; |
| + if (!m.isConstructor) return; |
| + var task = new Task(); |
| + task.name = MirrorSystem.getName(m.qualifiedName); |
| + |
| + task.action = () { |
| + var instance = classMirror.newInstance(m.constructorName, |
| + new List(m.parameters.length)); |
|
gbracha
2014/04/17 21:25:28
So if the constructor fails, no instance methods w
rmacnak
2014/04/17 21:47:19
Right, in the absence of primitiveExecuteMethod we
|
| + checkInstance(instance, task.name); |
| + }; |
| + queue.add(task); |
| + }); |
| +} |
| + |
| +checkLibrary(libraryMirror) { |
| + // Don't recurse on this test. |
| + if (libraryMirror.simpleName == #test.invoke_natives) return; |
| + |
| + libraryMirror.declarations.values |
| + .where((d) => d is ClassMirror) |
| + .forEach(checkClass); |
| + |
| + libraryMirror.declarations.values |
| + .where((d) => d is MethodMirror) |
| + .forEach((m) => checkMethod(m, libraryMirror)); |
| +} |
| + |
| +var testZone; |
| +var debug = true; |
| + |
| +doOneTask() { |
| + if (queue.length == 0) { |
| + if (debug) print('Done'); |
| + return; |
| + } |
| + |
| + var task = queue.removeLast(); |
| + if (debug) print(task.name); |
| + try { |
| + task.action(); |
| + } catch(e) {} |
| + // Register the next task in a timer callback so as to yield to async code |
| + // scheduled in the current task. This isn't necessary for the test itself, |
| + // but is helpful when trying to figure out which function is responsible for |
| + // a crash. |
| + testZone.createTimer(Duration.ZERO, doOneTask); |
| +} |
| + |
| +main() { |
| + currentMirrorSystem().libraries.values.forEach(checkLibrary); |
| + |
| + uncaughtErrorHandler(self, parent, zone, error, stack) {}; |
| + var zoneSpec = |
| + new ZoneSpecification(handleUncaughtError: uncaughtErrorHandler); |
| + testZone = Zone.current.fork(specification: zoneSpec); |
| + testZone.createTimer(Duration.ZERO, doOneTask); |
| +} |