| // Copyright (c) 2016, 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. |
| |
| library js_backend.serialization; |
| |
| import '../common/backend_api.dart' show BackendSerialization; |
| import '../elements/elements.dart'; |
| import '../elements/resolution_types.dart'; |
| import '../elements/types.dart'; |
| import '../js/js.dart' as js; |
| import '../native/native.dart'; |
| import '../resolution/resolution_strategy.dart'; |
| import '../serialization/keys.dart'; |
| import '../serialization/serialization.dart' |
| show DeserializerPlugin, ObjectDecoder, ObjectEncoder, SerializerPlugin; |
| import '../universe/side_effects.dart'; |
| import 'js_backend.dart'; |
| import 'native_data.dart'; |
| |
| const String _BACKEND_DATA_TAG = 'jsBackendData'; |
| const Key DART_TYPES_RETURNED = const Key('dartTypesReturned'); |
| const Key THIS_TYPES_RETURNED = const Key('thisTypesReturned'); |
| const Key SPECIAL_TYPES_RETURNED = const Key('specialTypesReturned'); |
| const Key DART_TYPES_INSTANTIATED = const Key('dartTypesInstantiated'); |
| const Key THIS_TYPES_INSTANTIATED = const Key('thisTypesInstantiated'); |
| const Key SPECIAL_TYPES_INSTANTIATED = const Key('specialTypesInstantiated'); |
| const Key CODE_TEMPLATE = const Key('codeTemplate'); |
| const Key SIDE_EFFECTS = const Key('sideEffects'); |
| const Key THROW_BEHAVIOR = const Key('throwBehavior'); |
| const Key IS_ALLOCATION = const Key('isAllocation'); |
| const Key USE_GVN = const Key('useGvn'); |
| |
| class JavaScriptBackendSerialization implements BackendSerialization { |
| final JavaScriptBackendSerializer serializer; |
| final JavaScriptBackendDeserializer deserializer; |
| |
| JavaScriptBackendSerialization(JavaScriptBackend backend) |
| : serializer = new JavaScriptBackendSerializer(backend), |
| deserializer = new JavaScriptBackendDeserializer(backend); |
| } |
| |
| const Key JS_INTEROP_LIBRARY_NAME = const Key('jsInteropLibraryName'); |
| const Key JS_INTEROP_CLASS_NAME = const Key('jsInteropClassName'); |
| const Key JS_INTEROP_MEMBER_NAME = const Key('jsInteropMemberName'); |
| const Key NATIVE_MEMBER_NAME = const Key('nativeMemberName'); |
| const Key NATIVE_CLASS_TAG_INFO = const Key('nativeClassTagInfo'); |
| const Key NATIVE_METHOD_BEHAVIOR = const Key('nativeMethodBehavior'); |
| const Key NATIVE_FIELD_LOAD_BEHAVIOR = const Key('nativeFieldLoadBehavior'); |
| const Key NATIVE_FIELD_STORE_BEHAVIOR = const Key('nativeFieldStoreBehavior'); |
| |
| class JavaScriptBackendSerializer implements SerializerPlugin { |
| final JavaScriptBackend _backend; |
| |
| JavaScriptBackendSerializer(this._backend); |
| |
| NativeBasicDataImpl get nativeBasicData => |
| _backend.compiler.frontendStrategy.nativeBasicData; |
| NativeDataBuilderImpl get nativeData => _backend.nativeDataBuilder; |
| |
| @override |
| void onElement(Element element, ObjectEncoder createEncoder(String tag)) { |
| ObjectEncoder encoder; |
| ObjectEncoder getEncoder() { |
| return encoder ??= createEncoder(_BACKEND_DATA_TAG); |
| } |
| |
| String jsInteropLibraryName = nativeData.jsInteropLibraries[element]; |
| if (jsInteropLibraryName != null) { |
| getEncoder().setString(JS_INTEROP_LIBRARY_NAME, jsInteropLibraryName); |
| } |
| String jsInteropClassName = nativeData.jsInteropClasses[element]; |
| if (jsInteropClassName != null) { |
| getEncoder().setString(JS_INTEROP_CLASS_NAME, jsInteropClassName); |
| } |
| String jsInteropMemberName = nativeData.jsInteropMembers[element]; |
| if (jsInteropMemberName != null) { |
| getEncoder().setString(JS_INTEROP_MEMBER_NAME, jsInteropMemberName); |
| } |
| String nativeMemberName = nativeData.nativeMemberName[element]; |
| if (nativeMemberName != null) { |
| getEncoder().setString(NATIVE_MEMBER_NAME, nativeMemberName); |
| } |
| NativeClassTag nativeClassTagInfo = |
| nativeBasicData.nativeClassTagInfo[element]; |
| if (nativeClassTagInfo != null) { |
| getEncoder().setString(NATIVE_CLASS_TAG_INFO, nativeClassTagInfo.text); |
| } |
| NativeBehavior nativeMethodBehavior = |
| nativeData.nativeMethodBehavior[element]; |
| if (nativeMethodBehavior != null) { |
| NativeBehaviorSerialization.serializeNativeBehavior(nativeMethodBehavior, |
| getEncoder().createObject(NATIVE_METHOD_BEHAVIOR)); |
| } |
| NativeBehavior nativeFieldLoadBehavior = |
| nativeData.nativeFieldLoadBehavior[element]; |
| if (nativeFieldLoadBehavior != null) { |
| NativeBehaviorSerialization.serializeNativeBehavior( |
| nativeFieldLoadBehavior, |
| getEncoder().createObject(NATIVE_FIELD_LOAD_BEHAVIOR)); |
| } |
| NativeBehavior nativeFieldStoreBehavior = |
| nativeData.nativeFieldStoreBehavior[element]; |
| if (nativeFieldStoreBehavior != null) { |
| NativeBehaviorSerialization.serializeNativeBehavior( |
| nativeFieldStoreBehavior, |
| getEncoder().createObject(NATIVE_FIELD_STORE_BEHAVIOR)); |
| } |
| } |
| |
| @override |
| void onData(covariant NativeBehavior behavior, ObjectEncoder encoder) { |
| NativeBehaviorSerialization.serializeNativeBehavior(behavior, encoder); |
| } |
| } |
| |
| class JavaScriptBackendDeserializer implements DeserializerPlugin { |
| final JavaScriptBackend _backend; |
| |
| JavaScriptBackendDeserializer(this._backend); |
| |
| NativeBasicDataBuilderImpl get nativeBasicData { |
| ResolutionFrontEndStrategy frontendStrategy = |
| _backend.compiler.frontendStrategy; |
| return frontendStrategy.nativeBasicDataBuilder; |
| } |
| |
| NativeDataBuilderImpl get nativeData => _backend.nativeDataBuilder; |
| |
| @override |
| void onElement(Element element, ObjectDecoder getDecoder(String tag)) { |
| ObjectDecoder decoder = getDecoder(_BACKEND_DATA_TAG); |
| if (decoder != null) { |
| if (element is LibraryElement) { |
| String jsInteropLibraryName = |
| decoder.getString(JS_INTEROP_LIBRARY_NAME, isOptional: true); |
| if (jsInteropLibraryName != null) { |
| nativeData.jsInteropLibraries[element] = jsInteropLibraryName; |
| } |
| } else if (element is ClassElement) { |
| String jsInteropClassName = |
| decoder.getString(JS_INTEROP_CLASS_NAME, isOptional: true); |
| if (jsInteropClassName != null) { |
| nativeData.jsInteropClasses[element] = jsInteropClassName; |
| } |
| String nativeClassTagInfo = |
| decoder.getString(NATIVE_CLASS_TAG_INFO, isOptional: true); |
| if (nativeClassTagInfo != null) { |
| nativeBasicData.nativeClassTagInfo[element] = |
| new NativeClassTag(nativeClassTagInfo); |
| } |
| } else if (element is MemberElement) { |
| String jsInteropMemberName = |
| decoder.getString(JS_INTEROP_MEMBER_NAME, isOptional: true); |
| if (jsInteropMemberName != null) { |
| nativeData.jsInteropMembers[element] = jsInteropMemberName; |
| } |
| String nativeMemberName = |
| decoder.getString(NATIVE_MEMBER_NAME, isOptional: true); |
| if (nativeMemberName != null) { |
| nativeData.nativeMemberName[element] = nativeMemberName; |
| } |
| |
| if (element is MethodElement) { |
| ObjectDecoder nativeMethodBehavior = |
| decoder.getObject(NATIVE_METHOD_BEHAVIOR, isOptional: true); |
| if (nativeMethodBehavior != null) { |
| nativeData.nativeMethodBehavior[element] = |
| NativeBehaviorSerialization |
| .deserializeNativeBehavior(nativeMethodBehavior); |
| } |
| } else if (element is FieldElement) { |
| ObjectDecoder nativeFieldLoadBehavior = |
| decoder.getObject(NATIVE_FIELD_LOAD_BEHAVIOR, isOptional: true); |
| if (nativeFieldLoadBehavior != null) { |
| nativeData.nativeFieldLoadBehavior[element] = |
| NativeBehaviorSerialization |
| .deserializeNativeBehavior(nativeFieldLoadBehavior); |
| } |
| ObjectDecoder nativeFieldStoreBehavior = |
| decoder.getObject(NATIVE_FIELD_STORE_BEHAVIOR, isOptional: true); |
| if (nativeFieldStoreBehavior != null) { |
| nativeData.nativeFieldStoreBehavior[element] = |
| NativeBehaviorSerialization |
| .deserializeNativeBehavior(nativeFieldStoreBehavior); |
| } |
| } |
| } |
| } |
| } |
| |
| @override |
| NativeBehavior onData(ObjectDecoder decoder) { |
| return NativeBehaviorSerialization.deserializeNativeBehavior(decoder); |
| } |
| } |
| |
| class NativeBehaviorSerialization { |
| static const int NORMAL_TYPE = 0; |
| static const int THIS_TYPE = 1; |
| static const int SPECIAL_TYPE = 2; |
| |
| static int getTypeKind(var type) { |
| if (type is DartType) { |
| // TODO(johnniwinther): Remove this when annotation are no longer resolved |
| // to this-types. |
| if (type is InterfaceType && |
| type.typeArguments.isNotEmpty && |
| type.typeArguments.first is TypeVariableType) { |
| return THIS_TYPE; |
| } |
| return NORMAL_TYPE; |
| } |
| return SPECIAL_TYPE; |
| } |
| |
| /// Returns a list of the non-this-type [ResolutionDartType]s in [types]. |
| static List<ResolutionDartType> filterDartTypes(List types) { |
| return types.where((type) => getTypeKind(type) == NORMAL_TYPE).toList(); |
| } |
| |
| // TODO(johnniwinther): Remove this when annotation are no longer resolved |
| // to this-types. |
| /// Returns a list of the classes of this-types in [types]. |
| static List<Element> filterThisTypes(List types) { |
| return types |
| .where((type) => getTypeKind(type) == THIS_TYPE) |
| .map((type) => type.element) |
| .toList(); |
| } |
| |
| /// Returns a list of the names of the [SpecialType]s in [types]. |
| static List<String> filterSpecialTypes(List types) { |
| return types.where((type) => getTypeKind(type) == SPECIAL_TYPE).map((t) { |
| SpecialType type = t; |
| return type.name; |
| }).toList(); |
| } |
| |
| static void serializeNativeBehavior( |
| NativeBehavior behavior, ObjectEncoder encoder) { |
| encoder.setTypes( |
| DART_TYPES_RETURNED, filterDartTypes(behavior.typesReturned)); |
| encoder.setElements( |
| THIS_TYPES_RETURNED, filterThisTypes(behavior.typesReturned)); |
| encoder.setStrings( |
| SPECIAL_TYPES_RETURNED, filterSpecialTypes(behavior.typesReturned)); |
| |
| encoder.setTypes( |
| DART_TYPES_INSTANTIATED, filterDartTypes(behavior.typesInstantiated)); |
| encoder.setElements( |
| THIS_TYPES_INSTANTIATED, filterThisTypes(behavior.typesInstantiated)); |
| encoder.setStrings(SPECIAL_TYPES_INSTANTIATED, |
| filterSpecialTypes(behavior.typesInstantiated)); |
| |
| if (behavior.codeTemplateText != null) { |
| encoder.setString(CODE_TEMPLATE, behavior.codeTemplateText); |
| } |
| |
| encoder.setInt(SIDE_EFFECTS, behavior.sideEffects.flags); |
| encoder.setEnum(THROW_BEHAVIOR, behavior.throwBehavior); |
| encoder.setBool(IS_ALLOCATION, behavior.isAllocation); |
| encoder.setBool(USE_GVN, behavior.useGvn); |
| } |
| |
| static NativeBehavior deserializeNativeBehavior(ObjectDecoder decoder) { |
| SideEffects sideEffects = |
| new SideEffects.fromFlags(decoder.getInt(SIDE_EFFECTS)); |
| NativeBehavior behavior = new NativeBehavior.internal(sideEffects); |
| |
| behavior.typesReturned |
| .addAll(decoder.getTypes(DART_TYPES_RETURNED, isOptional: true)); |
| behavior.typesReturned.addAll(decoder |
| .getElements(THIS_TYPES_RETURNED, isOptional: true) |
| .map((dynamic element) => element.thisType) |
| .toList()); |
| behavior.typesReturned.addAll(decoder |
| .getStrings(SPECIAL_TYPES_RETURNED, isOptional: true) |
| .map(SpecialType.fromName)); |
| |
| behavior.typesInstantiated |
| .addAll(decoder.getTypes(DART_TYPES_INSTANTIATED, isOptional: true)); |
| behavior.typesInstantiated.addAll(decoder |
| .getElements(THIS_TYPES_INSTANTIATED, isOptional: true) |
| .map((dynamic element) => element.thisType) |
| .toList()); |
| behavior.typesInstantiated.addAll(decoder |
| .getStrings(SPECIAL_TYPES_INSTANTIATED, isOptional: true) |
| .map(SpecialType.fromName)); |
| |
| behavior.codeTemplateText = |
| decoder.getString(CODE_TEMPLATE, isOptional: true); |
| if (behavior.codeTemplateText != null) { |
| behavior.codeTemplate = js.js.parseForeignJS(behavior.codeTemplateText); |
| } |
| |
| behavior.throwBehavior = |
| decoder.getEnum(THROW_BEHAVIOR, NativeThrowBehavior.values); |
| behavior.isAllocation = decoder.getBool(IS_ALLOCATION); |
| behavior.useGvn = decoder.getBool(USE_GVN); |
| return behavior; |
| } |
| } |