| // 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![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(); | |
| } |