diff --git a/pkg/front_end/testcases/general/sdk_diagnostic.dart.strong.expect b/pkg/front_end/testcases/general/sdk_diagnostic.dart.strong.expect index 3885c63c3aa..aeb6d627443 100644 --- a/pkg/front_end/testcases/general/sdk_diagnostic.dart.strong.expect +++ b/pkg/front_end/testcases/general/sdk_diagnostic.dart.strong.expect @@ -19,7 +19,7 @@ library; // pkg/front_end/testcases/general/sdk_diagnostic.dart:12:8: Error: Too few positional arguments: 1 required, 0 given. // print(incorrectArgument: "fisk"); // ^ -// sdk/lib/core/print.dart:8:6: Context: Found this candidate, but the arguments don't match. +// sdk/lib/core/print.dart:19:6: Context: Found this candidate, but the arguments don't match. // void print(Object? object) { // ^^^^^ // diff --git a/pkg/front_end/testcases/general/sdk_diagnostic.dart.strong.transformed.expect b/pkg/front_end/testcases/general/sdk_diagnostic.dart.strong.transformed.expect index 3885c63c3aa..aeb6d627443 100644 --- a/pkg/front_end/testcases/general/sdk_diagnostic.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/general/sdk_diagnostic.dart.strong.transformed.expect @@ -19,7 +19,7 @@ library; // pkg/front_end/testcases/general/sdk_diagnostic.dart:12:8: Error: Too few positional arguments: 1 required, 0 given. // print(incorrectArgument: "fisk"); // ^ -// sdk/lib/core/print.dart:8:6: Context: Found this candidate, but the arguments don't match. +// sdk/lib/core/print.dart:19:6: Context: Found this candidate, but the arguments don't match. // void print(Object? object) { // ^^^^^ // diff --git a/pkg/front_end/testcases/general/sdk_diagnostic.dart.weak.expect b/pkg/front_end/testcases/general/sdk_diagnostic.dart.weak.expect index 3885c63c3aa..aeb6d627443 100644 --- a/pkg/front_end/testcases/general/sdk_diagnostic.dart.weak.expect +++ b/pkg/front_end/testcases/general/sdk_diagnostic.dart.weak.expect @@ -19,7 +19,7 @@ library; // pkg/front_end/testcases/general/sdk_diagnostic.dart:12:8: Error: Too few positional arguments: 1 required, 0 given. // print(incorrectArgument: "fisk"); // ^ -// sdk/lib/core/print.dart:8:6: Context: Found this candidate, but the arguments don't match. +// sdk/lib/core/print.dart:19:6: Context: Found this candidate, but the arguments don't match. // void print(Object? object) { // ^^^^^ // diff --git a/pkg/front_end/testcases/general/sdk_diagnostic.dart.weak.modular.expect b/pkg/front_end/testcases/general/sdk_diagnostic.dart.weak.modular.expect index 3885c63c3aa..aeb6d627443 100644 --- a/pkg/front_end/testcases/general/sdk_diagnostic.dart.weak.modular.expect +++ b/pkg/front_end/testcases/general/sdk_diagnostic.dart.weak.modular.expect @@ -19,7 +19,7 @@ library; // pkg/front_end/testcases/general/sdk_diagnostic.dart:12:8: Error: Too few positional arguments: 1 required, 0 given. // print(incorrectArgument: "fisk"); // ^ -// sdk/lib/core/print.dart:8:6: Context: Found this candidate, but the arguments don't match. +// sdk/lib/core/print.dart:19:6: Context: Found this candidate, but the arguments don't match. // void print(Object? object) { // ^^^^^ // diff --git a/pkg/front_end/testcases/general/sdk_diagnostic.dart.weak.transformed.expect b/pkg/front_end/testcases/general/sdk_diagnostic.dart.weak.transformed.expect index 3885c63c3aa..aeb6d627443 100644 --- a/pkg/front_end/testcases/general/sdk_diagnostic.dart.weak.transformed.expect +++ b/pkg/front_end/testcases/general/sdk_diagnostic.dart.weak.transformed.expect @@ -19,7 +19,7 @@ library; // pkg/front_end/testcases/general/sdk_diagnostic.dart:12:8: Error: Too few positional arguments: 1 required, 0 given. // print(incorrectArgument: "fisk"); // ^ -// sdk/lib/core/print.dart:8:6: Context: Found this candidate, but the arguments don't match. +// sdk/lib/core/print.dart:19:6: Context: Found this candidate, but the arguments don't match. // void print(Object? object) { // ^^^^^ // diff --git a/pkg/vm_service/test/break_on_dart_colon_test.dart b/pkg/vm_service/test/break_on_dart_colon_test.dart index a70e98aa309..846438ce2fb 100644 --- a/pkg/vm_service/test/break_on_dart_colon_test.dart +++ b/pkg/vm_service/test/break_on_dart_colon_test.dart @@ -11,7 +11,7 @@ import 'common/service_test_common.dart'; import 'common/test_helper.dart'; // Line in core/print.dart -const int LINE_A = 10; +const int LINE_A = 19; testMain() { debugger(); diff --git a/runtime/observatory/tests/service/break_on_dart_colon_test.dart b/runtime/observatory/tests/service/break_on_dart_colon_test.dart index f9b49083f48..891396f3d33 100644 --- a/runtime/observatory/tests/service/break_on_dart_colon_test.dart +++ b/runtime/observatory/tests/service/break_on_dart_colon_test.dart @@ -11,7 +11,7 @@ import 'service_test_common.dart'; import 'test_helper.dart'; // Line in core/print.dart -const int LINE_A = 10; +const int LINE_A = 19; testMain() { debugger(); diff --git a/sdk/lib/core/print.dart b/sdk/lib/core/print.dart index 10a02634137..070174daa54 100644 --- a/sdk/lib/core/print.dart +++ b/sdk/lib/core/print.dart @@ -4,7 +4,18 @@ part of dart.core; -/// Prints a string representation of the object to the console. +/// Prints an object to the console. +/// +/// On the web, `object` is converted to a string and that string is output to +/// the web console using `console.log`. +/// +/// On native (non-Web) platforms, `object` is converted to a string and that +/// string is terminated by a line feed (`'\n'`, U+000A) and written to +/// `stdout`. On Windows, the terminating line feed, and any line feeds in the +/// string representation of `object`, are output using the Windows line +/// terminator sequence of (`'\r\n'`, U+000D + U+000A). +/// +/// Calls to `print` can be intercepted by [Zone.print]. void print(Object? object) { String line = "$object"; var toZone = printToZone; diff --git a/tests/standalone/io/print_test.dart b/tests/standalone/io/print_test.dart new file mode 100644 index 00000000000..40a5715154b --- /dev/null +++ b/tests/standalone/io/print_test.dart @@ -0,0 +1,74 @@ +// Copyright (c) 2023, 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. + +// OtherResources=print_test_script.dart + +/// Tests the `dart:core` `print` function. +/// +/// The actual print code is in "print_test_script.dart" and the output is +/// validated in this test. + +import 'dart:io'; + +import "package:expect/expect.dart"; + +final nl = Platform.isWindows ? [13, 10] : [10]; + +/// Execute "print_test_script.dart" with `command` as an argument and return +/// the commands stdout as a list of bytes. +List runTest(String command) { + final result = Process.runSync( + Platform.executable, + [] + ..addAll(Platform.executableArguments) + ..add('--verbosity=warning') + ..add(Platform.script.resolve('print_test_script.dart').toFilePath()) + ..add(command), + stdoutEncoding: null); + + if (result.exitCode != 0) { + throw AssertionError( + 'unexpected exit code for command $command: ${result.stderr}'); + } + return result.stdout; +} + +void testSimpleString() { + // "Hello World!" + final expected = [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, ...nl]; + Expect.listEquals(expected, runTest("simple-string")); +} + +void testStringInternalLineEnding() { + // "l1\nl2\nl3" + final expected = [108, 49, ...nl, 108, 50, ...nl, 108, 51, ...nl]; + Expect.listEquals(expected, runTest("string-internal-linefeeds")); +} + +void testStringCarriageReturns() { + // "l1\rl2\rl3\r" + final expected = [108, 49, 13, 108, 50, 13, 108, 51, 13, ...nl]; + Expect.listEquals(expected, runTest("string-internal-carriagereturns")); +} + +void testStringCarriageReturnLinefeeds() { + // ""l1\r\nl2\r\nl3\r\n"" + // Notice on Windows this will result in `\r\n` => `\r\r\n' + final expected = [108, 49, 13, ...nl, 108, 50, 13, ...nl, 108, 51, 13, ...nl]; + Expect.listEquals( + expected, runTest("string-internal-carriagereturn-linefeeds")); +} + +void testObjectInternalLineEnding() { + // Object.toString() => "l1\nl2\nl3" + final expected = [108, 49, ...nl, 108, 50, ...nl, 108, 51, ...nl]; + Expect.listEquals(expected, runTest("object-internal-linefeeds")); +} + +void main() { + testSimpleString(); + testStringInternalLineEnding(); + testStringCarriageReturns(); + testObjectInternalLineEnding(); +} diff --git a/tests/standalone/io/print_test_script.dart b/tests/standalone/io/print_test_script.dart new file mode 100644 index 00000000000..9cc30f6bf4d --- /dev/null +++ b/tests/standalone/io/print_test_script.dart @@ -0,0 +1,39 @@ +// Copyright (c) 2023, 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 is a companion script to print_test.dart. + +import 'dart:io'; + +class ToString { + String _toString; + + ToString(this._toString); + + String toString() => _toString; +} + +main(List arguments) { + switch (arguments.last) { + case "simple-string": + print("Hello World"); + break; + case "string-internal-linefeeds": + print("l1\nl2\nl3"); + break; + case "string-internal-carriagereturns": + print("l1\rl2\rl3\r"); + break; + case "string-internal-carriagereturn-linefeeds": + print("l1\r\nl2\r\nl3\r\n"); + break; + case "object-internal-linefeeds": + print(ToString("l1\nl2\nl3")); + break; + default: + stderr.writeln("Command was not recognized"); + exit(1); + break; + } +} diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status index 7e588d08fa8..64e3c653b12 100644 --- a/tests/standalone/standalone.status +++ b/tests/standalone/standalone.status @@ -62,6 +62,7 @@ verbose_gc_to_bmu_test: SkipByDesign # No verbose_gc in product mode http_launch_test: Skip io/addlatexhash_test: Skip io/issue_46436_test: SkipByDesign # Uses mirrors. +io/print_test: SkipByDesign # Attempts to spawn dart using Platform.executable io/socket_sigpipe_test: SkipByDesign # Spawns server process using Platform.executable io/wait_for_event_isolate_test: SkipByDesign # Uses mirrors. io/wait_for_event_microtask_test: SkipByDesign # Uses mirrors.