Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(771)

Unified Diff: pkg/compiler/lib/src/native/behavior.dart

Issue 2455073003: Compute NativeBehavior for foreign functions. (Closed)
Patch Set: Updated cf. comment. Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/compiler/lib/src/js_backend/backend_helpers.dart ('k') | pkg/compiler/lib/src/native/enqueue.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/compiler/lib/src/native/behavior.dart
diff --git a/pkg/compiler/lib/src/native/behavior.dart b/pkg/compiler/lib/src/native/behavior.dart
index 57ef61a02df98506e5e15e2799ff48ef0ceb6582..86096300d62254086991f5033e4501bcaa1de793 100644
--- a/pkg/compiler/lib/src/native/behavior.dart
+++ b/pkg/compiler/lib/src/native/behavior.dart
@@ -6,6 +6,7 @@ import '../common.dart';
import '../common/backend_api.dart' show ForeignResolver;
import '../common/resolution.dart' show ParsingContext, Resolution;
import '../compiler.dart' show Compiler;
+import '../constants/expressions.dart';
import '../constants/values.dart';
import '../core_types.dart' show CoreTypes;
import '../dart_types.dart';
@@ -18,6 +19,8 @@ import '../util/util.dart';
import 'enqueue.dart';
import 'js.dart';
+typedef dynamic /*DartType|SpecialType*/ TypeLookup(String typeString);
+
/// This class is a temporary work-around until we get a more powerful DartType.
class SpecialType {
final String name;
@@ -263,7 +266,7 @@ class NativeBehavior {
void setThrows(NativeThrowBehavior throwKind),
void setIsAllocation(bool isAllocation),
void setUseGvn(bool useGvn),
- dynamic resolveType(String typeString),
+ TypeLookup lookupType,
List typesReturned,
List typesInstantiated,
objectType,
@@ -306,7 +309,7 @@ class NativeBehavior {
return;
}
for (final typeString in typesString.split('|')) {
- onType(resolveType(typeString.trim()));
+ onType(_parseType(typeString.trim(), spannable, reporter, lookupType));
}
}
@@ -486,49 +489,61 @@ class NativeBehavior {
return sideEffects;
}
- static NativeBehavior ofJsCall(Send jsCall, DiagnosticReporter reporter,
- ParsingContext parsing, CoreTypes coreTypes, ForeignResolver resolver) {
- // The first argument of a JS-call is a string encoding various attributes
- // of the code.
- //
- // 'Type1|Type2'. A union type.
- // '=Object'. A JavaScript Object, no subtype.
-
- NativeBehavior behavior = new NativeBehavior();
+ /// Returns a [TypeLookup] that uses [resolver] to perform lookup and [node]
+ /// as position for errors.
+ static TypeLookup _typeLookup(Node node, ForeignResolver resolver) {
+ return (String name) => resolver.resolveTypeFromString(node, name);
+ }
+ /// Compute the [NativeBehavior] for a [Send] node calling the 'JS' function.
+ static NativeBehavior ofJsCallSend(Send jsCall, DiagnosticReporter reporter,
+ ParsingContext parsing, CoreTypes coreTypes, ForeignResolver resolver) {
var argNodes = jsCall.arguments;
if (argNodes.isEmpty || argNodes.tail.isEmpty) {
- reporter.reportErrorMessage(jsCall, MessageKind.GENERIC,
- {'text': "JS expression takes two or more arguments."});
- return behavior;
+ reporter.reportErrorMessage(jsCall, MessageKind.WRONG_ARGUMENT_FOR_JS);
+ return new NativeBehavior();
}
var specArgument = argNodes.head;
if (specArgument is! StringNode || specArgument.isInterpolation) {
- reporter.reportErrorMessage(specArgument, MessageKind.GENERIC,
- {'text': "JS first argument must be a string literal."});
- return behavior;
+ reporter.reportErrorMessage(
+ specArgument, MessageKind.WRONG_ARGUMENT_FOR_JS_FIRST);
+ return new NativeBehavior();
}
var codeArgument = argNodes.tail.head;
if (codeArgument is! StringNode || codeArgument.isInterpolation) {
- reporter.reportErrorMessage(codeArgument, MessageKind.GENERIC,
- {'text': "JS second argument must be a string literal."});
- return behavior;
+ reporter.reportErrorMessage(
+ codeArgument, MessageKind.WRONG_ARGUMENT_FOR_JS_SECOND);
+ return new NativeBehavior();
}
- behavior.codeTemplateText = codeArgument.dartString.slowToString();
- behavior.codeTemplate = js.js.parseForeignJS(behavior.codeTemplateText);
-
String specString = specArgument.dartString.slowToString();
+ String codeString = codeArgument.dartString.slowToString();
- dynamic resolveType(String typeString) {
- return _parseType(
- typeString,
- parsing,
- (name) => resolver.resolveTypeFromString(specArgument, name),
- specArgument);
- }
+ return ofJsCall(specString, codeString, _typeLookup(specArgument, resolver),
+ specArgument, reporter, coreTypes);
+ }
+
+ /// Compute the [NativeBehavior] for a call to the 'JS' function with the
+ /// given [specString] and [codeString] (first and second arguments).
+ static NativeBehavior ofJsCall(
+ String specString,
+ String codeString,
+ TypeLookup lookupType,
+ Spannable spannable,
+ DiagnosticReporter reporter,
+ CoreTypes coreTypes) {
+ // The first argument of a JS-call is a string encoding various attributes
+ // of the code.
+ //
+ // 'Type1|Type2'. A union type.
+ // '=Object'. A JavaScript Object, no subtype.
+
+ NativeBehavior behavior = new NativeBehavior();
+
+ behavior.codeTemplateText = codeString;
+ behavior.codeTemplate = js.js.parseForeignJS(behavior.codeTemplateText);
bool sideEffectsAreEncodedInSpecString = false;
@@ -551,12 +566,12 @@ class NativeBehavior {
behavior.useGvn = useGvn;
}
- processSpecString(reporter, specArgument, specString,
+ processSpecString(reporter, spannable, specString,
setSideEffects: setSideEffects,
setThrows: setThrows,
setIsAllocation: setIsAllocation,
setUseGvn: setUseGvn,
- resolveType: resolveType,
+ lookupType: lookupType,
typesReturned: behavior.typesReturned,
typesInstantiated: behavior.typesInstantiated,
objectType: coreTypes.objectType,
@@ -576,40 +591,51 @@ class NativeBehavior {
static void _fillNativeBehaviorOfBuiltinOrEmbeddedGlobal(
NativeBehavior behavior,
- Send jsBuiltinOrEmbeddedGlobalCall,
+ Spannable spannable,
+ String specString,
+ TypeLookup lookupType,
DiagnosticReporter reporter,
- ParsingContext parsing,
CoreTypes coreTypes,
- ForeignResolver resolver,
- {bool isBuiltin,
- List<String> validTags}) {
+ {List<String> validTags}) {
+ void setSideEffects(SideEffects newEffects) {
+ behavior.sideEffects.setTo(newEffects);
+ }
+
+ processSpecString(reporter, spannable, specString,
+ validTags: validTags,
+ lookupType: lookupType,
+ setSideEffects: setSideEffects,
+ typesReturned: behavior.typesReturned,
+ typesInstantiated: behavior.typesInstantiated,
+ objectType: coreTypes.objectType,
+ nullType: coreTypes.nullType);
+ }
+
+ static NativeBehavior ofJsBuiltinCallSend(
+ Send jsBuiltinCall,
+ DiagnosticReporter reporter,
+ CoreTypes coreTypes,
+ ForeignResolver resolver) {
+ NativeBehavior behavior = new NativeBehavior();
+ behavior.sideEffects.setTo(new SideEffects());
+
// The first argument of a JS-embedded global call is a string encoding
// the type of the code.
//
// 'Type1|Type2'. A union type.
// '=Object'. A JavaScript Object, no subtype.
- String builtinOrGlobal = isBuiltin ? "builtin" : "embedded global";
-
- Link<Node> argNodes = jsBuiltinOrEmbeddedGlobalCall.arguments;
+ Link<Node> argNodes = jsBuiltinCall.arguments;
if (argNodes.isEmpty) {
- reporter.internalError(jsBuiltinOrEmbeddedGlobalCall,
- "JS $builtinOrGlobal expression has no type.");
+ reporter.internalError(
+ jsBuiltinCall, "JS builtin expression has no type.");
}
// We don't check the given name. That needs to be done at a later point.
// This is, because we want to allow non-literals (like references to
// enums) as names.
if (argNodes.tail.isEmpty) {
- reporter.internalError(jsBuiltinOrEmbeddedGlobalCall,
- 'JS $builtinOrGlobal is missing name.');
- }
-
- if (!isBuiltin) {
- if (!argNodes.tail.tail.isEmpty) {
- reporter.internalError(argNodes.tail.tail.head,
- 'JS embedded global has more than 2 arguments');
- }
+ reporter.internalError(jsBuiltinCall, "JS builtin is missing name.");
}
LiteralString specLiteral = argNodes.head.asLiteralString();
@@ -621,48 +647,26 @@ class NativeBehavior {
String specString = specLiteral.dartString.slowToString();
- dynamic resolveType(String typeString) {
- return _parseType(
- typeString,
- parsing,
- (name) => resolver.resolveTypeFromString(specLiteral, name),
- jsBuiltinOrEmbeddedGlobalCall);
- }
-
- void setSideEffects(SideEffects newEffects) {
- behavior.sideEffects.setTo(newEffects);
- }
-
- processSpecString(reporter, jsBuiltinOrEmbeddedGlobalCall, specString,
- validTags: validTags,
- resolveType: resolveType,
- setSideEffects: setSideEffects,
- typesReturned: behavior.typesReturned,
- typesInstantiated: behavior.typesInstantiated,
- objectType: coreTypes.objectType,
- nullType: coreTypes.nullType);
+ return ofJsBuiltinCall(specString, _typeLookup(jsBuiltinCall, resolver),
+ jsBuiltinCall, reporter, coreTypes);
}
static NativeBehavior ofJsBuiltinCall(
- Send jsBuiltinCall,
+ String specString,
+ TypeLookup lookupType,
+ Spannable spannable,
DiagnosticReporter reporter,
- ParsingContext parsing,
- CoreTypes coreTypes,
- ForeignResolver resolver) {
+ CoreTypes coreTypes) {
NativeBehavior behavior = new NativeBehavior();
behavior.sideEffects.setTo(new SideEffects());
-
_fillNativeBehaviorOfBuiltinOrEmbeddedGlobal(
- behavior, jsBuiltinCall, reporter, parsing, coreTypes, resolver,
- isBuiltin: true);
-
+ behavior, spannable, specString, lookupType, reporter, coreTypes);
return behavior;
}
- static NativeBehavior ofJsEmbeddedGlobalCall(
+ static NativeBehavior ofJsEmbeddedGlobalCallSend(
Send jsEmbeddedGlobalCall,
DiagnosticReporter reporter,
- ParsingContext parsing,
CoreTypes coreTypes,
ForeignResolver resolver) {
NativeBehavior behavior = new NativeBehavior();
@@ -672,18 +676,97 @@ class NativeBehavior {
behavior.sideEffects.setTo(new SideEffects.empty());
behavior.throwBehavior = NativeThrowBehavior.NEVER;
- _fillNativeBehaviorOfBuiltinOrEmbeddedGlobal(
- behavior, jsEmbeddedGlobalCall, reporter, parsing, coreTypes, resolver,
- isBuiltin: false, validTags: const ['returns', 'creates']);
+ // The first argument of a JS-embedded global call is a string encoding
+ // the type of the code.
+ //
+ // 'Type1|Type2'. A union type.
+ // '=Object'. A JavaScript Object, no subtype.
+
+ Link<Node> argNodes = jsEmbeddedGlobalCall.arguments;
+ if (argNodes.isEmpty) {
+ reporter.internalError(
+ jsEmbeddedGlobalCall, "JS embedded global expression has no type.");
+ }
+
+ // We don't check the given name. That needs to be done at a later point.
+ // This is, because we want to allow non-literals (like references to
+ // enums) as names.
+ if (argNodes.tail.isEmpty) {
+ reporter.internalError(
+ jsEmbeddedGlobalCall, "JS embedded global is missing name.");
+ }
+
+ if (!argNodes.tail.tail.isEmpty) {
+ reporter.internalError(argNodes.tail.tail.head,
+ 'JS embedded global has more than 2 arguments');
+ }
+
+ LiteralString specLiteral = argNodes.head.asLiteralString();
+ if (specLiteral == null) {
+ // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It
+ // is not very satisfactory because it does not work for void, dynamic.
+ reporter.internalError(argNodes.head, "Unexpected first argument.");
+ }
+
+ String specString = specLiteral.dartString.slowToString();
+
+ return ofJsEmbeddedGlobalCall(
+ specString,
+ _typeLookup(jsEmbeddedGlobalCall, resolver),
+ jsEmbeddedGlobalCall,
+ reporter,
+ coreTypes);
+ }
+ static NativeBehavior ofJsEmbeddedGlobalCall(
+ String specString,
+ TypeLookup lookupType,
+ Spannable spannable,
+ DiagnosticReporter reporter,
+ CoreTypes coreTypes) {
+ NativeBehavior behavior = new NativeBehavior();
+ // TODO(sra): Allow the use site to override these defaults.
+ // Embedded globals are usually pre-computed data structures or JavaScript
+ // functions that never change.
+ behavior.sideEffects.setTo(new SideEffects.empty());
+ behavior.throwBehavior = NativeThrowBehavior.NEVER;
+ _fillNativeBehaviorOfBuiltinOrEmbeddedGlobal(
+ behavior, spannable, specString, lookupType, reporter, coreTypes,
+ validTags: ['returns', 'creates']);
return behavior;
}
- static NativeBehavior ofMethod(FunctionElement method, Compiler compiler) {
- FunctionType type = method.computeType(compiler.resolution);
+ static NativeBehavior ofMethodElement(
+ FunctionElement element, Compiler compiler) {
+ FunctionType type = element.computeType(compiler.resolution);
+ List<ConstantExpression> metadata = <ConstantExpression>[];
+ for (MetadataAnnotation annotation in element.implementation.metadata) {
+ annotation.ensureResolved(compiler.resolution);
+ metadata.add(annotation.constant);
+ }
+
+ DartType lookup(String name) {
+ Element e = element.buildScope().lookup(name);
+ if (e == null) return null;
+ if (e is! ClassElement) return null;
+ ClassElement cls = e;
+ cls.ensureResolved(compiler.resolution);
+ return cls.thisType;
+ }
+
+ return ofMethod(element, type, metadata, lookup, compiler,
+ isJsInterop: compiler.backend.isJsInterop(element));
+ }
+
+ static NativeBehavior ofMethod(
+ Spannable spannable,
+ FunctionType type,
+ List<ConstantExpression> metadata,
+ TypeLookup lookupType,
+ Compiler compiler,
+ {bool isJsInterop}) {
var behavior = new NativeBehavior();
var returnType = type.returnType;
- bool isInterop = compiler.backend.isJsInterop(method);
// Note: For dart:html and other internal libraries we maintain, we can
// trust the return type and use it to limit what we enqueue. We have to
// be more conservative about JS interop types and assume they can return
@@ -694,7 +777,7 @@ class NativeBehavior {
// but otherwise we would include a lot of code by default).
// TODO(sigmund,sra): consider doing something better for numeric types.
behavior.typesReturned.add(
- !isInterop || compiler.options.trustJSInteropTypeAnnotations
+ !isJsInterop || compiler.options.trustJSInteropTypeAnnotations
? returnType
: const DynamicType());
if (!type.returnType.isVoid) {
@@ -702,7 +785,7 @@ class NativeBehavior {
behavior.typesReturned.add(compiler.coreTypes.nullType);
}
behavior._capture(type, compiler.resolution,
- isInterop: isInterop, compiler: compiler);
+ isInterop: isJsInterop, compiler: compiler);
for (DartType type in type.optionalParameterTypes) {
behavior._escape(type, compiler.resolution);
@@ -711,31 +794,64 @@ class NativeBehavior {
behavior._escape(type, compiler.resolution);
}
- behavior._overrideWithAnnotations(method, compiler);
+ behavior._overrideWithAnnotations(
+ spannable, metadata, lookupType, compiler);
return behavior;
}
- static NativeBehavior ofFieldLoad(MemberElement field, Compiler compiler) {
+ static NativeBehavior ofFieldElementLoad(
+ MemberElement element, Compiler compiler) {
+ Resolution resolution = compiler.resolution;
+ DartType type = element.computeType(resolution);
+ List<ConstantExpression> metadata = <ConstantExpression>[];
+ for (MetadataAnnotation annotation in element.implementation.metadata) {
+ annotation.ensureResolved(compiler.resolution);
+ metadata.add(annotation.constant);
+ }
+
+ DartType lookup(String name) {
+ Element e = element.buildScope().lookup(name);
+ if (e == null) return null;
+ if (e is! ClassElement) return null;
+ ClassElement cls = e;
+ cls.ensureResolved(compiler.resolution);
+ return cls.thisType;
+ }
+
+ return ofFieldLoad(element, type, metadata, lookup, compiler,
+ isJsInterop: compiler.backend.isJsInterop(element));
+ }
+
+ static NativeBehavior ofFieldLoad(
+ Spannable spannable,
+ DartType type,
+ List<ConstantExpression> metadata,
+ TypeLookup lookupType,
+ Compiler compiler,
+ {bool isJsInterop}) {
Resolution resolution = compiler.resolution;
- DartType type = field.computeType(resolution);
var behavior = new NativeBehavior();
- bool isInterop = compiler.backend.isJsInterop(field);
// TODO(sigmund,sra): consider doing something better for numeric types.
behavior.typesReturned.add(
- !isInterop || compiler.options.trustJSInteropTypeAnnotations
+ !isJsInterop || compiler.options.trustJSInteropTypeAnnotations
? type
: const DynamicType());
// Declared types are nullable.
behavior.typesReturned.add(resolution.coreTypes.nullType);
behavior._capture(type, resolution,
- isInterop: isInterop, compiler: compiler);
- behavior._overrideWithAnnotations(field, compiler);
+ isInterop: isJsInterop, compiler: compiler);
+ behavior._overrideWithAnnotations(
+ spannable, metadata, lookupType, compiler);
return behavior;
}
- static NativeBehavior ofFieldStore(MemberElement field, Compiler compiler) {
- Resolution resolution = compiler.resolution;
+ static NativeBehavior ofFieldElementStore(
+ MemberElement field, Resolution resolution) {
DartType type = field.computeType(resolution);
+ return ofFieldStore(type, resolution);
+ }
+
+ static NativeBehavior ofFieldStore(DartType type, Resolution resolution) {
var behavior = new NativeBehavior();
behavior._escape(type, resolution);
// We don't override the default behaviour - the annotations apply to
@@ -743,23 +859,18 @@ class NativeBehavior {
return behavior;
}
- void _overrideWithAnnotations(Element element, Compiler compiler) {
- if (element.implementation.metadata.isEmpty) return;
-
- DartType lookup(String name) {
- Element e = element.buildScope().lookup(name);
- if (e == null) return null;
- if (e is! ClassElement) return null;
- ClassElement cls = e;
- cls.ensureResolved(compiler.resolution);
- return cls.thisType;
- }
+ void _overrideWithAnnotations(
+ Spannable spannable,
+ Iterable<ConstantExpression> metadata,
+ TypeLookup lookupType,
+ Compiler compiler) {
+ if (metadata.isEmpty) return;
NativeEnqueuer enqueuer = compiler.enqueuer.resolution.nativeEnqueuer;
- var creates =
- _collect(element, compiler, enqueuer.annotationCreatesClass, lookup);
- var returns =
- _collect(element, compiler, enqueuer.annotationReturnsClass, lookup);
+ var creates = _collect(spannable, metadata, compiler,
+ enqueuer.annotationCreatesClass, lookupType);
+ var returns = _collect(spannable, metadata, compiler,
+ enqueuer.annotationReturnsClass, lookupType);
if (creates != null) {
typesInstantiated
@@ -778,14 +889,12 @@ class NativeBehavior {
* [annotationClass].
* Returns `null` if no constraints.
*/
- static _collect(Element element, Compiler compiler, Element annotationClass,
- lookup(str)) {
+ static _collect(Spannable spannable, Iterable<ConstantExpression> metadata,
+ Compiler compiler, Element annotationClass, TypeLookup lookupType) {
DiagnosticReporter reporter = compiler.reporter;
var types = null;
- for (MetadataAnnotation annotation in element.implementation.metadata) {
- annotation.ensureResolved(compiler.resolution);
- ConstantValue value =
- compiler.constants.getConstantValue(annotation.constant);
+ for (ConstantExpression constant in metadata) {
+ ConstantValue value = compiler.constants.getConstantValue(constant);
if (!value.isConstructedObject) continue;
ConstructedConstantValue constructedObject = value;
if (constructedObject.type.element != annotationClass) continue;
@@ -793,14 +902,13 @@ class NativeBehavior {
Iterable<ConstantValue> fields = constructedObject.fields.values;
// TODO(sra): Better validation of the constant.
if (fields.length != 1 || !fields.single.isString) {
- reporter.internalError(
- annotation, 'Annotations needs one string: ${annotation.node}');
+ reporter.internalError(spannable,
+ 'Annotations needs one string: ${constant.toStructuredText()}');
}
StringConstantValue specStringConstant = fields.single;
String specString = specStringConstant.toDartString().slowToString();
for (final typeString in specString.split('|')) {
- var type =
- _parseType(typeString, compiler.parsingContext, lookup, annotation);
+ var type = _parseType(typeString, spannable, reporter, lookupType);
if (types == null) types = [];
types.add(type);
}
@@ -874,34 +982,28 @@ class NativeBehavior {
}
}
- static dynamic _parseType(String typeString, ParsingContext parsing,
- lookup(name), locationNodeOrElement) {
- DiagnosticReporter reporter = parsing.reporter;
+ static dynamic /*DartType|SpecialType*/ _parseType(String typeString,
+ Spannable spannable, DiagnosticReporter reporter, TypeLookup lookupType) {
if (typeString == '=Object') return SpecialType.JsObject;
if (typeString == 'dynamic') {
return const DynamicType();
}
- var type = lookup(typeString);
+ var type = lookupType(typeString);
if (type != null) return type;
int index = typeString.indexOf('<');
if (index < 1) {
- reporter.reportErrorMessage(_errorNode(locationNodeOrElement, parsing),
- MessageKind.GENERIC, {'text': "Type '$typeString' not found."});
+ reporter.reportErrorMessage(spannable, MessageKind.GENERIC,
+ {'text': "Type '$typeString' not found."});
return const DynamicType();
}
- type = lookup(typeString.substring(0, index));
+ type = lookupType(typeString.substring(0, index));
if (type != null) {
// TODO(sra): Parse type parameters.
return type;
}
- reporter.reportErrorMessage(_errorNode(locationNodeOrElement, parsing),
- MessageKind.GENERIC, {'text': "Type '$typeString' not found."});
+ reporter.reportErrorMessage(spannable, MessageKind.GENERIC,
+ {'text': "Type '$typeString' not found."});
return const DynamicType();
}
-
- static _errorNode(locationNodeOrElement, ParsingContext parsing) {
- if (locationNodeOrElement is Node) return locationNodeOrElement;
- return locationNodeOrElement.parseNode(parsing);
- }
}
« no previous file with comments | « pkg/compiler/lib/src/js_backend/backend_helpers.dart ('k') | pkg/compiler/lib/src/native/enqueue.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698