mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 16:26:38 +00:00
[ddc] Fix method call on object without prototype
Creating a js object with `Object.create(null)` produces an empty js object with no prototype. Calling methods attached to these objects would result in a NPE since we assumed all objects would have a valid `.__proto__`. Discovered the error in some benchmark code. Created a new test with the same patterns. Change-Id: I1fd38893b0c90315b338c3b6a1b4981be1d64065 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/156582 Commit-Queue: Nicholas Shahan <nshahan@google.com> Reviewed-by: Mark Zhou <markzipan@google.com>
This commit is contained in:
parent
15494c741b
commit
d514ee5a0e
|
@ -268,9 +268,18 @@ getStaticSetters(value) => _getMembers(value, _staticSetterSig);
|
|||
|
||||
getGenericTypeCtor(value) => JS('', '#[#]', value, _genericTypeCtor);
|
||||
|
||||
/// Get the type of a method from an object using the stored signature
|
||||
getType(obj) =>
|
||||
JS('', '# == null ? # : #.__proto__.constructor', obj, Object, obj);
|
||||
/// Get the type of an object.
|
||||
getType(obj) {
|
||||
if (obj == null) return JS('!', '#', Object);
|
||||
|
||||
if (JS<bool>('!', '#.__proto__ == null', obj)) {
|
||||
// Object.create(null) produces a js object without a prototype.
|
||||
// In that case use the version from a js object literal.
|
||||
return JS('!', '#.Object.prototype.constructor', global_);
|
||||
}
|
||||
|
||||
return JS('!', '#.__proto__.constructor', obj);
|
||||
}
|
||||
|
||||
getLibraryUri(value) => JS('', '#[#]', value, _libraryUri);
|
||||
setLibraryUri(f, uri) => JS('', '#[#] = #', f, _libraryUri, uri);
|
||||
|
|
65
tests/lib/js/method_call_on_object_test.dart
Normal file
65
tests/lib/js/method_call_on_object_test.dart
Normal file
|
@ -0,0 +1,65 @@
|
|||
// 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.
|
||||
|
||||
// Tests method calls (typed and dynamic) on various forms of JS objects.
|
||||
|
||||
@JS()
|
||||
library js_parameters_test;
|
||||
|
||||
import 'package:js/js.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
@JS()
|
||||
external void eval(String code);
|
||||
|
||||
@JS()
|
||||
class Foo {
|
||||
external Foo();
|
||||
external dynamic method(int x);
|
||||
}
|
||||
|
||||
@JS()
|
||||
external Foo makeFooLiteral();
|
||||
|
||||
@JS()
|
||||
external Foo makeFooObjectCreate();
|
||||
|
||||
main() {
|
||||
// These examples from based on benchmarks-internal/js
|
||||
eval(r'''
|
||||
self.Foo = function Foo() {}
|
||||
self.Foo.prototype.method = function(x) { return x + 1; }
|
||||
|
||||
self.makeFooLiteral = function() {
|
||||
return {
|
||||
method: function(x) { return x + 1; }
|
||||
}
|
||||
}
|
||||
|
||||
// Objects created in this way have no prototype.
|
||||
self.makeFooObjectCreate = function() {
|
||||
var o = Object.create(null);
|
||||
o.method = function(x) { return x + 1; }
|
||||
return o;
|
||||
}
|
||||
''');
|
||||
|
||||
var foo = Foo();
|
||||
Expect.equals(2, foo.method(1));
|
||||
|
||||
foo = makeFooLiteral();
|
||||
Expect.equals(2, foo.method(1));
|
||||
|
||||
foo = makeFooObjectCreate();
|
||||
Expect.equals(2, foo.method(1));
|
||||
|
||||
dynamic dynamicFoo = Foo();
|
||||
Expect.equals(2, dynamicFoo.method(1));
|
||||
|
||||
dynamicFoo = makeFooLiteral();
|
||||
Expect.equals(2, dynamicFoo.method(1));
|
||||
|
||||
dynamicFoo = makeFooObjectCreate();
|
||||
Expect.equals(2, dynamicFoo.method(1));
|
||||
}
|
65
tests/lib_2/js/method_call_on_object_test.dart
Normal file
65
tests/lib_2/js/method_call_on_object_test.dart
Normal file
|
@ -0,0 +1,65 @@
|
|||
// 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.
|
||||
|
||||
// Tests method calls (typed and dynamic) on various forms of JS objects.
|
||||
|
||||
@JS()
|
||||
library js_parameters_test;
|
||||
|
||||
import 'package:js/js.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
@JS()
|
||||
external void eval(String code);
|
||||
|
||||
@JS()
|
||||
class Foo {
|
||||
external Foo();
|
||||
external dynamic method(int x);
|
||||
}
|
||||
|
||||
@JS()
|
||||
external Foo makeFooLiteral();
|
||||
|
||||
@JS()
|
||||
external Foo makeFooObjectCreate();
|
||||
|
||||
main() {
|
||||
// These examples from based on benchmarks-internal/js
|
||||
eval(r'''
|
||||
self.Foo = function Foo() {}
|
||||
self.Foo.prototype.method = function(x) { return x + 1; }
|
||||
|
||||
self.makeFooLiteral = function() {
|
||||
return {
|
||||
method: function(x) { return x + 1; }
|
||||
}
|
||||
}
|
||||
|
||||
// Objects created in this way have no prototype.
|
||||
self.makeFooObjectCreate = function() {
|
||||
var o = Object.create(null);
|
||||
o.method = function(x) { return x + 1; }
|
||||
return o;
|
||||
}
|
||||
''');
|
||||
|
||||
var foo = Foo();
|
||||
Expect.equals(2, foo.method(1));
|
||||
|
||||
foo = makeFooLiteral();
|
||||
Expect.equals(2, foo.method(1));
|
||||
|
||||
foo = makeFooObjectCreate();
|
||||
Expect.equals(2, foo.method(1));
|
||||
|
||||
dynamic dynamicFoo = Foo();
|
||||
Expect.equals(2, dynamicFoo.method(1));
|
||||
|
||||
dynamicFoo = makeFooLiteral();
|
||||
Expect.equals(2, dynamicFoo.method(1));
|
||||
|
||||
dynamicFoo = makeFooObjectCreate();
|
||||
Expect.equals(2, dynamicFoo.method(1));
|
||||
}
|
Loading…
Reference in a new issue