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