Chromium Code Reviews| Index: pkg/compiler/lib/src/kernel/element_adapter.dart |
| diff --git a/pkg/compiler/lib/src/kernel/element_adapter.dart b/pkg/compiler/lib/src/kernel/element_adapter.dart |
| index 5d9f69dc51fb983c31f1a356f6d7add69d9eb84a..15d7e0e96e4d8de0194e17b23f5bc7550190bd2d 100644 |
| --- a/pkg/compiler/lib/src/kernel/element_adapter.dart |
| +++ b/pkg/compiler/lib/src/kernel/element_adapter.dart |
| @@ -4,15 +4,22 @@ |
| import 'package:kernel/ast.dart' as ir; |
| +import '../common.dart'; |
| +import '../common/names.dart'; |
| +import '../core_types.dart'; |
| import '../elements/elements.dart'; |
| import '../elements/entities.dart'; |
| import '../elements/types.dart'; |
| +import '../js_backend/backend_helpers.dart'; |
| import '../native/native.dart' as native; |
| import '../universe/call_structure.dart'; |
| import '../universe/selector.dart'; |
| /// Interface that translates between Kernel IR nodes and entities. |
| abstract class KernelElementAdapter { |
| + /// Access to the commonly used elements and types. |
| + CommonElements get commonElements; |
| + |
| /// Returns the [DartType] corresponding to [type]. |
| DartType getDartType(ir.DartType type); |
| @@ -52,7 +59,7 @@ abstract class KernelElementAdapter { |
| /// Returns the [Local] corresponding to the [node]. The node must be either |
| /// a [ir.FunctionDeclaration] or [ir.FunctionExpression]. |
| - Local getLocalFunction(ir.Node node); |
| + Local getLocalFunction(ir.TreeNode node); |
| /// Returns the [LibraryEntity] corresponding to the library [node]. |
| LibraryEntity getLibrary(ir.Library node); |
| @@ -104,3 +111,266 @@ enum ForeignKind { |
| JS_INTERCEPTOR_CONSTANT, |
| NONE, |
| } |
| + |
| +abstract class KernelElementAdapterMixin implements KernelElementAdapter { |
| + DiagnosticReporter get reporter; |
| + CommonElements get commonElements; |
| + |
| + LibraryEntity lookupLibrary(Uri uri); |
| + ClassEntity lookupClass(LibraryEntity library, String name); |
| + InterfaceType getRawType(ClassEntity cls); |
| + InterfaceType getThisType(ClassEntity cls); |
| + |
| + @override |
| + Name getName(ir.Name name) { |
| + return new Name( |
| + name.name, name.isPrivate ? getLibrary(name.library) : null); |
| + } |
| + |
| + @override |
| + CallStructure getCallStructure(ir.Arguments arguments) { |
| + int argumentCount = arguments.positional.length + arguments.named.length; |
| + List<String> namedArguments = arguments.named.map((e) => e.name).toList(); |
| + return new CallStructure(argumentCount, namedArguments); |
| + } |
| + |
| + @override |
| + Selector getSelector(ir.Expression node) { |
| + if (node is ir.PropertyGet) return getGetterSelector(node); |
| + if (node is ir.PropertySet) return getSetterSelector(node); |
| + if (node is ir.InvocationExpression) return getInvocationSelector(node); |
| + throw new SpannableAssertionFailure(CURRENT_ELEMENT_SPANNABLE, |
| + "Can only get the selector for a property get or an invocation: ${node}"); |
|
Johnni Winther
2017/01/30 14:40:00
Fixed in later CL (dartfmt doesn't handle this)
|
| + } |
| + |
| + Selector getInvocationSelector(ir.InvocationExpression invocation) { |
| + Name name = getName(invocation.name); |
| + SelectorKind kind; |
| + if (Elements.isOperatorName(invocation.name.name)) { |
| + if (name == Names.INDEX_NAME || name == Names.INDEX_SET_NAME) { |
| + kind = SelectorKind.INDEX; |
| + } else { |
| + kind = SelectorKind.OPERATOR; |
| + } |
| + } else { |
| + kind = SelectorKind.CALL; |
| + } |
| + |
| + CallStructure callStructure = getCallStructure(invocation.arguments); |
| + return new Selector(kind, name, callStructure); |
| + } |
| + |
| + Selector getGetterSelector(ir.PropertyGet getter) { |
| + ir.Name irName = getter.name; |
| + Name name = new Name( |
| + irName.name, irName.isPrivate ? getLibrary(irName.library) : null); |
| + return new Selector.getter(name); |
| + } |
| + |
| + Selector getSetterSelector(ir.PropertySet setter) { |
| + ir.Name irName = setter.name; |
| + Name name = new Name( |
| + irName.name, irName.isPrivate ? getLibrary(irName.library) : null); |
| + return new Selector.setter(name); |
| + } |
| + |
| + /// Returns `true` is [node] has a `@Native(...)` annotation. |
| + // TODO(johnniwinther): Cache this for later use. |
| + bool isNativeClass(ir.Class node) { |
| + for (ir.Expression annotation in node.annotations) { |
| + if (annotation is ir.ConstructorInvocation) { |
| + FunctionEntity target = getConstructor(annotation.target); |
| + if (target.enclosingClass == commonElements.nativeAnnotationClass) { |
| + return true; |
| + } |
| + } |
| + } |
| + return false; |
| + } |
| + |
| + /// Compute the kind of foreign helper function called by [node], if any. |
| + ForeignKind getForeignKind(ir.StaticInvocation node) { |
| + if (isForeignLibrary(node.target.enclosingLibrary)) { |
| + switch (node.target.name.name) { |
| + case BackendHelpers.JS: |
| + return ForeignKind.JS; |
| + case BackendHelpers.JS_BUILTIN: |
| + return ForeignKind.JS_BUILTIN; |
| + case BackendHelpers.JS_EMBEDDED_GLOBAL: |
| + return ForeignKind.JS_EMBEDDED_GLOBAL; |
| + case BackendHelpers.JS_INTERCEPTOR_CONSTANT: |
| + return ForeignKind.JS_INTERCEPTOR_CONSTANT; |
| + } |
| + } |
| + return ForeignKind.NONE; |
| + } |
| + |
| + /// Return `true` if [node] is the `dart:_foreign_helper` library. |
| + bool isForeignLibrary(ir.Library node) { |
| + return node.importUri == BackendHelpers.DART_FOREIGN_HELPER; |
| + } |
| + |
| + /// Looks up [typeName] for use in the spec-string of a `JS` called. |
| + // TODO(johnniwinther): Use this in [native.NativeBehavior] instead of calling |
| + // the `ForeignResolver`. |
| + // TODO(johnniwinther): Cache the result to avoid redundant lookups? |
| + native.TypeLookup typeLookup({bool resolveAsRaw: true}) { |
| + return (String typeName) { |
| + DartType findIn(Uri uri) { |
| + LibraryEntity library = lookupLibrary(uri); |
| + if (library != null) { |
| + ClassEntity cls = lookupClass(library, typeName); |
| + if (cls != null) { |
| + // TODO(johnniwinther): Align semantics. |
| + return resolveAsRaw ? getRawType(cls) : getThisType(cls); |
| + } |
| + } |
| + return null; |
| + } |
| + |
| + DartType type = findIn(Uris.dart_core); |
| + type ??= findIn(BackendHelpers.DART_JS_HELPER); |
| + type ??= findIn(BackendHelpers.DART_INTERCEPTORS); |
| + type ??= findIn(BackendHelpers.DART_ISOLATE_HELPER); |
| + type ??= findIn(Uris.dart_collection); |
| + type ??= findIn(Uris.dart_html); |
| + type ??= findIn(Uris.dart_svg); |
| + type ??= findIn(Uris.dart_web_audio); |
| + type ??= findIn(Uris.dart_web_gl); |
| + return type; |
| + }; |
| + } |
| + |
| + String _getStringArgument(ir.StaticInvocation node, int index) { |
| + return node.arguments.positional[index].accept(new Stringifier()); |
| + } |
| + |
| + /// Computes the [native.NativeBehavior] for a call to the [JS] function. |
| + // TODO(johnniwinther): Cache this for later use. |
| + native.NativeBehavior getNativeBehaviorForJsCall(ir.StaticInvocation node) { |
| + if (node.arguments.positional.length < 2 || |
| + node.arguments.named.isNotEmpty) { |
| + reporter.reportErrorMessage( |
| + CURRENT_ELEMENT_SPANNABLE, MessageKind.WRONG_ARGUMENT_FOR_JS); |
| + return new native.NativeBehavior(); |
| + } |
| + String specString = _getStringArgument(node, 0); |
| + if (specString == null) { |
| + reporter.reportErrorMessage( |
| + CURRENT_ELEMENT_SPANNABLE, MessageKind.WRONG_ARGUMENT_FOR_JS_FIRST); |
| + return new native.NativeBehavior(); |
| + } |
| + |
| + String codeString = _getStringArgument(node, 1); |
| + if (codeString == null) { |
| + reporter.reportErrorMessage( |
| + CURRENT_ELEMENT_SPANNABLE, MessageKind.WRONG_ARGUMENT_FOR_JS_SECOND); |
| + return new native.NativeBehavior(); |
| + } |
| + |
| + return native.NativeBehavior.ofJsCall( |
| + specString, |
| + codeString, |
| + typeLookup(resolveAsRaw: true), |
| + CURRENT_ELEMENT_SPANNABLE, |
| + reporter, |
| + commonElements); |
| + } |
| + |
| + /// Computes the [native.NativeBehavior] for a call to the [JS_BUILTIN] |
| + /// function. |
| + // TODO(johnniwinther): Cache this for later use. |
| + native.NativeBehavior getNativeBehaviorForJsBuiltinCall( |
| + ir.StaticInvocation node) { |
| + if (node.arguments.positional.length < 1) { |
| + reporter.internalError( |
| + CURRENT_ELEMENT_SPANNABLE, "JS builtin expression has no type."); |
| + return new native.NativeBehavior(); |
| + } |
| + if (node.arguments.positional.length < 2) { |
| + reporter.internalError( |
| + CURRENT_ELEMENT_SPANNABLE, "JS builtin is missing name."); |
| + return new native.NativeBehavior(); |
| + } |
| + String specString = _getStringArgument(node, 0); |
| + if (specString == null) { |
| + reporter.internalError( |
| + CURRENT_ELEMENT_SPANNABLE, "Unexpected first argument."); |
| + return new native.NativeBehavior(); |
| + } |
| + return native.NativeBehavior.ofJsBuiltinCall( |
| + specString, |
| + typeLookup(resolveAsRaw: true), |
| + CURRENT_ELEMENT_SPANNABLE, |
| + reporter, |
| + commonElements); |
| + } |
| + |
| + /// Computes the [native.NativeBehavior] for a call to the |
| + /// [JS_EMBEDDED_GLOBAL] function. |
| + // TODO(johnniwinther): Cache this for later use. |
| + native.NativeBehavior getNativeBehaviorForJsEmbeddedGlobalCall( |
| + ir.StaticInvocation node) { |
| + if (node.arguments.positional.length < 1) { |
| + reporter.internalError(CURRENT_ELEMENT_SPANNABLE, |
| + "JS embedded global expression has no type."); |
| + return new native.NativeBehavior(); |
| + } |
| + if (node.arguments.positional.length < 2) { |
| + reporter.internalError( |
| + CURRENT_ELEMENT_SPANNABLE, "JS embedded global is missing name."); |
| + return new native.NativeBehavior(); |
| + } |
| + if (node.arguments.positional.length > 2 || |
| + node.arguments.named.isNotEmpty) { |
| + reporter.internalError(CURRENT_ELEMENT_SPANNABLE, |
| + "JS embedded global has more than 2 arguments."); |
| + return new native.NativeBehavior(); |
| + } |
| + String specString = _getStringArgument(node, 0); |
| + if (specString == null) { |
| + reporter.internalError( |
| + CURRENT_ELEMENT_SPANNABLE, "Unexpected first argument."); |
| + return new native.NativeBehavior(); |
| + } |
| + return native.NativeBehavior.ofJsEmbeddedGlobalCall( |
| + specString, |
| + typeLookup(resolveAsRaw: true), |
| + CURRENT_ELEMENT_SPANNABLE, |
| + reporter, |
| + commonElements); |
| + } |
| + |
| + /// Computes the [InterfaceType] referenced by a call to the |
| + /// [JS_INTERCEPTOR_CONSTANT] function, if any. |
| + InterfaceType getInterfaceTypeForJsInterceptorCall(ir.StaticInvocation node) { |
| + if (node.arguments.positional.length != 1 || |
| + node.arguments.named.isNotEmpty) { |
| + reporter.reportErrorMessage(CURRENT_ELEMENT_SPANNABLE, |
| + MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT); |
| + } |
| + ir.Node argument = node.arguments.positional.first; |
| + if (argument is ir.TypeLiteral && argument.type is ir.InterfaceType) { |
| + return getInterfaceType(argument.type); |
| + } |
| + return null; |
| + } |
| +} |
| + |
| +/// Visitor that converts string literals and concatenations of string literals |
| +/// into the string value. |
| +class Stringifier extends ir.ExpressionVisitor<String> { |
| + @override |
| + String visitStringLiteral(ir.StringLiteral node) => node.value; |
| + |
| + @override |
| + String visitStringConcatenation(ir.StringConcatenation node) { |
| + StringBuffer sb = new StringBuffer(); |
| + for (ir.Expression expression in node.expressions) { |
| + String value = expression.accept(this); |
| + if (value == null) return null; |
| + sb.write(value); |
| + } |
| + return sb.toString(); |
| + } |
| +} |