mirror of
https://github.com/dart-lang/sdk
synced 2024-09-20 01:38:14 +00:00
Make print interceptable.
BUG= http://dartbug.com/3486 R=lrn@google.com Review URL: https://codereview.chromium.org//27112002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@28650 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
69872a6b28
commit
10e9a799d1
|
@ -660,13 +660,13 @@ Dart_Handle DartUtils::LoadSource(CommandLineOptions* url_mapping,
|
|||
|
||||
Dart_Handle DartUtils::PrepareForScriptLoading(const char* package_root,
|
||||
Dart_Handle builtin_lib) {
|
||||
Dart_Handle corelib = Dart_LookupLibrary(NewString("dart:core"));
|
||||
DART_CHECK_VALID(corelib);
|
||||
|
||||
// Setup the corelib 'print' function.
|
||||
// Setup the internal library's 'internalPrint' function.
|
||||
Dart_Handle internal_lib =
|
||||
Dart_LookupLibrary(NewString("dart:_collection-dev"));
|
||||
DART_CHECK_VALID(internal_lib);
|
||||
Dart_Handle print = Dart_Invoke(
|
||||
builtin_lib, NewString("_getPrintClosure"), 0, NULL);
|
||||
Dart_Handle result = Dart_SetField(corelib,
|
||||
Dart_Handle result = Dart_SetField(internal_lib,
|
||||
NewString("_printClosure"),
|
||||
print);
|
||||
DART_CHECK_VALID(result);
|
||||
|
@ -685,6 +685,8 @@ Dart_Handle DartUtils::PrepareForScriptLoading(const char* package_root,
|
|||
async_lib, NewString("_setTimerFactoryClosure"), 1, args));
|
||||
|
||||
// Setup the corelib 'Uri.base' getter.
|
||||
Dart_Handle corelib = Dart_LookupLibrary(NewString("dart:core"));
|
||||
DART_CHECK_VALID(corelib);
|
||||
Dart_Handle uri_base = Dart_Invoke(
|
||||
builtin_lib, NewString("_getUriBaseClosure"), 0, NULL);
|
||||
DART_CHECK_VALID(uri_base);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
'sources': [
|
||||
'collection_dev_patch.dart',
|
||||
# The above file needs to be first as it imports required libraries.
|
||||
'print_patch.dart',
|
||||
'symbol_patch.dart',
|
||||
],
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
'null_patch.dart',
|
||||
'object.cc',
|
||||
'object_patch.dart',
|
||||
'print_patch.dart',
|
||||
'regexp.cc',
|
||||
'regexp_jsc.cc',
|
||||
'regexp_jsc.h',
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
typedef void _PrintClosure(Object obj);
|
||||
|
||||
patch void print(Object obj) {
|
||||
_printClosure(obj.toString());
|
||||
patch void printToConsole(String line) {
|
||||
_printClosure(line);
|
||||
}
|
||||
|
||||
void _unsupportedPrint(Object obj) {
|
||||
|
|
|
@ -14,6 +14,6 @@ import 'dart:collection' show HashSet;
|
|||
part 'arrays.dart';
|
||||
part 'iterable.dart';
|
||||
part 'list.dart';
|
||||
part 'print.dart';
|
||||
part 'sort.dart';
|
||||
part 'symbol.dart';
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
'arrays.dart',
|
||||
'iterable.dart',
|
||||
'list.dart',
|
||||
'print.dart',
|
||||
'sort.dart',
|
||||
'symbol.dart',
|
||||
],
|
||||
|
|
17
sdk/lib/_collection_dev/print.dart
Normal file
17
sdk/lib/_collection_dev/print.dart
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2013, 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.
|
||||
|
||||
part of dart._collection.dev;
|
||||
|
||||
/**
|
||||
* This function is set by the first allocation of a Zone.
|
||||
*
|
||||
* Once the function is set the core `print` function calls this closure instead
|
||||
* of [printToConsole].
|
||||
*
|
||||
* This decouples the core library from the async library.
|
||||
*/
|
||||
Function printToZone = null;
|
||||
|
||||
external void printToConsole(String line);
|
|
@ -2,7 +2,45 @@
|
|||
// 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.
|
||||
|
||||
import 'dart:_foreign_helper' show JS;
|
||||
|
||||
patch class Symbol implements core.Symbol {
|
||||
patch const Symbol(String name)
|
||||
: this._name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the low-level method that is used to implement
|
||||
* [print]. It is possible to override this function from JavaScript
|
||||
* by defining a function in JavaScript called "dartPrint".
|
||||
*/
|
||||
patch void printToConsole(String line) {
|
||||
if (JS('bool', r'typeof dartPrint == "function"')) {
|
||||
// Support overriding print from JavaScript.
|
||||
JS('void', r'dartPrint(#)', line);
|
||||
return;
|
||||
}
|
||||
|
||||
// Inside browser or nodejs.
|
||||
if (JS('bool', r'typeof console == "object"') &&
|
||||
JS('bool', r'typeof console.log == "function"')) {
|
||||
JS('void', r'console.log(#)', line);
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't throw inside IE, the console is only defined if dev tools is open.
|
||||
if (JS('bool', r'typeof window == "object"')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Running in d8, the V8 developer shell, or in Firefox' js-shell.
|
||||
if (JS('bool', r'typeof print == "function"')) {
|
||||
JS('void', r'print(#)', line);
|
||||
return;
|
||||
}
|
||||
|
||||
// This is somewhat nasty, but we don't want to drag in a bunch of
|
||||
// dependencies to handle a situation that cannot happen. So we
|
||||
// avoid using Dart [:throw:] and Dart [toString].
|
||||
JS('void', 'throw "Unable to print message: " + String(#)', line);
|
||||
}
|
||||
|
|
|
@ -24,10 +24,6 @@ _symbolMapToStringMap(Map<Symbol, dynamic> map) {
|
|||
return result;
|
||||
}
|
||||
|
||||
patch void print(Object object) {
|
||||
Primitives.printString(object.toString());
|
||||
}
|
||||
|
||||
patch int identityHashCode(Object object) => objectHashCode(object);
|
||||
|
||||
// Patch for Object implementation.
|
||||
|
|
|
@ -281,42 +281,6 @@ class Primitives {
|
|||
|
||||
static computeGlobalThis() => JS('', 'function() { return this; }()');
|
||||
|
||||
/**
|
||||
* This is the low-level method that is used to implement
|
||||
* [print]. It is possible to override this function from JavaScript
|
||||
* by defining a function in JavaScript called "dartPrint".
|
||||
*/
|
||||
static void printString(String string) {
|
||||
if (JS('bool', r'typeof dartPrint == "function"')) {
|
||||
// Support overriding print from JavaScript.
|
||||
JS('void', r'dartPrint(#)', string);
|
||||
return;
|
||||
}
|
||||
|
||||
// Inside browser or nodejs.
|
||||
if (JS('bool', r'typeof console == "object"') &&
|
||||
JS('bool', r'typeof console.log == "function"')) {
|
||||
JS('void', r'console.log(#)', string);
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't throw inside IE, the console is only defined if dev tools is open.
|
||||
if (JS('bool', r'typeof window == "object"')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Running in d8, the V8 developer shell, or in Firefox' js-shell.
|
||||
if (JS('bool', r'typeof print == "function"')) {
|
||||
JS('void', r'print(#)', string);
|
||||
return;
|
||||
}
|
||||
|
||||
// This is somewhat nasty, but we don't want to drag in a bunch of
|
||||
// dependencies to handle a situation that cannot happen. So we
|
||||
// avoid using Dart [:throw:] and Dart [toString].
|
||||
JS('void', 'throw "Unable to print message: " + String(#)', string);
|
||||
}
|
||||
|
||||
static _throwFormatException(String string) {
|
||||
throw new FormatException(string);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
library dart.async;
|
||||
|
||||
import "dart:collection";
|
||||
import "dart:_collection-dev" show deprecated;
|
||||
import "dart:_collection-dev" show deprecated, printToZone, printToConsole;
|
||||
|
||||
part 'async_error.dart';
|
||||
part 'broadcast_stream_controller.dart';
|
||||
|
|
|
@ -31,6 +31,8 @@ typedef Timer CreateTimerHandler(
|
|||
typedef Timer CreatePeriodicTimerHandler(
|
||||
Zone self, ZoneDelegate parent, Zone zone,
|
||||
Duration period, void f(Timer timer));
|
||||
typedef void PrintHandler(
|
||||
Zone self, ZoneDelegate parent, Zone zone, String line);
|
||||
typedef Zone ForkHandler(Zone self, ZoneDelegate parent, Zone zone,
|
||||
ZoneSpecification specification,
|
||||
Map<Symbol, dynamic> zoneValues);
|
||||
|
@ -82,6 +84,7 @@ abstract class ZoneSpecification {
|
|||
Duration duration, void f()): null,
|
||||
Timer createPeriodicTimer(Zone self, ZoneDelegate parent, Zone zone,
|
||||
Duration period, void f(Timer timer)): null,
|
||||
void print(Zone self, ZoneDelegate parent, Zone zone, String line): null,
|
||||
Zone fork(Zone self, ZoneDelegate parent, Zone zone,
|
||||
ZoneSpecification specification, Map zoneValues): null
|
||||
}) = _ZoneSpecification;
|
||||
|
@ -112,6 +115,7 @@ abstract class ZoneSpecification {
|
|||
Duration duration, void f()): null,
|
||||
Timer createPeriodicTimer(Zone self, ZoneDelegate parent, Zone zone,
|
||||
Duration period, void f(Timer timer)): null,
|
||||
void print(Zone self, ZoneDelegate parent, Zone zone, String line): null,
|
||||
Zone fork(Zone self, ZoneDelegate parent, Zone zone,
|
||||
ZoneSpecification specification,
|
||||
Map<Symbol, dynamic> zoneValues): null
|
||||
|
@ -141,6 +145,7 @@ abstract class ZoneSpecification {
|
|||
createPeriodicTimer: createPeriodicTimer != null
|
||||
? createPeriodicTimer
|
||||
: other.createPeriodicTimer,
|
||||
print : print != null ? print : other.print,
|
||||
fork: fork != null ? fork : other.fork);
|
||||
}
|
||||
|
||||
|
@ -156,6 +161,7 @@ abstract class ZoneSpecification {
|
|||
RunAsyncHandler get runAsync;
|
||||
CreateTimerHandler get createTimer;
|
||||
CreatePeriodicTimerHandler get createPeriodicTimer;
|
||||
PrintHandler get print;
|
||||
ForkHandler get fork;
|
||||
}
|
||||
|
||||
|
@ -179,6 +185,7 @@ class _ZoneSpecification implements ZoneSpecification {
|
|||
this.runAsync: null,
|
||||
this.createTimer: null,
|
||||
this.createPeriodicTimer: null,
|
||||
this.print: null,
|
||||
this.fork: null
|
||||
});
|
||||
|
||||
|
@ -195,6 +202,7 @@ class _ZoneSpecification implements ZoneSpecification {
|
|||
final /*RunAsyncHandler*/ runAsync;
|
||||
final /*CreateTimerHandler*/ createTimer;
|
||||
final /*CreatePeriodicTimerHandler*/ createPeriodicTimer;
|
||||
final /*PrintHandler*/ print;
|
||||
final /*ForkHandler*/ fork;
|
||||
}
|
||||
|
||||
|
@ -224,6 +232,7 @@ abstract class ZoneDelegate {
|
|||
void scheduleMicrotask(Zone zone, f());
|
||||
Timer createTimer(Zone zone, Duration duration, void f());
|
||||
Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer));
|
||||
void print(Zone zone, String line);
|
||||
Zone fork(Zone zone, ZoneSpecification specification, Map zoneValues);
|
||||
}
|
||||
|
||||
|
@ -385,6 +394,11 @@ abstract class Zone {
|
|||
*/
|
||||
Timer createPeriodicTimer(Duration period, void callback(Timer timer));
|
||||
|
||||
/**
|
||||
* Prints the given [line].
|
||||
*/
|
||||
void print(String line);
|
||||
|
||||
/**
|
||||
* The error zone is the one that is responsible for dealing with uncaught
|
||||
* errors. Errors are not allowed to cross zones with different error-zones.
|
||||
|
@ -507,6 +521,15 @@ class _ZoneDelegate implements ZoneDelegate {
|
|||
parent, new _ZoneDelegate(parent.parent), zone, period, f);
|
||||
}
|
||||
|
||||
void print(Zone zone, String line) {
|
||||
_CustomizedZone parent = _degelationTarget;
|
||||
while (parent._specification.print == null) {
|
||||
parent = parent.parent;
|
||||
}
|
||||
(parent._specification.print)(
|
||||
parent, new _ZoneDelegate(parent.parent), zone, line);
|
||||
}
|
||||
|
||||
Zone fork(Zone zone, ZoneSpecification specification,
|
||||
Map<Symbol, dynamic> zoneValues) {
|
||||
_BaseZone parent = _degelationTarget;
|
||||
|
@ -670,6 +693,10 @@ class _CustomizedZone extends _BaseZone {
|
|||
Timer createPeriodicTimer(Duration duration, void f(Timer timer)) {
|
||||
return new _ZoneDelegate(this).createPeriodicTimer(this, duration, f);
|
||||
}
|
||||
|
||||
void print(String line) {
|
||||
new _ZoneDelegate(this).print(this, line);
|
||||
}
|
||||
}
|
||||
|
||||
void _rootHandleUncaughtError(
|
||||
|
@ -754,9 +781,22 @@ Timer _rootCreatePeriodicTimer(
|
|||
return _createPeriodicTimer(duration, callback);
|
||||
}
|
||||
|
||||
void _rootPrint(Zone self, ZoneDelegate parent, Zone zone, String line) {
|
||||
printToConsole(line);
|
||||
}
|
||||
|
||||
void _printToZone(String line) {
|
||||
Zone.current.print(line);
|
||||
}
|
||||
|
||||
Zone _rootFork(Zone self, ZoneDelegate parent, Zone zone,
|
||||
ZoneSpecification specification,
|
||||
Map<Symbol, dynamic> zoneValues) {
|
||||
// TODO(floitsch): it would be nice if we could get rid of this hack.
|
||||
// Change the static zoneOrDirectPrint function to go through zones
|
||||
// from now on.
|
||||
printToZone = _printToZone;
|
||||
|
||||
if (specification == null) {
|
||||
specification = const ZoneSpecification();
|
||||
} else if (specification is! _ZoneSpecification) {
|
||||
|
@ -794,6 +834,7 @@ class _RootZoneSpecification implements ZoneSpecification {
|
|||
CreateTimerHandler get createTimer => _rootCreateTimer;
|
||||
CreatePeriodicTimerHandler get createPeriodicTimer =>
|
||||
_rootCreatePeriodicTimer;
|
||||
PrintHandler get print => _rootPrint;
|
||||
ForkHandler get fork => _rootFork;
|
||||
}
|
||||
|
||||
|
@ -846,6 +887,8 @@ class _RootZone extends _BaseZone {
|
|||
|
||||
Timer createPeriodicTimer(Duration duration, void f(Timer timer)) =>
|
||||
_rootCreatePeriodicTimer(this, null, this, duration, f);
|
||||
|
||||
void print(String line) => _rootPrint(this, null, this, line);
|
||||
}
|
||||
|
||||
const _ROOT_ZONE = const _RootZone();
|
||||
|
|
|
@ -4,4 +4,11 @@
|
|||
|
||||
part of dart.core;
|
||||
|
||||
external void print(Object object);
|
||||
void print(Object object) {
|
||||
String line = object.toString();
|
||||
if (printToZone == null) {
|
||||
printToConsole(line);
|
||||
} else {
|
||||
printToZone(line);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ void main() {
|
|||
// 2. Some code was refactored, and there are more methods.
|
||||
// Either situation could be problematic, but in situation 2, it is often
|
||||
// acceptable to increase [expectedMethodCount] a little.
|
||||
int expectedMethodCount = 326;
|
||||
int expectedMethodCount = 327;
|
||||
Expect.isTrue(
|
||||
generatedCode.length <= expectedMethodCount,
|
||||
'Too many compiled methods: '
|
||||
|
|
44
tests/lib/async/intercept_print1_test.dart
Normal file
44
tests/lib/async/intercept_print1_test.dart
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) 2013, 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.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
import 'dart:async';
|
||||
import 'catch_errors.dart';
|
||||
|
||||
var events = [];
|
||||
|
||||
void printHandler1(Zone self, ZoneDelegate parent, Zone origin, String line) {
|
||||
events.add("print: $line");
|
||||
}
|
||||
|
||||
bool shouldIntercept = true;
|
||||
|
||||
void printHandler2(Zone self, ZoneDelegate parent, Zone origin, String line) {
|
||||
if (shouldIntercept) {
|
||||
events.add("print **: $line");
|
||||
} else {
|
||||
parent.print(origin, line);
|
||||
}
|
||||
}
|
||||
|
||||
const TEST_SPEC1 = const ZoneSpecification(print: printHandler1);
|
||||
const TEST_SPEC2 = const ZoneSpecification(print: printHandler2);
|
||||
|
||||
main() {
|
||||
Zone zone1 = Zone.current.fork(specification: TEST_SPEC1);
|
||||
Zone zone2 = zone1.fork(specification: TEST_SPEC2);
|
||||
zone1.run(() {
|
||||
print("1");
|
||||
print(2);
|
||||
print({3: [4]});
|
||||
});
|
||||
zone2.run(() {
|
||||
print("5");
|
||||
shouldIntercept = false;
|
||||
print(6);
|
||||
});
|
||||
Expect.listEquals(
|
||||
["print: 1", "print: 2", "print: {3: [4]}", "print **: 5", "print: 6"],
|
||||
events);
|
||||
}
|
Loading…
Reference in a new issue