blob: 3f1f540eccbf7297cdb87cd18fdd887422cbae8e [file] [log] [blame]
// Copyright (c) 2020, 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 'js_invocations_in_non_web_library.dart';
import 'js_invocations_in_web_library.dart';
import 'null_assertions_lib.dart';
/// Returns an 'AAA' object that satisfies the interface.
AAA makeA() native;
/// Returns an 'AAA' object where each method breaks the interface's contract.
AAA makeAX() native;
/// Returns a 'JSInterface' object whose `JS()` invocations exist in a library
/// that is part of the allowlist.
CCCInWebLibrary makeWebC() native;
/// Returns the same as above but where each method breaks the interface's
/// contract.
CCCInWebLibrary makeWebCX() native;
/// Returns a 'JSInterface' object whose `JS()` invocations exist in a library
/// that is not part of the allowlist.
CCCInNonWebLibrary makeNonWebC() native;
/// Returns the same as above but where each method breaks the interface's
/// contract.
CCCInNonWebLibrary makeNonWebCX() native;
void setup() {
JS('', r"""
(function(){
function AAA(s,n,m1,m2) {
this.size = s;
this.name = n;
this.optName = n;
this._m1 = m1;
this._m2 = m2;
}
AAA.prototype.method1 = function(){return this._m1};
AAA.prototype.method2 = function(){return this._m2};
AAA.prototype.optMethod = function(){return this._m2};
makeA = function() {
return new AAA(100, 'Albert', 200, 'amazing!');
};
makeAX = function() {
return new AAA(void 0, void 0, void 0, void 0);
};
self.nativeConstructor(AAA);
function CCCInWebLibrary(n) {
this.name = n;
this.optName = n;
}
function CCCInNonWebLibrary(n) {
this.name = n;
this.optName = n;
}
makeWebC = function() {
return new CCCInWebLibrary('Carol');
};
makeWebCX = function() {
return new CCCInWebLibrary(void 0);
};
makeNonWebC = function() {
return new CCCInNonWebLibrary('Carol');
};
makeNonWebCX = function() {
return new CCCInNonWebLibrary(void 0);
};
self.nativeConstructor(CCCInWebLibrary);
self.nativeConstructor(CCCInNonWebLibrary);
})()""");
}
// The 'NativeInterface' version of the code is passed both native and Dart
// objects, so there will be an interceptor dispatch to the method. This tests
// that the null-check exists in the forwarding method.
//
// The 'AAA' version of the code is passed only objects of a single native
// class, so the native method can be inlined (which happens in the optimizer).
// This tests that the null-check exists in the 'inlined' code.
@pragma('dart2js:noInline')
String describeNativeInterface(NativeInterface o) {
return '${o.name} ${o.method2()} ${o.size} ${o.method1()}';
}
@pragma('dart2js:noInline')
String describeAAA(AAA o) {
return '${o.name} ${o.method2()} ${o.size} ${o.method1()}';
}
@pragma('dart2js:noInline')
String describeOptNativeInterface(NativeInterface o) {
return '${o.optName} ${o.optMethod()}';
}
@pragma('dart2js:noInline')
String describeOptAAA(AAA o) {
return '${o.optName} ${o.optMethod()}';
}
@pragma('dart2js:noInline')
String describeJSInterface(JSInterface o) {
return '${o.name}';
}
@pragma('dart2js:noInline')
String describeOptJSInterface(JSInterface o) {
return '${o.optName}';
}
const expectedA = 'Albert amazing! 100 200';
const expectedB = 'Brenda brilliant! 300 400';
const expectedOptA = 'Albert amazing!';
const expectedOptB = 'Brenda brilliant!';
const expectedOptX = 'null null';
const expectedC = 'Carol';
const expectedOptC = 'Carol';
const expectedOptCX = 'null';
// Test that `--native-null-assertions` injects null-checks on the returned
// value of native methods with a non-nullable return type in an opt-in library.
void testNativeNullAssertions(bool flagEnabled) {
nativeTesting();
setup();
AAA a = makeA();
BBB b = BBB();
Expect.equals(expectedA, describeNativeInterface(a));
Expect.equals(expectedB, describeNativeInterface(b));
Expect.equals(expectedA, describeAAA(a));
AAA x = makeAX(); // This object returns `null`!
var checkExpectation = flagEnabled ? Expect.throws : (f) => f();
checkExpectation(() => describeNativeInterface(x));
checkExpectation(() => describeAAA(x));
checkExpectation(() => x.name);
checkExpectation(() => x.size);
checkExpectation(() => x.method1());
checkExpectation(() => x.method2());
// Now test that a nullable return type does not have a check.
Expect.equals(expectedOptA, describeOptNativeInterface(a));
Expect.equals(expectedOptB, describeOptNativeInterface(b));
Expect.equals(expectedOptX, describeOptNativeInterface(x));
Expect.equals(expectedOptA, describeOptAAA(a));
Expect.equals(expectedOptX, describeOptAAA(x));
}
// Test that `--native-null-assertions` injects null-checks on the returned
// value of `JS()` invocations with a non-nullable static type in an opt-in
// library.
void testJSInvocationNullAssertions(bool flagEnabled) {
nativeTesting();
setup();
CCCInWebLibrary webC = makeWebC();
CCCInWebLibrary webCX = makeWebCX();
CCCInNonWebLibrary nonWebC = makeNonWebC();
CCCInNonWebLibrary nonWebCX = makeNonWebCX();
Expect.equals(expectedC, describeJSInterface(webC));
Expect.equals(expectedC, describeJSInterface(nonWebC));
// If invocations are in a web library, this should throw if null checks are
// enabled.
var checkExpectationWeb = flagEnabled ? Expect.throws : (f) => f();
checkExpectationWeb(() => describeJSInterface(webCX));
// If invocations are not in a web library, there should not be a null check
// regardless if the flag is enabled or not.
var checkExpectationNonWeb = (f) => f();
checkExpectationNonWeb(() => describeJSInterface(nonWebCX));
// Test that invocations with a nullable static type do not have checks.
Expect.equals(expectedOptC, describeOptJSInterface(webC));
Expect.equals(expectedOptC, describeOptJSInterface(nonWebC));
Expect.equals(expectedOptCX, describeOptJSInterface(webCX));
Expect.equals(expectedOptCX, describeOptJSInterface(nonWebCX));
}