| // Copyright (c) 2015, 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 "native_testing.dart"; |
| import 'dart:_foreign_helper' show JS_INTERCEPTOR_CONSTANT; |
| import 'dart:_interceptors' |
| show |
| Interceptor, |
| LegacyJavaScriptObject, |
| PlainJavaScriptObject, |
| UnknownJavaScriptObject; |
| |
| // Test for safe formatting of JavaScript objects by Error.safeToString. |
| |
| @Native('PPPP') |
| class Purple {} |
| |
| @Native('QQQQ') |
| class Q {} |
| |
| @Native('RRRR') |
| class Rascal { |
| toString() => 'RRRRRRRR'; |
| } |
| |
| makeA() native; |
| makeB() native; |
| makeC() native; |
| makeD() native; |
| makeE() native; |
| makeP() native; |
| makeQ() native; |
| makeR() native; |
| |
| void setup() { |
| JS('', r""" |
| (function(){ |
| self.makeA = function(){return {hello: 123};}; |
| |
| function BB(){} |
| self.makeB = function(){return new BB();}; |
| |
| function CC(){} |
| self.makeC = function(){ |
| var x = new CC(); |
| x.constructor = null; // Foils constructor lookup. |
| return x; |
| }; |
| |
| function DD(){} |
| self.makeD = function(){ |
| var x = new DD(); |
| x.constructor = {name: 'DDxxx'}; // Foils constructor lookup. |
| return x; |
| }; |
| |
| function EE(){} |
| self.makeE = function(){ |
| var x = new EE(); |
| x.constructor = function Liar(){}; // Looks like a legitimate constructor. |
| return x; |
| }; |
| |
| function PPPP(){} |
| self.makeP = function(){return new PPPP();}; |
| |
| function QQQQ(){} |
| self.makeQ = function(){return new QQQQ();}; |
| |
| function RRRR(){} |
| self.makeR = function(){return new RRRR();}; |
| |
| self.nativeConstructor(PPPP); |
| self.nativeConstructor(QQQQ); |
| self.nativeConstructor(RRRR); |
| })()"""); |
| applyTestExtensions(['PPPP', 'QQQQ', 'RRRR']); |
| } |
| |
| expectTypeName(expectedName, s) { |
| var m = new RegExp(r"Instance of '(.*)'").firstMatch(s); |
| Expect.isNotNull(m); |
| var name = m!.group(1); |
| Expect.isTrue( |
| expectedName == name || |
| name!.length <= 3 || |
| name!.startsWith('minified:'), |
| "Is '$expectedName' or minified: '$name'"); |
| } |
| |
| final plainJsString = |
| Error.safeToString(JS_INTERCEPTOR_CONSTANT(PlainJavaScriptObject)); |
| |
| final unknownJsString = |
| Error.safeToString(JS_INTERCEPTOR_CONSTANT(UnknownJavaScriptObject)); |
| |
| final interceptorString = |
| Error.safeToString(JS_INTERCEPTOR_CONSTANT(Interceptor)); |
| |
| testDistinctInterceptors() { |
| // Test invariants needed for the other tests. |
| |
| Expect.notEquals(plainJsString, unknownJsString); |
| Expect.notEquals(plainJsString, interceptorString); |
| Expect.notEquals(unknownJsString, interceptorString); |
| |
| expectTypeName('PlainJavaScriptObject', plainJsString); |
| expectTypeName('UnknownJavaScriptObject', unknownJsString); |
| expectTypeName('Interceptor', interceptorString); |
| |
| // Sometimes interceptor *objects* are used instead of the prototypes. Check |
| // these work too. |
| var plain2 = Error.safeToString(const PlainJavaScriptObject()); |
| Expect.equals(plainJsString, plain2); |
| |
| var unk2 = Error.safeToString(const UnknownJavaScriptObject()); |
| Expect.equals(unknownJsString, unk2); |
| } |
| |
| testExternal() { |
| var x = makeA(); |
| Expect.equals(plainJsString, Error.safeToString(x)); |
| |
| x = makeB(); |
| // Gets name from constructor, regardless of minification. |
| Expect.equals("Instance of 'BB'", Error.safeToString(x)); |
| |
| x = makeC(); |
| Expect.equals(unknownJsString, Error.safeToString(x)); |
| |
| x = makeD(); |
| Expect.equals(unknownJsString, Error.safeToString(x)); |
| |
| x = makeE(); |
| Expect.equals("Instance of 'Liar'", Error.safeToString(x)); |
| } |
| |
| testNative() { |
| var x = makeP(); |
| Expect.isTrue(x is Purple); // This test forces Purple to be distinguished. |
| Expect.notEquals(plainJsString, Error.safeToString(x)); |
| Expect.notEquals(unknownJsString, Error.safeToString(x)); |
| Expect.notEquals(interceptorString, Error.safeToString(x)); |
| // And not the native class constructor. |
| Expect.notEquals("Instance of 'PPPP'", Error.safeToString(x)); |
| expectTypeName('Purple', Error.safeToString(x)); |
| |
| x = makeQ(); |
| print('Q: $x ${Error.safeToString(x)}'); |
| // We are going to get either the general interceptor or the JavaScript |
| // constructor. |
| Expect.isTrue("Instance of 'QQQQ'" == Error.safeToString(x) || |
| interceptorString == Error.safeToString(x)); |
| |
| x = makeR(); |
| |
| // Rascal overrides 'toString'. The toString() call causes Rascal to be |
| // distinguished. |
| x.toString(); |
| Expect.notEquals(plainJsString, Error.safeToString(x)); |
| Expect.notEquals(unknownJsString, Error.safeToString(x)); |
| Expect.notEquals(interceptorString, Error.safeToString(x)); |
| // And not the native class constructor. |
| Expect.notEquals("Instance of 'RRRR'", Error.safeToString(x)); |
| expectTypeName('Rascal', Error.safeToString(x)); |
| } |
| |
| main() { |
| nativeTesting(); |
| setup(); |
| |
| testDistinctInterceptors(); |
| testExternal(); |
| testNative(); |
| } |