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:
floitsch@google.com 2013-10-15 16:02:07 +00:00
parent 69872a6b28
commit 10e9a799d1
15 changed files with 164 additions and 52 deletions

View file

@ -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);

View file

@ -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',
],
}

View file

@ -38,7 +38,6 @@
'null_patch.dart',
'object.cc',
'object_patch.dart',
'print_patch.dart',
'regexp.cc',
'regexp_jsc.cc',
'regexp_jsc.h',

View file

@ -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) {

View file

@ -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';

View file

@ -10,6 +10,7 @@
'arrays.dart',
'iterable.dart',
'list.dart',
'print.dart',
'sort.dart',
'symbol.dart',
],

View 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);

View file

@ -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);
}

View file

@ -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.

View file

@ -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);
}

View file

@ -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';

View file

@ -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();

View file

@ -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);
}
}

View file

@ -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: '

View 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);
}