mirror of
https://github.com/dart-lang/sdk
synced 2024-11-05 18:22:09 +00:00
Add @anonymous annotation and restrict object literal constructors to only anonymous classes. This frees up defining factory constructors that do not correspond to object literals on JS interop classes.
BUG= R=sra@google.com Review URL: https://codereview.chromium.org/1409033005 .
This commit is contained in:
parent
688b69fb65
commit
e0d2263bb3
7 changed files with 126 additions and 13 deletions
|
@ -278,6 +278,8 @@ enum MessageKind {
|
|||
INVALID_USE_OF_SUPER,
|
||||
JS_INTEROP_CLASS_CANNOT_EXTEND_DART_CLASS,
|
||||
JS_INTEROP_CLASS_NON_EXTERNAL_MEMBER,
|
||||
JS_OBJECT_LITERAL_CONSTRUCTOR_WITH_POSITIONAL_ARGUMENTS,
|
||||
JS_INTEROP_METHOD_WITH_NAMED_ARGUMENTS,
|
||||
LIBRARY_NAME_MISMATCH,
|
||||
LIBRARY_NOT_FOUND,
|
||||
LIBRARY_NOT_SUPPORTED,
|
||||
|
@ -2172,6 +2174,51 @@ main() => A.A = 1;
|
|||
}
|
||||
"""]),
|
||||
|
||||
MessageKind.JS_INTEROP_METHOD_WITH_NAMED_ARGUMENTS:
|
||||
const MessageTemplate(
|
||||
MessageKind.JS_INTEROP_METHOD_WITH_NAMED_ARGUMENTS,
|
||||
"Js-interop method '#{method}' has named arguments but is not "
|
||||
"a factory constructor of an @anonymous @JS class.",
|
||||
howToFix: "Remove all named arguments from js-interop method or "
|
||||
"in the case of a factory constructor annotate the class "
|
||||
"as @anonymous.",
|
||||
examples: const [
|
||||
"""
|
||||
import 'package:js/js.dart';
|
||||
|
||||
@JS()
|
||||
class Foo {
|
||||
external bar(foo, {baz});
|
||||
}
|
||||
|
||||
main() {
|
||||
new Foo().bar(4, baz: 5);
|
||||
}
|
||||
"""]),
|
||||
|
||||
MessageKind.JS_OBJECT_LITERAL_CONSTRUCTOR_WITH_POSITIONAL_ARGUMENTS:
|
||||
const MessageTemplate(
|
||||
MessageKind.JS_OBJECT_LITERAL_CONSTRUCTOR_WITH_POSITIONAL_ARGUMENTS,
|
||||
"Parameter '#{parameter}' in anonymous js-interop class '#{cls}' "
|
||||
"object literal constructor is positional instead of named."
|
||||
".",
|
||||
howToFix: "Make all arguments in external factory object literal "
|
||||
"constructors named.",
|
||||
examples: const [
|
||||
"""
|
||||
import 'package:js/js.dart';
|
||||
|
||||
@anonymous
|
||||
@JS()
|
||||
class Foo {
|
||||
external factory Foo(foo, {baz});
|
||||
}
|
||||
|
||||
main() {
|
||||
new Foo(5, baz: 5);
|
||||
}
|
||||
"""]),
|
||||
|
||||
MessageKind.LIBRARY_NOT_FOUND:
|
||||
const MessageTemplate(MessageKind.LIBRARY_NOT_FOUND,
|
||||
"Library not found '#{resolvedUri}'."),
|
||||
|
|
|
@ -130,6 +130,7 @@ class BackendHelpers {
|
|||
ClassElement irRepresentationClass;
|
||||
|
||||
ClassElement jsAnnotationClass;
|
||||
ClassElement jsAnonymousClass;
|
||||
|
||||
Element getInterceptorMethod;
|
||||
|
||||
|
@ -303,6 +304,7 @@ class BackendHelpers {
|
|||
typedArrayOfIntClass = findClass('NativeTypedArrayOfInt');
|
||||
} else if (uri == PACKAGE_JS) {
|
||||
jsAnnotationClass = find(library, 'JS');
|
||||
jsAnonymousClass = find(library, '_Anonymous');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import '../elements/elements.dart'
|
|||
FieldElement,
|
||||
FunctionElement,
|
||||
LibraryElement,
|
||||
ParameterElement,
|
||||
MetadataAnnotation;
|
||||
|
||||
import '../js/js.dart' as jsAst;
|
||||
|
@ -83,11 +84,39 @@ class JsInteropAnalysis {
|
|||
}
|
||||
}
|
||||
|
||||
bool hasAnonymousAnnotation(Element element) {
|
||||
if (backend.helpers.jsAnonymousClass == null) return false;
|
||||
return element.metadata.any((MetadataAnnotation annotation) {
|
||||
ConstantValue constant = backend.compiler.constants.getConstantValue(
|
||||
annotation.constant);
|
||||
if (constant == null ||
|
||||
constant is! ConstructedConstantValue) return false;
|
||||
ConstructedConstantValue constructedConstant = constant;
|
||||
return constructedConstant.type.element ==
|
||||
backend.helpers.jsAnonymousClass;
|
||||
});
|
||||
}
|
||||
|
||||
void _checkFunctionParameters(FunctionElement fn) {
|
||||
if (fn.hasFunctionSignature &&
|
||||
fn.functionSignature.optionalParametersAreNamed) {
|
||||
backend.reporter.reportErrorMessage(fn,
|
||||
MessageKind.JS_INTEROP_METHOD_WITH_NAMED_ARGUMENTS, {
|
||||
'method': fn.name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void processJsInteropAnnotationsInLibrary(LibraryElement library) {
|
||||
processJsInteropAnnotation(library);
|
||||
library.implementation.forEachLocalMember((Element element) {
|
||||
processJsInteropAnnotation(element);
|
||||
if (!element.isClass || !backend.isJsInterop(element)) return;
|
||||
if (!backend.isJsInterop(element)) return;
|
||||
if (element is FunctionElement) {
|
||||
_checkFunctionParameters(element);
|
||||
}
|
||||
|
||||
if (!element.isClass) return;
|
||||
|
||||
ClassElement classElement = element;
|
||||
|
||||
|
@ -108,12 +137,30 @@ class JsInteropAnalysis {
|
|||
backend.isJsInterop(classElement) &&
|
||||
member is FunctionElement) {
|
||||
FunctionElement fn = member;
|
||||
if (!fn.isExternal && !fn.isAbstract) {
|
||||
if (!fn.isExternal && !fn.isAbstract && !fn.isConstructor &&
|
||||
!fn.isStatic) {
|
||||
backend.reporter.reportErrorMessage(
|
||||
fn,
|
||||
MessageKind.JS_INTEROP_CLASS_NON_EXTERNAL_MEMBER,
|
||||
{'cls': classElement.name, 'member': member.name});
|
||||
}
|
||||
|
||||
if (fn.isFactoryConstructor && hasAnonymousAnnotation(classElement)) {
|
||||
fn.functionSignature.orderedForEachParameter(
|
||||
(ParameterElement parameter) {
|
||||
if (!parameter.isNamed) {
|
||||
backend.reporter.reportErrorMessage(parameter,
|
||||
MessageKind
|
||||
.JS_OBJECT_LITERAL_CONSTRUCTOR_WITH_POSITIONAL_ARGUMENTS,
|
||||
{
|
||||
'parameter': parameter.name,
|
||||
'cls': classElement.name
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
_checkFunctionParameters(fn);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5843,6 +5843,12 @@ class SsaBuilder extends ast.Visitor
|
|||
}
|
||||
}
|
||||
|
||||
bool _hasNamedParameters(FunctionElement function) {
|
||||
FunctionSignature params = function.functionSignature;
|
||||
return params.optionalParameterCount > 0
|
||||
&& params.optionalParametersAreNamed;
|
||||
}
|
||||
|
||||
HForeignCode invokeJsInteropFunction(Element element,
|
||||
List<HInstruction> arguments,
|
||||
SourceInformation sourceInformation) {
|
||||
|
@ -5850,9 +5856,10 @@ class SsaBuilder extends ast.Visitor
|
|||
nativeEmitter.nativeMethods.add(element);
|
||||
String templateString;
|
||||
|
||||
if (element.isFactoryConstructor) {
|
||||
// Treat factory constructors as syntactic sugar for creating object
|
||||
// literals.
|
||||
if (element.isFactoryConstructor &&
|
||||
backend.jsInteropAnalysis.hasAnonymousAnnotation(element.contextClass)) {
|
||||
// Factory constructor that is syntactic sugar for creating a JavaScript
|
||||
// object literal.
|
||||
ConstructorElement constructor = element;
|
||||
FunctionSignature params = constructor.functionSignature;
|
||||
int i = 0;
|
||||
|
@ -5863,13 +5870,6 @@ class SsaBuilder extends ast.Visitor
|
|||
// TODO(jacobr): throw if parameter names do not match names of property
|
||||
// names in the class.
|
||||
assert (parameter.isNamed);
|
||||
if (!parameter.isNamed) {
|
||||
reporter.reportErrorMessage(
|
||||
parameter, MessageKind.GENERIC,
|
||||
{'text': 'All arguments to external constructors of JavaScript '
|
||||
'interop classes must be named as these constructors '
|
||||
'are syntactic sugar for object literals.'});
|
||||
}
|
||||
HInstruction argument = arguments[i];
|
||||
if (argument != null) {
|
||||
filteredArguments.add(argument);
|
||||
|
@ -5908,10 +5908,14 @@ class SsaBuilder extends ast.Visitor
|
|||
} else if (element.isSetter) {
|
||||
codeTemplate = js.js.parseForeignJS("# = #");
|
||||
} else {
|
||||
FunctionElement function = element;
|
||||
FunctionSignature params = function.functionSignature;
|
||||
|
||||
var argsStub = <String>[];
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
argsStub.add('#');
|
||||
}
|
||||
|
||||
if (element.isConstructor) {
|
||||
codeTemplate = js.js.parseForeignJS("new #(${argsStub.join(",")})");
|
||||
} else {
|
||||
|
|
|
@ -18,3 +18,15 @@ class JS {
|
|||
final String name;
|
||||
const JS([this.name]);
|
||||
}
|
||||
|
||||
class _Anonymous {
|
||||
const _Anonymous();
|
||||
}
|
||||
|
||||
/// A metadata annotation that indicates that a @JS annotated class is
|
||||
/// structural and does not have a known JavaScript prototype.
|
||||
///
|
||||
/// Factory constructors for anonymous JavaScript classes desugar to creating
|
||||
/// JavaScript object literals with name-value pairs corresponding to the
|
||||
/// parameter names and values.
|
||||
const _Anonymous anonymous = const _Anonymous();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: js
|
||||
version: 0.6.0-beta.6
|
||||
version: 0.6.0-beta.7
|
||||
authors:
|
||||
- Dart Team <misc@dartlang.org>
|
||||
description: Access JavaScript from Dart.
|
||||
|
|
|
@ -114,6 +114,7 @@ class Foo {
|
|||
external static int multiplyDefault2(int a, [int b]);
|
||||
}
|
||||
|
||||
@anonymous
|
||||
@JS()
|
||||
class ExampleLiteral {
|
||||
external factory ExampleLiteral({int x, String y, num z});
|
||||
|
|
Loading…
Reference in a new issue