[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:
Nicholas Shahan 2020-07-31 01:29:44 +00:00 committed by commit-bot@chromium.org
parent 15494c741b
commit d514ee5a0e
3 changed files with 142 additions and 3 deletions

View file

@ -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);

View 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));
}

View 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));
}