| // Copyright (c) 2012, 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; |
| |
| // ignore: implementation_imports |
| import 'package:js_ast/src/precedence.dart' as js show Precedence; |
| |
| import '../common/elements.dart'; |
| import '../constants/values.dart'; |
| import '../elements/entities.dart'; |
| import '../elements/types.dart' show DartType, InterfaceType; |
| import '../io/source_information.dart'; |
| import '../js/js.dart' as js; |
| import '../js_backend/backend.dart'; |
| import '../js_backend/codegen_inputs.dart'; |
| import '../js_backend/namer.dart' |
| show AsyncName, Namer, operatorNameToIdentifier, StringBackedName; |
| import '../js_backend/deferred_holder_expression.dart' |
| show DeferredHolderExpression; |
| import '../js_backend/string_reference.dart' show StringReference; |
| import '../js_backend/type_reference.dart' show TypeReference; |
| import '../js_emitter/js_emitter.dart' show Emitter; |
| import '../js_model/elements.dart'; |
| import '../native/behavior.dart'; |
| import '../serialization/serialization.dart'; |
| import '../universe/feature.dart'; |
| import '../universe/resource_identifier.dart' show ResourceIdentifier; |
| import '../universe/selector.dart'; |
| import '../universe/use.dart' show ConstantUse, DynamicUse, StaticUse, TypeUse; |
| import '../universe/world_impact.dart' show WorldImpact, WorldImpactBuilderImpl; |
| import '../util/enumset.dart'; |
| import '../util/util.dart'; |
| |
| class CodegenImpact extends WorldImpact { |
| const CodegenImpact(); |
| |
| factory CodegenImpact.readFromDataSource(DataSourceReader source) = |
| _CodegenImpact.readFromDataSource; |
| |
| void writeToDataSink(DataSinkWriter sink) { |
| throw UnsupportedError('CodegenImpact.writeToDataSink'); |
| } |
| |
| Iterable<(DartType, DartType)> get typeVariableBoundsSubtypeChecks { |
| return const <(DartType, DartType)>[]; |
| } |
| |
| Iterable<String> get constSymbols => const <String>[]; |
| |
| Iterable<Set<ClassEntity>> get specializedGetInterceptors { |
| return const <Set<ClassEntity>>[]; |
| } |
| |
| bool get usesInterceptor => false; |
| |
| Iterable<AsyncMarker> get asyncMarkers => const <AsyncMarker>[]; |
| |
| Iterable<GenericInstantiation> get genericInstantiations => |
| const <GenericInstantiation>[]; |
| |
| Iterable<NativeBehavior> get nativeBehaviors => const []; |
| |
| Iterable<FunctionEntity> get nativeMethods => const []; |
| |
| Iterable<Selector> get oneShotInterceptors => const []; |
| } |
| |
| class _CodegenImpact extends WorldImpactBuilderImpl implements CodegenImpact { |
| static const String tag = 'codegen-impact'; |
| |
| Set<(DartType, DartType)>? _typeVariableBoundsSubtypeChecks; |
| Set<String>? _constSymbols; |
| List<Set<ClassEntity>>? _specializedGetInterceptors; |
| bool _usesInterceptor = false; |
| EnumSet<AsyncMarker> _asyncMarkers = EnumSet.empty(); |
| Set<GenericInstantiation>? _genericInstantiations; |
| List<NativeBehavior>? _nativeBehaviors; |
| Set<FunctionEntity>? _nativeMethods; |
| Set<Selector>? _oneShotInterceptors; |
| |
| _CodegenImpact(MemberEntity super.member); |
| |
| _CodegenImpact.internal( |
| MemberEntity member, |
| super.dynamicUses, |
| super.staticUses, |
| super.typeUses, |
| super.constantUses, |
| this._typeVariableBoundsSubtypeChecks, |
| this._constSymbols, |
| this._specializedGetInterceptors, |
| this._usesInterceptor, |
| this._asyncMarkers, |
| this._genericInstantiations, |
| this._nativeBehaviors, |
| this._nativeMethods, |
| this._oneShotInterceptors, |
| ) : super.internal(member: member); |
| |
| factory _CodegenImpact.readFromDataSource(DataSourceReader source) { |
| source.begin(tag); |
| MemberEntity member = source.readMember(); |
| final dynamicUses = |
| source |
| .readListOrNull(() => DynamicUse.readFromDataSource(source)) |
| ?.toSet(); |
| final staticUses = |
| source |
| .readListOrNull(() => StaticUse.readFromDataSource(source)) |
| ?.toSet(); |
| final typeUses = |
| source |
| .readListOrNull(() => TypeUse.readFromDataSource(source)) |
| ?.toSet(); |
| final constantUses = |
| source |
| .readListOrNull(() => ConstantUse.readFromDataSource(source)) |
| ?.toSet(); |
| final typeVariableBoundsSubtypeChecks = |
| source.readListOrNull(() { |
| return (source.readDartType(), source.readDartType()); |
| })?.toSet(); |
| final constSymbols = source.readStringsOrNull()?.toSet(); |
| final specializedGetInterceptors = source.readListOrNull(() { |
| return source.readClasses().toSet(); |
| }); |
| bool usesInterceptor = source.readBool(); |
| final asyncMarkersValue = source.readInt(); |
| final asyncMarkers = EnumSet<AsyncMarker>.fromRawBits(asyncMarkersValue); |
| final genericInstantiations = |
| source |
| .readListOrNull( |
| () => GenericInstantiation.readFromDataSource(source), |
| ) |
| ?.toSet(); |
| final nativeBehaviors = source.readListOrNull( |
| () => NativeBehavior.readFromDataSource(source), |
| ); |
| final nativeMethods = source.readMembersOrNull<FunctionEntity>()?.toSet(); |
| final oneShotInterceptors = |
| source |
| .readListOrNull(() => Selector.readFromDataSource(source)) |
| ?.toSet(); |
| source.end(tag); |
| return _CodegenImpact.internal( |
| member, |
| dynamicUses, |
| staticUses, |
| typeUses, |
| constantUses, |
| typeVariableBoundsSubtypeChecks, |
| constSymbols, |
| specializedGetInterceptors, |
| usesInterceptor, |
| asyncMarkers, |
| genericInstantiations, |
| nativeBehaviors, |
| nativeMethods, |
| oneShotInterceptors, |
| ); |
| } |
| |
| @override |
| void writeToDataSink(DataSinkWriter sink) { |
| sink.begin(tag); |
| sink.writeMember(member); |
| sink.writeList(dynamicUses, (DynamicUse use) => use.writeToDataSink(sink)); |
| sink.writeList(staticUses, (StaticUse use) => use.writeToDataSink(sink)); |
| sink.writeList(typeUses, (TypeUse use) => use.writeToDataSink(sink)); |
| sink.writeList( |
| constantUses, |
| (ConstantUse use) => use.writeToDataSink(sink), |
| ); |
| sink.writeListOrNull<(DartType, DartType)>( |
| _typeVariableBoundsSubtypeChecks, |
| (pair) { |
| sink.writeDartType(pair.$1); |
| sink.writeDartType(pair.$2); |
| }, |
| ); |
| sink.writeStringsOrNull(_constSymbols); |
| sink.writeListOrNull(_specializedGetInterceptors, sink.writeClasses); |
| sink.writeBool(_usesInterceptor); |
| sink.writeInt(_asyncMarkers.mask.bits); |
| sink.writeListOrNull( |
| _genericInstantiations, |
| (GenericInstantiation instantiation) => |
| instantiation.writeToDataSink(sink), |
| ); |
| sink.writeListOrNull( |
| _nativeBehaviors, |
| (NativeBehavior behavior) => behavior.writeToDataSink(sink), |
| ); |
| sink.writeMembersOrNull(_nativeMethods); |
| sink.writeListOrNull( |
| _oneShotInterceptors, |
| (Selector selector) => selector.writeToDataSink(sink), |
| ); |
| sink.end(tag); |
| } |
| |
| @override |
| MemberEntity get member => super.member!; |
| |
| void registerTypeVariableBoundsSubtypeCheck( |
| DartType subtype, |
| DartType supertype, |
| ) { |
| (_typeVariableBoundsSubtypeChecks ??= {}).add((subtype, supertype)); |
| } |
| |
| @override |
| Iterable<(DartType, DartType)> get typeVariableBoundsSubtypeChecks { |
| return _typeVariableBoundsSubtypeChecks ?? const {}; |
| } |
| |
| void registerConstSymbol(String name) { |
| (_constSymbols ??= {}).add(name); |
| } |
| |
| @override |
| Iterable<String> get constSymbols { |
| return _constSymbols ?? const []; |
| } |
| |
| void registerSpecializedGetInterceptor(Set<ClassEntity> classes) { |
| (_specializedGetInterceptors ??= []).add(classes); |
| } |
| |
| @override |
| Iterable<Set<ClassEntity>> get specializedGetInterceptors { |
| return _specializedGetInterceptors ?? const []; |
| } |
| |
| void registerUseInterceptor() { |
| _usesInterceptor = true; |
| } |
| |
| @override |
| bool get usesInterceptor => _usesInterceptor; |
| |
| void registerAsyncMarker(AsyncMarker asyncMarker) { |
| _asyncMarkers = _asyncMarkers.add(asyncMarker); |
| } |
| |
| @override |
| Iterable<AsyncMarker> get asyncMarkers { |
| return _asyncMarkers.isEmpty |
| ? const [] |
| : _asyncMarkers.iterable(AsyncMarker.values); |
| } |
| |
| void registerGenericInstantiation(GenericInstantiation instantiation) { |
| (_genericInstantiations ??= {}).add(instantiation); |
| } |
| |
| @override |
| Iterable<GenericInstantiation> get genericInstantiations { |
| return _genericInstantiations ?? const []; |
| } |
| |
| void registerNativeBehavior(NativeBehavior nativeBehavior) { |
| (_nativeBehaviors ??= []).add(nativeBehavior); |
| } |
| |
| @override |
| Iterable<NativeBehavior> get nativeBehaviors { |
| return _nativeBehaviors ?? const []; |
| } |
| |
| void registerNativeMethod(FunctionEntity function) { |
| (_nativeMethods ??= {}).add(function); |
| } |
| |
| @override |
| Iterable<FunctionEntity> get nativeMethods { |
| return _nativeMethods ?? const []; |
| } |
| |
| void registerOneShotInterceptor(Selector selector) { |
| (_oneShotInterceptors ??= {}).add(selector); |
| } |
| |
| @override |
| Iterable<Selector> get oneShotInterceptors { |
| return _oneShotInterceptors ?? const []; |
| } |
| |
| @override |
| String toString() { |
| StringBuffer sb = StringBuffer(); |
| sb.write('CodegenImpact:'); |
| WorldImpact.printOn(sb, this); |
| |
| void add(String title, Iterable<Object?> iterable) { |
| if (iterable.isNotEmpty) { |
| sb.write('\n $title:'); |
| for (var e in iterable) { |
| sb.write('\n $e'); |
| } |
| } |
| } |
| |
| add('typeVariableBoundsSubtypeChecks', typeVariableBoundsSubtypeChecks); |
| add('constSymbols', constSymbols); |
| add('specializedGetInterceptors', specializedGetInterceptors); |
| if (usesInterceptor) { |
| sb.write('\n usesInterceptor: true'); |
| } |
| add('asyncMarkers', asyncMarkers); |
| add('genericInstantiations', genericInstantiations); |
| add('nativeBehaviors', nativeBehaviors); |
| add('nativeMethods', nativeMethods); |
| add('oneShotInterceptors', oneShotInterceptors); |
| |
| return sb.toString(); |
| } |
| } |
| |
| // TODO(johnniwinther): Split this class into interface and implementation. |
| // TODO(johnniwinther): Move this implementation to the JS backend. |
| class CodegenRegistry { |
| final ElementEnvironment _elementEnvironment; |
| final MemberEntity _currentElement; |
| final _CodegenImpact _worldImpact; |
| late final List<ModularName> _names = []; |
| late final List<ModularExpression> _expressions = []; |
| |
| CodegenRegistry(this._elementEnvironment, this._currentElement) |
| : _worldImpact = _CodegenImpact(_currentElement); |
| |
| @override |
| String toString() => 'CodegenRegistry for $_currentElement'; |
| |
| @Deprecated("Use StaticUse for precise registration of statically known use") |
| void registerInstantiatedClass(ClassEntity element) { |
| registerInstantiation(_elementEnvironment.getRawType(element)); |
| } |
| |
| void registerStaticUse(StaticUse staticUse) { |
| _worldImpact.registerStaticUse(staticUse); |
| } |
| |
| void registerDynamicUse(DynamicUse dynamicUse) { |
| _worldImpact.registerDynamicUse(dynamicUse); |
| } |
| |
| void registerTypeUse(TypeUse typeUse) { |
| _worldImpact.registerTypeUse(typeUse); |
| } |
| |
| void registerConstantUse(ConstantUse constantUse) { |
| _worldImpact.registerConstantUse(constantUse); |
| } |
| |
| void registerTypeVariableBoundsSubtypeCheck( |
| DartType subtype, |
| DartType supertype, |
| ) { |
| _worldImpact.registerTypeVariableBoundsSubtypeCheck(subtype, supertype); |
| } |
| |
| void registerInstantiatedClosure(FunctionEntity element) { |
| _worldImpact.registerStaticUse(StaticUse.callMethod(element)); |
| } |
| |
| void registerConstSymbol(String name) { |
| _worldImpact.registerConstSymbol(name); |
| } |
| |
| void registerSpecializedGetInterceptor(Set<ClassEntity> classes) { |
| _worldImpact.registerSpecializedGetInterceptor(classes); |
| } |
| |
| void registerOneShotInterceptor(Selector selector) { |
| _worldImpact.registerOneShotInterceptor(selector); |
| } |
| |
| void registerUseInterceptor() { |
| _worldImpact.registerUseInterceptor(); |
| } |
| |
| void registerInstantiation(InterfaceType type) { |
| registerTypeUse(TypeUse.instantiation(type)); |
| } |
| |
| void registerAsyncMarker(AsyncMarker asyncMarker) { |
| _worldImpact.registerAsyncMarker(asyncMarker); |
| } |
| |
| void registerGenericInstantiation(GenericInstantiation instantiation) { |
| _worldImpact.registerGenericInstantiation(instantiation); |
| } |
| |
| void registerNativeBehavior(NativeBehavior nativeBehavior) { |
| _worldImpact.registerNativeBehavior(nativeBehavior); |
| } |
| |
| void registerNativeMethod(FunctionEntity function) { |
| _worldImpact.registerNativeMethod(function); |
| } |
| |
| void registerModularName(covariant ModularName name) { |
| _names.add(name); |
| } |
| |
| void registerModularExpression(covariant ModularExpression expression) { |
| _expressions.add(expression); |
| } |
| |
| CodegenResult close(js.Fun? code) { |
| return CodegenResult( |
| code, |
| _worldImpact, |
| js.DeferredExpressionData( |
| _names.isEmpty ? const [] : _names, |
| _expressions.isEmpty ? const [] : _expressions, |
| ), |
| ); |
| } |
| } |
| |
| /// Code generation results computed on-demand. |
| /// |
| /// This is used in the non-modular codegen enqueuer driving code generation. |
| class OnDemandCodegenResults implements CodegenResults { |
| @override |
| final CodegenInputs codegenInputs; |
| final FunctionCompiler _functionCompiler; |
| |
| OnDemandCodegenResults(this.codegenInputs, this._functionCompiler); |
| |
| @override |
| ({CodegenResult result, bool isGenerated}) getCodegenResults( |
| MemberEntity member, |
| ) { |
| return (result: _functionCompiler.compile(member), isGenerated: true); |
| } |
| } |
| |
| /// The code generation result for a single [MemberEntity]. |
| class CodegenResult { |
| static const String tag = 'codegen-result'; |
| |
| final js.Fun? code; |
| final CodegenImpact impact; |
| final js.DeferredExpressionData deferredExpressionData; |
| |
| CodegenResult(this.code, this.impact, this.deferredExpressionData); |
| |
| /// Reads a [CodegenResult] object from [source]. |
| factory CodegenResult.readFromDataSource(DataSourceReader source) { |
| source.begin(tag); |
| js.Fun? code = source.readJsNodeOrNull() as js.Fun?; |
| CodegenImpact impact = CodegenImpact.readFromDataSource(source); |
| final deferredExpressionData = js |
| .DeferredExpressionRegistry.readDataFromDataSource(source); |
| source.end(tag); |
| if (code != null) { |
| code = code.withAnnotation(deferredExpressionData) as js.Fun; |
| } |
| return CodegenResult(code, impact, deferredExpressionData); |
| } |
| |
| /// Writes the [CodegenResult] object to [sink]. |
| void writeToDataSink(DataSinkWriter sink) { |
| sink.begin(tag); |
| final registry = js.DeferredExpressionRegistry(); |
| sink.withDeferredExpressionRegistry( |
| registry, |
| () => sink.writeJsNodeOrNull(code), |
| ); |
| impact.writeToDataSink(sink); |
| registry.writeToDataSink(sink); |
| sink.end(tag); |
| } |
| |
| void applyModularState(Namer namer, Emitter emitter) { |
| final Set<ModularName> updated = Set.identity(); |
| for (ModularName name in deferredExpressionData.modularNames) { |
| if (!updated.add(name)) continue; |
| switch (name.kind) { |
| case ModularNameKind.rtiField: |
| name.value = namer.rtiFieldJsName; |
| break; |
| case ModularNameKind.className: |
| name.value = namer.className(name.data as ClassEntity); |
| break; |
| case ModularNameKind.aliasedSuperMember: |
| name.value = namer.aliasedSuperMemberPropertyName( |
| name.data as MemberEntity, |
| ); |
| break; |
| case ModularNameKind.staticClosure: |
| name.value = namer.staticClosureName(name.data as FunctionEntity); |
| break; |
| case ModularNameKind.methodProperty: |
| name.value = namer.methodPropertyName(name.data as FunctionEntity); |
| break; |
| case ModularNameKind.operatorIs: |
| name.value = namer.operatorIs(name.data as ClassEntity); |
| break; |
| case ModularNameKind.instanceMethod: |
| name.value = namer.instanceMethodName(name.data as FunctionEntity); |
| break; |
| case ModularNameKind.instanceField: |
| name.value = namer.instanceFieldPropertyName( |
| name.data as FieldEntity, |
| ); |
| break; |
| case ModularNameKind.invocation: |
| name.value = namer.invocationName(name.data as Selector); |
| break; |
| case ModularNameKind.lazyInitializer: |
| name.value = namer.lazyInitializerName(name.data as FieldEntity); |
| break; |
| case ModularNameKind.globalPropertyNameForClass: |
| name.value = namer.globalPropertyNameForClass( |
| name.data as ClassEntity, |
| ); |
| break; |
| case ModularNameKind.globalPropertyNameForMember: |
| name.value = namer.globalPropertyNameForMember( |
| name.data as MemberEntity, |
| ); |
| break; |
| case ModularNameKind.globalNameForInterfaceTypeVariable: |
| name.value = namer.globalNameForInterfaceTypeVariable( |
| name.data as TypeVariableEntity, |
| ); |
| break; |
| case ModularNameKind.nameForGetInterceptor: |
| name.value = namer.nameForGetInterceptor(name.set!); |
| break; |
| case ModularNameKind.nameForOneShotInterceptor: |
| name.value = namer.nameForOneShotInterceptor( |
| name.data as Selector, |
| name.set!, |
| ); |
| break; |
| case ModularNameKind.asName: |
| name.value = namer.asName(name.data as String); |
| break; |
| } |
| } |
| for (ModularExpression expression |
| in deferredExpressionData.modularExpressions) { |
| switch (expression.kind) { |
| case ModularExpressionKind.constant: |
| expression.value = emitter |
| .constantReference(expression.data as ConstantValue) |
| .withSourceInformation(expression.sourceInformation); |
| break; |
| case ModularExpressionKind.embeddedGlobalAccess: |
| expression.value = emitter |
| .generateEmbeddedGlobalAccess(expression.data as String) |
| .withSourceInformation(expression.sourceInformation); |
| break; |
| } |
| } |
| } |
| |
| @override |
| String toString() { |
| StringBuffer sb = StringBuffer(); |
| sb.write('CodegenResult(code='); |
| sb.write(code != null ? js.DebugPrint(code!) : '<null>,'); |
| sb.write('impact=$impact,'); |
| sb.write('modularNames=${deferredExpressionData.modularNames},'); |
| sb.write('modularExpressions=${deferredExpressionData.modularExpressions}'); |
| sb.write(')'); |
| return sb.toString(); |
| } |
| } |
| |
| enum ModularExpressionKind { constant, embeddedGlobalAccess } |
| |
| class ModularExpression extends js.DeferredExpression |
| implements js.AstContainer { |
| static const String tag = 'modular-expression'; |
| |
| final ModularExpressionKind kind; |
| final Object data; |
| js.Expression? _value; |
| |
| ModularExpression(this.kind, this.data); |
| |
| factory ModularExpression.readFromDataSource(DataSourceReader source) { |
| source.begin(tag); |
| ModularExpressionKind kind = source.readEnum(ModularExpressionKind.values); |
| Object data; |
| switch (kind) { |
| case ModularExpressionKind.constant: |
| data = source.readConstant(); |
| break; |
| case ModularExpressionKind.embeddedGlobalAccess: |
| data = source.readString(); |
| break; |
| } |
| source.end(tag); |
| return ModularExpression(kind, data); |
| } |
| |
| void writeToDataSink(DataSinkWriter sink) { |
| sink.begin(tag); |
| sink.writeEnum(kind); |
| switch (kind) { |
| case ModularExpressionKind.constant: |
| sink.writeConstant(data as ConstantValue); |
| break; |
| case ModularExpressionKind.embeddedGlobalAccess: |
| sink.writeString(data as String); |
| break; |
| } |
| sink.end(tag); |
| } |
| |
| @override |
| bool get isFinalized => _value != null; |
| |
| @override |
| js.Expression get value { |
| return _value!; |
| } |
| |
| set value(js.Expression node) { |
| assert(!isFinalized); |
| _value = node.withSourceInformation(sourceInformation); |
| } |
| |
| @override |
| js.Precedence get precedenceLevel => |
| _value?.precedenceLevel ?? js.Precedence.primary; |
| |
| @override |
| Iterable<js.Node> get containedNodes { |
| return _value != null ? [_value!] : const []; |
| } |
| |
| @override |
| int get hashCode { |
| return Hashing.objectsHash(kind, data); |
| } |
| |
| @override |
| bool operator ==(Object other) { |
| if (identical(this, other)) return true; |
| return other is ModularExpression && |
| kind == other.kind && |
| data == other.data; |
| } |
| |
| @override |
| String toString() { |
| StringBuffer sb = StringBuffer(); |
| sb.write('ModularExpression(kind=$kind,data='); |
| if (data is ConstantValue) { |
| sb.write((data as ConstantValue).toStructuredText(null)); |
| } else { |
| sb.write(data); |
| } |
| sb.write(',value=$_value)'); |
| return sb.toString(); |
| } |
| |
| @override |
| String nonfinalizedDebugText() { |
| switch (kind) { |
| case ModularExpressionKind.constant: |
| return 'ModularExpression"<constant>"'; |
| case ModularExpressionKind.embeddedGlobalAccess: |
| return 'ModularExpression"init.$data"'; |
| } |
| } |
| } |
| |
| enum JsNodeKind { |
| comment, |
| await, |
| regExpLiteral, |
| property, |
| methodDefinition, |
| objectInitializer, |
| arrayHole, |
| arrayInitializer, |
| parentheses, |
| modularName, |
| asyncName, |
| stringBackedName, |
| stringConcatenation, |
| literalNull, |
| literalNumber, |
| literalString, |
| literalStringFromName, |
| literalBool, |
| modularExpression, |
| function, |
| arrowFunction, |
| namedFunction, |
| access, |
| parameter, |
| variableDeclaration, |
| thisExpression, |
| variableUse, |
| postfix, |
| prefix, |
| binary, |
| callExpression, |
| newExpression, |
| conditional, |
| variableInitialization, |
| assignment, |
| variableDeclarationList, |
| literalExpression, |
| dartYield, |
| literalStatement, |
| labeledStatement, |
| functionDeclaration, |
| switchDefault, |
| switchCase, |
| switchStatement, |
| catchClause, |
| tryStatement, |
| throwStatement, |
| returnStatement, |
| breakStatement, |
| continueStatement, |
| doStatement, |
| whileStatement, |
| forInStatement, |
| forStatement, |
| ifStatement, |
| emptyStatement, |
| expressionStatement, |
| block, |
| program, |
| stringReference, |
| typeReference, |
| deferredHolderExpression, |
| } |
| |
| /// Tags used for debugging serialization/deserialization boundary mismatches. |
| class JsNodeTags { |
| static const String tag = 'js-node'; |
| static const String comment = 'js-comment'; |
| static const String await = 'js-await'; |
| static const String regExpLiteral = 'js-regExpLiteral'; |
| static const String property = 'js-property'; |
| static const String methodDefinition = 'js-methodDefinition'; |
| static const String objectInitializer = 'js-objectInitializer'; |
| static const String arrayHole = 'js-arrayHole'; |
| static const String arrayInitializer = 'js-arrayInitializer'; |
| static const String parentheses = 'js-parentheses'; |
| static const String modularName = 'js-modularName'; |
| static const String asyncName = 'js-asyncName'; |
| static const String stringBackedName = 'js-stringBackedName'; |
| static const String stringConcatenation = 'js-stringConcatenation'; |
| static const String literalNull = 'js-literalNull'; |
| static const String literalNumber = 'js-literalNumber'; |
| static const String literalString = 'js-literalString'; |
| static const String literalStringFromName = 'js-literalStringFromName'; |
| static const String literalBool = 'js-literalBool'; |
| static const String modularExpression = 'js-modularExpression'; |
| static const String function = 'js-function'; |
| static const String arrowFunction = 'js-arrowFunction'; |
| static const String namedFunction = 'js-namedFunction'; |
| static const String access = 'js-access'; |
| static const String parameter = 'js-parameter'; |
| static const String variableDeclaration = 'js-variableDeclaration'; |
| static const String thisExpression = 'js-thisExpression'; |
| static const String variableUse = 'js-variableUse'; |
| static const String postfix = 'js-postfix'; |
| static const String prefix = 'js-prefix'; |
| static const String binary = 'js-binary'; |
| static const String callExpression = 'js-callExpression'; |
| static const String newExpression = 'js-newExpression'; |
| static const String conditional = 'js-conditional'; |
| static const String variableInitialization = 'js-variableInitialization'; |
| static const String assignment = 'js-assignment'; |
| static const String variableDeclarationList = 'js-variableDeclarationList'; |
| static const String literalExpression = 'js-literalExpression'; |
| static const String dartYield = 'js-dartYield'; |
| static const String literalStatement = 'js-literalStatement'; |
| static const String labeledStatement = 'js-labeledStatement'; |
| static const String functionDeclaration = 'js-functionDeclaration'; |
| static const String switchDefault = 'js-switchDefault'; |
| static const String switchCase = 'js-switchCase'; |
| static const String switchStatement = 'js-switchStatement'; |
| static const String catchClause = 'js-catchClause'; |
| static const String tryStatement = 'js-tryStatement'; |
| static const String throwStatement = 'js-throwStatement'; |
| static const String returnStatement = 'js-returnStatement'; |
| static const String breakStatement = 'js-breakStatement'; |
| static const String continueStatement = 'js-continueStatement'; |
| static const String doStatement = 'js-doStatement'; |
| static const String whileStatement = 'js-whileStatement'; |
| static const String forInStatement = 'js-forInStatement'; |
| static const String forStatement = 'js-forStatement'; |
| static const String ifStatement = 'js-ifStatement'; |
| static const String emptyStatement = 'js-emptyStatement'; |
| static const String expressionStatement = 'js-expressionStatement'; |
| static const String block = 'js-block'; |
| static const String program = 'js-program'; |
| static const String stringReference = 'js-stringReference'; |
| static const String typeReference = 'js-typeReference'; |
| static const String deferredHolderExpression = 'js-deferredHolderExpression'; |
| } |
| |
| enum JsAnnotationKind { string, resourceIdentifier } |
| |
| /// Visitor that serializes a [js.Node] into a [DataSinkWriter]. |
| /// |
| /// Collects deferred expressions into [deferredExpressionData] as it encounters |
| /// them in the AST. |
| class JsNodeSerializer implements js.NodeVisitor<void> { |
| final DataSinkWriter sink; |
| final js.DeferredExpressionRegistry? _registry; |
| |
| JsNodeSerializer._(this.sink, this._registry); |
| |
| static void writeToDataSink( |
| DataSinkWriter sink, |
| js.Node node, |
| js.DeferredExpressionRegistry? registry, |
| ) { |
| sink.begin(JsNodeTags.tag); |
| JsNodeSerializer serializer = JsNodeSerializer._(sink, registry); |
| serializer.visit(node); |
| sink.end(JsNodeTags.tag); |
| } |
| |
| void visitOrNull(js.Node? node) { |
| final isNotNull = node != null; |
| sink.writeBool(isNotNull); |
| if (isNotNull) { |
| visit(node); |
| } |
| } |
| |
| void visit(js.Node node) { |
| node.accept(this); |
| } |
| |
| void visitList(Iterable<js.Node> nodes) { |
| sink.writeList(nodes, visit); |
| } |
| |
| void _writeInfo(js.Node node) { |
| final sourceInformation = node.sourceInformation as SourceInformation?; |
| final annotations = node.annotations; |
| // Low bit encodes presence of `sourceInformation`, higher bits the number |
| // of annotations. |
| final infoCode = |
| (sourceInformation == null ? 0 : 1) + 2 * annotations.length; |
| sink.writeInt(infoCode); |
| final hasSourceInformation = infoCode.isOdd; |
| final annotationCount = infoCode ~/ 2; |
| if (hasSourceInformation) { |
| sink.writeIndexed<SourceInformation>(sourceInformation, ( |
| SourceInformation sourceInformation, |
| ) { |
| SourceInformation.writeToDataSink(sink, sourceInformation); |
| }); |
| } |
| for (int i = 0; i < annotationCount; i++) { |
| _writeAnnotation(annotations[i]); |
| } |
| } |
| |
| void _writeAnnotation(Object annotation) { |
| if (annotation is String) { |
| sink.writeEnum(JsAnnotationKind.string); |
| sink.writeString(annotation); |
| } else if (annotation is ResourceIdentifier) { |
| sink.writeEnum(JsAnnotationKind.resourceIdentifier); |
| annotation.writeToDataSink(sink); |
| } else { |
| throw UnsupportedError( |
| 'JsNodeAnnotation ${annotation.runtimeType}: $annotation', |
| ); |
| } |
| } |
| |
| @override |
| void visitInterpolatedDeclaration(js.InterpolatedDeclaration node) { |
| throw UnsupportedError('JsNodeSerializer.visitInterpolatedDeclaration'); |
| } |
| |
| @override |
| void visitInterpolatedStatement(js.InterpolatedStatement node) { |
| throw UnsupportedError('JsNodeSerializer.visitInterpolatedStatement'); |
| } |
| |
| @override |
| void visitInterpolatedSelector(js.InterpolatedSelector node) { |
| throw UnsupportedError('JsNodeSerializer.visitInterpolatedDeclaration'); |
| } |
| |
| @override |
| void visitInterpolatedParameter(js.InterpolatedParameter node) { |
| throw UnsupportedError('JsNodeSerializer.visitInterpolatedParameter'); |
| } |
| |
| @override |
| void visitInterpolatedLiteral(js.InterpolatedLiteral node) { |
| throw UnsupportedError('JsNodeSerializer.visitInterpolatedLiteral'); |
| } |
| |
| @override |
| void visitInterpolatedExpression(js.InterpolatedExpression node) { |
| throw UnsupportedError('JsNodeSerializer.visitInterpolatedExpression'); |
| } |
| |
| @override |
| void visitComment(js.Comment node) { |
| sink.writeEnum(JsNodeKind.comment); |
| sink.begin(JsNodeTags.comment); |
| sink.writeString(node.comment); |
| sink.end(JsNodeTags.comment); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitAwait(js.Await node) { |
| sink.writeEnum(JsNodeKind.await); |
| sink.begin(JsNodeTags.await); |
| visit(node.expression); |
| sink.end(JsNodeTags.await); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitRegExpLiteral(js.RegExpLiteral node) { |
| sink.writeEnum(JsNodeKind.regExpLiteral); |
| sink.begin(JsNodeTags.regExpLiteral); |
| sink.writeString(node.pattern); |
| sink.end(JsNodeTags.regExpLiteral); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitProperty(js.Property node) { |
| sink.writeEnum(JsNodeKind.property); |
| sink.begin(JsNodeTags.property); |
| visit(node.name); |
| visit(node.value); |
| sink.end(JsNodeTags.property); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitMethodDefinition(js.MethodDefinition node) { |
| sink.writeEnum(JsNodeKind.methodDefinition); |
| sink.begin(JsNodeTags.methodDefinition); |
| visit(node.name); |
| visit(node.function); |
| sink.end(JsNodeTags.methodDefinition); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitObjectInitializer(js.ObjectInitializer node) { |
| sink.writeEnum(JsNodeKind.objectInitializer); |
| sink.begin(JsNodeTags.objectInitializer); |
| visitList(node.properties); |
| sink.writeBool(node.isOneLiner); |
| sink.end(JsNodeTags.objectInitializer); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitArrayHole(js.ArrayHole node) { |
| sink.writeEnum(JsNodeKind.arrayHole); |
| sink.begin(JsNodeTags.arrayHole); |
| sink.end(JsNodeTags.arrayHole); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitArrayInitializer(js.ArrayInitializer node) { |
| sink.writeEnum(JsNodeKind.arrayInitializer); |
| sink.begin(JsNodeTags.arrayInitializer); |
| visitList(node.elements); |
| sink.end(JsNodeTags.arrayInitializer); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitParentheses(js.Parentheses node) { |
| sink.writeEnum(JsNodeKind.parentheses); |
| sink.begin(JsNodeTags.parentheses); |
| visit(node.enclosed); |
| sink.end(JsNodeTags.parentheses); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitName(js.Name node) { |
| if (node is ModularName) { |
| sink.writeEnum(JsNodeKind.modularName); |
| sink.begin(JsNodeTags.modularName); |
| sink.writeIndexed<ModularName>(node, (_) { |
| node.writeToDataSink(sink); |
| _writeInfo(node); |
| }, identity: true); |
| _registry?.registerModularName(node); |
| sink.end(JsNodeTags.modularName); |
| } else if (node is AsyncName) { |
| sink.writeEnum(JsNodeKind.asyncName); |
| sink.begin(JsNodeTags.asyncName); |
| visit(node.prefix); |
| visit(node.base); |
| sink.end(JsNodeTags.asyncName); |
| _writeInfo(node); |
| } else if (node is StringBackedName) { |
| sink.writeEnum(JsNodeKind.stringBackedName); |
| sink.begin(JsNodeTags.stringBackedName); |
| sink.writeString(node.name); |
| sink.end(JsNodeTags.stringBackedName); |
| _writeInfo(node); |
| } else { |
| throw UnsupportedError( |
| 'Unexpected deferred expression: ${node.runtimeType}.', |
| ); |
| } |
| } |
| |
| @override |
| void visitStringConcatenation(js.StringConcatenation node) { |
| sink.writeEnum(JsNodeKind.stringConcatenation); |
| sink.begin(JsNodeTags.stringConcatenation); |
| visitList(node.parts); |
| sink.end(JsNodeTags.stringConcatenation); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitLiteralNull(js.LiteralNull node) { |
| sink.writeEnum(JsNodeKind.literalNull); |
| sink.begin(JsNodeTags.literalNull); |
| sink.end(JsNodeTags.literalNull); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitLiteralNumber(js.LiteralNumber node) { |
| sink.writeEnum(JsNodeKind.literalNumber); |
| sink.begin(JsNodeTags.literalNumber); |
| sink.writeString(node.value); |
| sink.end(JsNodeTags.literalNumber); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitLiteralString(js.LiteralString node) { |
| if (node is js.LiteralStringFromName) { |
| sink.writeEnum(JsNodeKind.literalStringFromName); |
| sink.begin(JsNodeTags.literalStringFromName); |
| visit(node.name); |
| sink.end(JsNodeTags.literalStringFromName); |
| } else { |
| sink.writeEnum(JsNodeKind.literalString); |
| sink.begin(JsNodeTags.literalString); |
| sink.writeString(node.value); |
| sink.end(JsNodeTags.literalString); |
| } |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitLiteralBool(js.LiteralBool node) { |
| sink.writeEnum(JsNodeKind.literalBool); |
| sink.begin(JsNodeTags.literalBool); |
| sink.writeBool(node.value); |
| sink.end(JsNodeTags.literalBool); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitDeferredString(js.DeferredString node) { |
| throw UnsupportedError('JsNodeSerializer.visitDeferredString'); |
| } |
| |
| @override |
| void visitDeferredStatement(js.DeferredStatement node) { |
| throw UnsupportedError('JsNodeSerializer.visitDeferredStatement'); |
| } |
| |
| @override |
| void visitDeferredNumber(js.DeferredNumber node) { |
| throw UnsupportedError('JsNodeSerializer.visitDeferredNumber'); |
| } |
| |
| @override |
| void visitDeferredExpression(js.DeferredExpression node) { |
| if (node is ModularExpression) { |
| sink.writeEnum(JsNodeKind.modularExpression); |
| sink.begin(JsNodeTags.modularExpression); |
| sink.writeIndexed<ModularExpression>(node, (_) { |
| node.writeToDataSink(sink); |
| _writeInfo(node); |
| }, identity: true); |
| _registry?.registerModularExpression(node); |
| sink.end(JsNodeTags.modularExpression); |
| } else if (node is TypeReference) { |
| sink.writeEnum(JsNodeKind.typeReference); |
| sink.begin(JsNodeTags.typeReference); |
| sink.writeIndexed<TypeReference>(node, (_) { |
| node.writeToDataSink(sink); |
| _writeInfo(node); |
| }, identity: true); |
| _registry?.registerTypeReference(node); |
| sink.end(JsNodeTags.typeReference); |
| } else if (node is StringReference) { |
| sink.writeEnum(JsNodeKind.stringReference); |
| sink.begin(JsNodeTags.stringReference); |
| sink.writeIndexed<StringReference>(node, (_) { |
| node.writeToDataSink(sink); |
| _writeInfo(node); |
| }, identity: true); |
| _registry?.registerStringReference(node); |
| sink.end(JsNodeTags.stringReference); |
| } else if (node is DeferredHolderExpression) { |
| sink.writeEnum(JsNodeKind.deferredHolderExpression); |
| sink.begin(JsNodeTags.deferredHolderExpression); |
| sink.writeIndexed<DeferredHolderExpression>(node, (_) { |
| node.writeToDataSink(sink); |
| _writeInfo(node); |
| }, identity: true); |
| _registry?.registerDeferredHolderExpression(node); |
| sink.end(JsNodeTags.deferredHolderExpression); |
| } else { |
| throw UnsupportedError( |
| 'Unexpected deferred expression: ${node.runtimeType}.', |
| ); |
| } |
| } |
| |
| @override |
| void visitFun(js.Fun node) { |
| sink.writeEnum(JsNodeKind.function); |
| sink.begin(JsNodeTags.function); |
| visitList(node.params); |
| sink.writeDeferrable( |
| () => sink.writeList(node.body.statements, sink.writeJsNode), |
| ); |
| sink.writeEnum(node.asyncModifier); |
| sink.end(JsNodeTags.function); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitArrowFunction(js.ArrowFunction node) { |
| sink.writeEnum(JsNodeKind.arrowFunction); |
| sink.begin(JsNodeTags.arrowFunction); |
| visitList(node.params); |
| visit(node.body); |
| sink.writeEnum(node.asyncModifier); |
| sink.end(JsNodeTags.arrowFunction); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitNamedFunction(js.NamedFunction node) { |
| sink.writeEnum(JsNodeKind.namedFunction); |
| sink.begin(JsNodeTags.namedFunction); |
| visit(node.name); |
| visit(node.function); |
| sink.end(JsNodeTags.namedFunction); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitAccess(js.PropertyAccess node) { |
| sink.writeEnum(JsNodeKind.access); |
| sink.begin(JsNodeTags.access); |
| visit(node.receiver); |
| visit(node.selector); |
| sink.end(JsNodeTags.access); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitParameter(js.Parameter node) { |
| sink.writeEnum(JsNodeKind.parameter); |
| sink.begin(JsNodeTags.parameter); |
| sink.writeString(node.name); |
| sink.end(JsNodeTags.parameter); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitVariableDeclaration(js.VariableDeclaration node) { |
| sink.writeEnum(JsNodeKind.variableDeclaration); |
| sink.begin(JsNodeTags.variableDeclaration); |
| sink.writeString(node.name); |
| sink.writeBool(node.allowRename); |
| sink.end(JsNodeTags.variableDeclaration); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitThis(js.This node) { |
| sink.writeEnum(JsNodeKind.thisExpression); |
| sink.begin(JsNodeTags.thisExpression); |
| sink.end(JsNodeTags.thisExpression); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitVariableUse(js.VariableUse node) { |
| sink.writeEnum(JsNodeKind.variableUse); |
| sink.begin(JsNodeTags.variableUse); |
| sink.writeString(node.name); |
| sink.end(JsNodeTags.variableUse); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitPostfix(js.Postfix node) { |
| sink.writeEnum(JsNodeKind.postfix); |
| sink.begin(JsNodeTags.postfix); |
| sink.writeString(node.op); |
| visit(node.argument); |
| sink.end(JsNodeTags.postfix); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitPrefix(js.Prefix node) { |
| sink.writeEnum(JsNodeKind.prefix); |
| sink.begin(JsNodeTags.prefix); |
| sink.writeString(node.op); |
| visit(node.argument); |
| sink.end(JsNodeTags.prefix); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitBinary(js.Binary node) { |
| sink.writeEnum(JsNodeKind.binary); |
| sink.begin(JsNodeTags.binary); |
| sink.writeString(node.op); |
| visit(node.left); |
| visit(node.right); |
| sink.end(JsNodeTags.binary); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitCall(js.Call node) { |
| sink.writeEnum(JsNodeKind.callExpression); |
| sink.begin(JsNodeTags.callExpression); |
| visit(node.target); |
| visitList(node.arguments); |
| sink.end(JsNodeTags.callExpression); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitNew(js.New node) { |
| sink.writeEnum(JsNodeKind.newExpression); |
| sink.begin(JsNodeTags.newExpression); |
| visit(node.target); |
| visitList(node.arguments); |
| sink.end(JsNodeTags.newExpression); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitConditional(js.Conditional node) { |
| sink.writeEnum(JsNodeKind.conditional); |
| sink.begin(JsNodeTags.conditional); |
| visit(node.condition); |
| visit(node.then); |
| visit(node.otherwise); |
| sink.end(JsNodeTags.conditional); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitVariableInitialization(js.VariableInitialization node) { |
| sink.writeEnum(JsNodeKind.variableInitialization); |
| sink.begin(JsNodeTags.variableInitialization); |
| visit(node.declaration); |
| visitOrNull(node.value); |
| sink.end(JsNodeTags.variableInitialization); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitAssignment(js.Assignment node) { |
| sink.writeEnum(JsNodeKind.assignment); |
| sink.begin(JsNodeTags.assignment); |
| visit(node.leftHandSide); |
| sink.writeStringOrNull(node.op); |
| visit(node.value); |
| sink.end(JsNodeTags.assignment); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitVariableDeclarationList(js.VariableDeclarationList node) { |
| sink.writeEnum(JsNodeKind.variableDeclarationList); |
| sink.begin(JsNodeTags.variableDeclarationList); |
| visitList(node.declarations); |
| sink.writeBool(node.indentSplits); |
| sink.end(JsNodeTags.variableDeclarationList); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitLiteralExpression(js.LiteralExpression node) { |
| sink.writeEnum(JsNodeKind.literalExpression); |
| sink.begin(JsNodeTags.literalExpression); |
| sink.writeString(node.template); |
| sink.end(JsNodeTags.literalExpression); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitDartYield(js.DartYield node) { |
| sink.writeEnum(JsNodeKind.dartYield); |
| sink.begin(JsNodeTags.dartYield); |
| visit(node.expression); |
| sink.writeBool(node.hasStar); |
| sink.end(JsNodeTags.dartYield); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitLiteralStatement(js.LiteralStatement node) { |
| sink.writeEnum(JsNodeKind.literalStatement); |
| sink.begin(JsNodeTags.literalStatement); |
| sink.writeString(node.code); |
| sink.end(JsNodeTags.literalStatement); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitLabeledStatement(js.LabeledStatement node) { |
| sink.writeEnum(JsNodeKind.labeledStatement); |
| sink.begin(JsNodeTags.labeledStatement); |
| sink.writeString(node.label); |
| visit(node.body); |
| sink.end(JsNodeTags.labeledStatement); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitFunctionDeclaration(js.FunctionDeclaration node) { |
| sink.writeEnum(JsNodeKind.functionDeclaration); |
| sink.begin(JsNodeTags.functionDeclaration); |
| visit(node.name); |
| visit(node.function); |
| sink.end(JsNodeTags.functionDeclaration); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitDefault(js.Default node) { |
| sink.writeEnum(JsNodeKind.switchDefault); |
| sink.begin(JsNodeTags.switchDefault); |
| visit(node.body); |
| sink.end(JsNodeTags.switchDefault); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitCase(js.Case node) { |
| sink.writeEnum(JsNodeKind.switchCase); |
| sink.begin(JsNodeTags.switchCase); |
| visit(node.expression); |
| visit(node.body); |
| sink.end(JsNodeTags.switchCase); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitSwitch(js.Switch node) { |
| sink.writeEnum(JsNodeKind.switchStatement); |
| sink.begin(JsNodeTags.switchStatement); |
| visit(node.key); |
| visitList(node.cases); |
| sink.end(JsNodeTags.switchStatement); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitCatch(js.Catch node) { |
| sink.writeEnum(JsNodeKind.catchClause); |
| sink.begin(JsNodeTags.catchClause); |
| visit(node.declaration); |
| visit(node.body); |
| sink.end(JsNodeTags.catchClause); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitTry(js.Try node) { |
| sink.writeEnum(JsNodeKind.tryStatement); |
| sink.begin(JsNodeTags.tryStatement); |
| visit(node.body); |
| visitOrNull(node.catchPart); |
| visitOrNull(node.finallyPart); |
| sink.end(JsNodeTags.tryStatement); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitThrow(js.Throw node) { |
| sink.writeEnum(JsNodeKind.throwStatement); |
| sink.begin(JsNodeTags.throwStatement); |
| visit(node.expression); |
| sink.end(JsNodeTags.throwStatement); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitReturn(js.Return node) { |
| sink.writeEnum(JsNodeKind.returnStatement); |
| sink.begin(JsNodeTags.returnStatement); |
| visitOrNull(node.value); |
| sink.end(JsNodeTags.returnStatement); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitBreak(js.Break node) { |
| sink.writeEnum(JsNodeKind.breakStatement); |
| sink.begin(JsNodeTags.breakStatement); |
| sink.writeStringOrNull(node.targetLabel); |
| sink.end(JsNodeTags.breakStatement); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitContinue(js.Continue node) { |
| sink.writeEnum(JsNodeKind.continueStatement); |
| sink.begin(JsNodeTags.continueStatement); |
| sink.writeStringOrNull(node.targetLabel); |
| sink.end(JsNodeTags.continueStatement); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitDo(js.Do node) { |
| sink.writeEnum(JsNodeKind.doStatement); |
| sink.begin(JsNodeTags.doStatement); |
| visit(node.body); |
| visit(node.condition); |
| sink.end(JsNodeTags.doStatement); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitWhile(js.While node) { |
| sink.writeEnum(JsNodeKind.whileStatement); |
| sink.begin(JsNodeTags.whileStatement); |
| visit(node.condition); |
| visit(node.body); |
| sink.end(JsNodeTags.whileStatement); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitForIn(js.ForIn node) { |
| sink.writeEnum(JsNodeKind.forInStatement); |
| sink.begin(JsNodeTags.forInStatement); |
| visit(node.leftHandSide); |
| visit(node.object); |
| visit(node.body); |
| sink.end(JsNodeTags.forInStatement); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitFor(js.For node) { |
| sink.writeEnum(JsNodeKind.forStatement); |
| sink.begin(JsNodeTags.forStatement); |
| visitOrNull(node.init); |
| visitOrNull(node.condition); |
| visitOrNull(node.update); |
| visit(node.body); |
| sink.end(JsNodeTags.forStatement); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitIf(js.If node) { |
| sink.writeEnum(JsNodeKind.ifStatement); |
| sink.begin(JsNodeTags.ifStatement); |
| visit(node.condition); |
| visit(node.then); |
| visit(node.otherwise); |
| sink.end(JsNodeTags.ifStatement); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitEmptyStatement(js.EmptyStatement node) { |
| sink.writeEnum(JsNodeKind.emptyStatement); |
| sink.begin(JsNodeTags.emptyStatement); |
| sink.end(JsNodeTags.emptyStatement); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitExpressionStatement(js.ExpressionStatement node) { |
| sink.writeEnum(JsNodeKind.expressionStatement); |
| sink.begin(JsNodeTags.expressionStatement); |
| visit(node.expression); |
| sink.end(JsNodeTags.expressionStatement); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitBlock(js.Block node) { |
| sink.writeEnum(JsNodeKind.block); |
| sink.begin(JsNodeTags.block); |
| visitList(node.statements); |
| sink.end(JsNodeTags.block); |
| _writeInfo(node); |
| } |
| |
| @override |
| void visitProgram(js.Program node) { |
| sink.writeEnum(JsNodeKind.program); |
| sink.begin(JsNodeTags.program); |
| visitList(node.body); |
| sink.end(JsNodeTags.program); |
| _writeInfo(node); |
| } |
| } |
| |
| /// Helper class that deserializes a [js.Node] from [DataSourceReader]. |
| class JsNodeDeserializer { |
| final DataSourceReader source; |
| |
| JsNodeDeserializer._(this.source); |
| |
| static js.Node readFromDataSource(DataSourceReader source) { |
| source.begin(JsNodeTags.tag); |
| JsNodeDeserializer deserializer = JsNodeDeserializer._(source); |
| js.Node node = deserializer.read(); |
| source.end(JsNodeTags.tag); |
| return node; |
| } |
| |
| T? readOrNull<T extends js.Node>() { |
| bool hasValue = source.readBool(); |
| if (!hasValue) return null; |
| return read(); |
| } |
| |
| static List<js.Statement> _readFunBodyStatements(DataSourceReader source) { |
| return source.readList(() => source.readJsNode() as js.Statement); |
| } |
| |
| T read<T extends js.Node>() { |
| JsNodeKind kind = source.readEnum(JsNodeKind.values); |
| js.Node node; |
| bool needsInfo = true; |
| switch (kind) { |
| case JsNodeKind.comment: |
| source.begin(JsNodeTags.comment); |
| node = js.Comment(source.readString()); |
| source.end(JsNodeTags.comment); |
| break; |
| case JsNodeKind.await: |
| source.begin(JsNodeTags.await); |
| node = js.Await(read()); |
| source.end(JsNodeTags.await); |
| break; |
| case JsNodeKind.regExpLiteral: |
| source.begin(JsNodeTags.regExpLiteral); |
| node = js.RegExpLiteral(source.readString()); |
| source.end(JsNodeTags.regExpLiteral); |
| break; |
| case JsNodeKind.property: |
| source.begin(JsNodeTags.property); |
| js.Expression name = read(); |
| js.Expression value = read(); |
| node = js.Property(name, value); |
| source.end(JsNodeTags.property); |
| break; |
| case JsNodeKind.methodDefinition: |
| source.begin(JsNodeTags.methodDefinition); |
| js.Expression name = read(); |
| final function = read() as js.Fun; |
| node = js.MethodDefinition(name, function); |
| source.end(JsNodeTags.methodDefinition); |
| break; |
| case JsNodeKind.objectInitializer: |
| source.begin(JsNodeTags.objectInitializer); |
| List<js.Property> properties = readList(); |
| bool isOneLiner = source.readBool(); |
| node = js.ObjectInitializer(properties, isOneLiner: isOneLiner); |
| source.end(JsNodeTags.objectInitializer); |
| break; |
| case JsNodeKind.arrayHole: |
| source.begin(JsNodeTags.arrayHole); |
| node = js.ArrayHole(); |
| source.end(JsNodeTags.arrayHole); |
| break; |
| case JsNodeKind.arrayInitializer: |
| source.begin(JsNodeTags.arrayInitializer); |
| List<js.Expression> elements = readList(); |
| node = js.ArrayInitializer(elements); |
| source.end(JsNodeTags.arrayInitializer); |
| break; |
| case JsNodeKind.parentheses: |
| source.begin(JsNodeTags.parentheses); |
| node = js.Parentheses(read()); |
| source.end(JsNodeTags.parentheses); |
| break; |
| case JsNodeKind.modularName: |
| source.begin(JsNodeTags.modularName); |
| needsInfo = false; |
| node = source.readIndexed<ModularName>( |
| () => _readInfo(ModularName.readFromDataSource(source)), |
| ); |
| source.end(JsNodeTags.modularName); |
| break; |
| case JsNodeKind.asyncName: |
| source.begin(JsNodeTags.asyncName); |
| js.Name prefix = read(); |
| js.Name base = read(); |
| node = AsyncName(prefix, base); |
| source.end(JsNodeTags.asyncName); |
| break; |
| case JsNodeKind.stringBackedName: |
| source.begin(JsNodeTags.stringBackedName); |
| node = StringBackedName(source.readString()); |
| source.end(JsNodeTags.stringBackedName); |
| break; |
| case JsNodeKind.stringConcatenation: |
| source.begin(JsNodeTags.stringConcatenation); |
| List<js.Literal> parts = readList(); |
| node = js.StringConcatenation(parts); |
| source.end(JsNodeTags.stringConcatenation); |
| break; |
| case JsNodeKind.literalNull: |
| source.begin(JsNodeTags.literalNull); |
| node = js.LiteralNull(); |
| source.end(JsNodeTags.literalNull); |
| break; |
| case JsNodeKind.literalNumber: |
| source.begin(JsNodeTags.literalNumber); |
| node = js.LiteralNumber(source.readString()); |
| source.end(JsNodeTags.literalNumber); |
| break; |
| case JsNodeKind.literalString: |
| source.begin(JsNodeTags.literalString); |
| node = js.LiteralString(source.readString()); |
| source.end(JsNodeTags.literalString); |
| break; |
| case JsNodeKind.literalStringFromName: |
| source.begin(JsNodeTags.literalStringFromName); |
| js.Name name = read(); |
| node = js.LiteralStringFromName(name); |
| source.end(JsNodeTags.literalStringFromName); |
| break; |
| case JsNodeKind.literalBool: |
| source.begin(JsNodeTags.literalBool); |
| node = js.LiteralBool(source.readBool()); |
| source.end(JsNodeTags.literalBool); |
| break; |
| case JsNodeKind.modularExpression: |
| source.begin(JsNodeTags.modularExpression); |
| needsInfo = false; |
| node = source.readIndexed<ModularExpression>( |
| () => _readInfo(ModularExpression.readFromDataSource(source)), |
| ); |
| source.end(JsNodeTags.modularExpression); |
| break; |
| case JsNodeKind.function: |
| source.begin(JsNodeTags.function); |
| List<js.Parameter> params = readList(); |
| js.Block body = js.DeferredBlock( |
| source.readDeferrable(_readFunBodyStatements, cacheData: false), |
| ); |
| js.AsyncModifier asyncModifier = source.readEnum( |
| js.AsyncModifier.values, |
| ); |
| node = js.Fun(params, body, asyncModifier: asyncModifier); |
| source.end(JsNodeTags.function); |
| break; |
| case JsNodeKind.arrowFunction: |
| source.begin(JsNodeTags.arrowFunction); |
| List<js.Parameter> params = readList(); |
| js.Node body = read(); |
| js.AsyncModifier asyncModifier = source.readEnum( |
| js.AsyncModifier.values, |
| ); |
| node = js.ArrowFunction(params, body, asyncModifier: asyncModifier); |
| source.end(JsNodeTags.arrowFunction); |
| break; |
| case JsNodeKind.namedFunction: |
| source.begin(JsNodeTags.namedFunction); |
| js.Declaration name = read(); |
| js.Fun function = read(); |
| node = js.NamedFunction(name, function); |
| source.end(JsNodeTags.namedFunction); |
| break; |
| case JsNodeKind.access: |
| source.begin(JsNodeTags.access); |
| js.Expression receiver = read(); |
| js.Expression selector = read(); |
| node = js.PropertyAccess(receiver, selector); |
| source.end(JsNodeTags.access); |
| break; |
| case JsNodeKind.parameter: |
| source.begin(JsNodeTags.parameter); |
| node = js.Parameter(source.readString()); |
| source.end(JsNodeTags.parameter); |
| break; |
| case JsNodeKind.variableDeclaration: |
| source.begin(JsNodeTags.variableDeclaration); |
| String name = source.readString(); |
| bool allowRename = source.readBool(); |
| node = js.VariableDeclaration(name, allowRename: allowRename); |
| source.end(JsNodeTags.variableDeclaration); |
| break; |
| case JsNodeKind.thisExpression: |
| source.begin(JsNodeTags.thisExpression); |
| node = js.This(); |
| source.end(JsNodeTags.thisExpression); |
| break; |
| case JsNodeKind.variableUse: |
| source.begin(JsNodeTags.variableUse); |
| node = js.VariableUse(source.readString()); |
| source.end(JsNodeTags.variableUse); |
| break; |
| case JsNodeKind.postfix: |
| source.begin(JsNodeTags.postfix); |
| String op = source.readString(); |
| js.Expression argument = read(); |
| node = js.Postfix(op, argument); |
| source.end(JsNodeTags.postfix); |
| break; |
| case JsNodeKind.prefix: |
| source.begin(JsNodeTags.prefix); |
| String op = source.readString(); |
| js.Expression argument = read(); |
| node = js.Prefix(op, argument); |
| source.end(JsNodeTags.prefix); |
| break; |
| case JsNodeKind.binary: |
| source.begin(JsNodeTags.binary); |
| String op = source.readString(); |
| js.Expression left = read(); |
| js.Expression right = read(); |
| node = js.Binary(op, left, right); |
| source.end(JsNodeTags.binary); |
| break; |
| case JsNodeKind.callExpression: |
| source.begin(JsNodeTags.callExpression); |
| js.Expression target = read(); |
| List<js.Expression> arguments = readList(); |
| node = js.Call(target, arguments); |
| source.end(JsNodeTags.callExpression); |
| break; |
| case JsNodeKind.newExpression: |
| source.begin(JsNodeTags.newExpression); |
| js.Expression cls = read(); |
| List<js.Expression> arguments = readList(); |
| node = js.New(cls, arguments); |
| source.end(JsNodeTags.newExpression); |
| break; |
| case JsNodeKind.conditional: |
| source.begin(JsNodeTags.conditional); |
| js.Expression condition = read(); |
| js.Expression then = read(); |
| js.Expression otherwise = read(); |
| node = js.Conditional(condition, then, otherwise); |
| source.end(JsNodeTags.conditional); |
| break; |
| case JsNodeKind.variableInitialization: |
| source.begin(JsNodeTags.variableInitialization); |
| js.Declaration declaration = read(); |
| final value = source.readValueOrNull(read) as js.Expression?; |
| node = js.VariableInitialization(declaration, value); |
| source.end(JsNodeTags.variableInitialization); |
| break; |
| case JsNodeKind.assignment: |
| source.begin(JsNodeTags.assignment); |
| js.Expression leftHandSide = read(); |
| final op = source.readStringOrNull(); |
| js.Expression value = read(); |
| node = js.Assignment.compound(leftHandSide, op, value); |
| source.end(JsNodeTags.assignment); |
| break; |
| case JsNodeKind.variableDeclarationList: |
| source.begin(JsNodeTags.variableDeclarationList); |
| List<js.VariableInitialization> declarations = readList(); |
| bool indentSplits = source.readBool(); |
| node = js.VariableDeclarationList( |
| declarations, |
| indentSplits: indentSplits, |
| ); |
| source.end(JsNodeTags.variableDeclarationList); |
| break; |
| case JsNodeKind.literalExpression: |
| source.begin(JsNodeTags.literalExpression); |
| node = js.LiteralExpression(source.readString()); |
| source.end(JsNodeTags.literalExpression); |
| break; |
| case JsNodeKind.dartYield: |
| source.begin(JsNodeTags.dartYield); |
| js.Expression expression = read(); |
| bool hasStar = source.readBool(); |
| node = js.DartYield(expression, hasStar); |
| source.end(JsNodeTags.dartYield); |
| break; |
| case JsNodeKind.literalStatement: |
| source.begin(JsNodeTags.literalStatement); |
| node = js.LiteralStatement(source.readString()); |
| source.end(JsNodeTags.literalStatement); |
| break; |
| case JsNodeKind.labeledStatement: |
| source.begin(JsNodeTags.labeledStatement); |
| String label = source.readString(); |
| js.Statement body = read(); |
| node = js.LabeledStatement(label, body); |
| source.end(JsNodeTags.labeledStatement); |
| break; |
| case JsNodeKind.functionDeclaration: |
| source.begin(JsNodeTags.functionDeclaration); |
| js.Declaration name = read(); |
| js.Fun function = read(); |
| node = js.FunctionDeclaration(name, function); |
| source.end(JsNodeTags.functionDeclaration); |
| break; |
| case JsNodeKind.switchDefault: |
| source.begin(JsNodeTags.switchDefault); |
| js.Block body = read(); |
| node = js.Default(body); |
| source.end(JsNodeTags.switchDefault); |
| break; |
| case JsNodeKind.switchCase: |
| source.begin(JsNodeTags.switchCase); |
| js.Expression expression = read(); |
| js.Block body = read(); |
| node = js.Case(expression, body); |
| source.end(JsNodeTags.switchCase); |
| break; |
| case JsNodeKind.switchStatement: |
| source.begin(JsNodeTags.switchStatement); |
| js.Expression key = read(); |
| List<js.SwitchClause> cases = readList(); |
| node = js.Switch(key, cases); |
| source.end(JsNodeTags.switchStatement); |
| break; |
| case JsNodeKind.catchClause: |
| source.begin(JsNodeTags.catchClause); |
| js.Declaration declaration = read(); |
| js.Block body = read(); |
| node = js.Catch(declaration, body); |
| source.end(JsNodeTags.catchClause); |
| break; |
| case JsNodeKind.tryStatement: |
| source.begin(JsNodeTags.tryStatement); |
| js.Block body = read(); |
| final catchPart = source.readValueOrNull(read) as js.Catch?; |
| final finallyPart = source.readValueOrNull(read) as js.Block?; |
| node = js.Try(body, catchPart, finallyPart); |
| source.end(JsNodeTags.tryStatement); |
| break; |
| case JsNodeKind.throwStatement: |
| source.begin(JsNodeTags.throwStatement); |
| js.Expression expression = read(); |
| node = js.Throw(expression); |
| source.end(JsNodeTags.throwStatement); |
| break; |
| case JsNodeKind.returnStatement: |
| source.begin(JsNodeTags.returnStatement); |
| final value = source.readValueOrNull(read) as js.Expression?; |
| node = js.Return(value); |
| source.end(JsNodeTags.returnStatement); |
| break; |
| case JsNodeKind.breakStatement: |
| source.begin(JsNodeTags.breakStatement); |
| final targetLabel = source.readStringOrNull(); |
| node = js.Break(targetLabel); |
| source.end(JsNodeTags.breakStatement); |
| break; |
| case JsNodeKind.continueStatement: |
| source.begin(JsNodeTags.continueStatement); |
| final targetLabel = source.readStringOrNull(); |
| node = js.Continue(targetLabel); |
| source.end(JsNodeTags.continueStatement); |
| break; |
| case JsNodeKind.doStatement: |
| source.begin(JsNodeTags.doStatement); |
| js.Statement body = read(); |
| js.Expression condition = read(); |
| node = js.Do(body, condition); |
| source.end(JsNodeTags.doStatement); |
| break; |
| case JsNodeKind.whileStatement: |
| source.begin(JsNodeTags.whileStatement); |
| js.Expression condition = read(); |
| js.Statement body = read(); |
| node = js.While(condition, body); |
| source.end(JsNodeTags.whileStatement); |
| break; |
| case JsNodeKind.forInStatement: |
| source.begin(JsNodeTags.forInStatement); |
| js.Expression leftHandSide = read(); |
| js.Expression object = read(); |
| js.Statement body = read(); |
| node = js.ForIn(leftHandSide, object, body); |
| source.end(JsNodeTags.forInStatement); |
| break; |
| case JsNodeKind.forStatement: |
| source.begin(JsNodeTags.forStatement); |
| final init = readOrNull() as js.Expression?; |
| final condition = readOrNull() as js.Expression?; |
| final update = readOrNull() as js.Expression?; |
| js.Statement body = read(); |
| node = js.For(init, condition, update, body); |
| source.end(JsNodeTags.forStatement); |
| break; |
| case JsNodeKind.ifStatement: |
| source.begin(JsNodeTags.ifStatement); |
| js.Expression condition = read(); |
| js.Statement then = read(); |
| js.Statement otherwise = read(); |
| node = js.If(condition, then, otherwise); |
| source.end(JsNodeTags.ifStatement); |
| break; |
| case JsNodeKind.emptyStatement: |
| source.begin(JsNodeTags.emptyStatement); |
| node = js.EmptyStatement(); |
| source.end(JsNodeTags.emptyStatement); |
| break; |
| case JsNodeKind.expressionStatement: |
| source.begin(JsNodeTags.expressionStatement); |
| node = js.ExpressionStatement(read()); |
| source.end(JsNodeTags.expressionStatement); |
| break; |
| case JsNodeKind.block: |
| source.begin(JsNodeTags.block); |
| List<js.Statement> statements = readList(); |
| node = js.Block(statements); |
| source.end(JsNodeTags.block); |
| break; |
| case JsNodeKind.program: |
| source.begin(JsNodeTags.program); |
| List<js.Statement> body = readList(); |
| node = js.Program(body); |
| source.end(JsNodeTags.program); |
| break; |
| case JsNodeKind.stringReference: |
| source.begin(JsNodeTags.stringReference); |
| needsInfo = false; |
| node = source.readIndexed<StringReference>( |
| () => _readInfo(StringReference.readFromDataSource(source)), |
| ); |
| source.end(JsNodeTags.stringReference); |
| break; |
| case JsNodeKind.typeReference: |
| source.begin(JsNodeTags.typeReference); |
| needsInfo = false; |
| node = source.readIndexed<TypeReference>( |
| () => _readInfo(TypeReference.readFromDataSource(source)), |
| ); |
| source.end(JsNodeTags.typeReference); |
| break; |
| case JsNodeKind.deferredHolderExpression: |
| source.begin(JsNodeTags.deferredHolderExpression); |
| needsInfo = false; |
| node = source.readIndexed<DeferredHolderExpression>( |
| () => _readInfo(DeferredHolderExpression.readFromDataSource(source)), |
| ); |
| source.end(JsNodeTags.deferredHolderExpression); |
| break; |
| } |
| |
| return needsInfo ? _readInfo(node) : node as T; |
| } |
| |
| T _readInfo<T extends js.Node>(js.Node node) { |
| final infoCode = source.readInt(); |
| final hasSourceInformation = infoCode.isOdd; |
| final annotationCount = infoCode ~/ 2; |
| if (hasSourceInformation) { |
| final sourceInformation = source |
| .readIndexedOrNullNoCache<SourceInformation>(() { |
| return SourceInformation.readFromDataSource(source); |
| }); |
| node = node.withSourceInformation(sourceInformation); |
| } |
| for (int i = 0; i < annotationCount; i++) { |
| node = node.withAnnotation(_readAnnotation()); |
| } |
| return node as T; |
| } |
| |
| List<T> readList<T extends js.Node>() { |
| return source.readList(read); |
| } |
| |
| Object _readAnnotation() { |
| final kind = source.readEnum(JsAnnotationKind.values); |
| switch (kind) { |
| case JsAnnotationKind.string: |
| return source.readString(); |
| case JsAnnotationKind.resourceIdentifier: |
| return ResourceIdentifier.readFromDataSource(source); |
| } |
| } |
| } |
| |
| enum ModularNameKind { |
| rtiField, |
| className, |
| aliasedSuperMember, |
| staticClosure, |
| methodProperty, |
| operatorIs, |
| instanceMethod, |
| instanceField, |
| invocation, |
| lazyInitializer, |
| globalPropertyNameForClass, |
| globalPropertyNameForMember, |
| globalNameForInterfaceTypeVariable, |
| nameForGetInterceptor, |
| nameForOneShotInterceptor, |
| asName, |
| } |
| |
| class ModularName extends js.Name implements js.AstContainer { |
| static const String tag = 'modular-name'; |
| |
| final ModularNameKind kind; |
| js.Name? _value; |
| final Object? data; |
| final Set<ClassEntity>? set; |
| |
| ModularName(this.kind, {this.data, this.set}); |
| |
| factory ModularName.readFromDataSource(DataSourceReader source) { |
| source.begin(tag); |
| ModularNameKind kind = source.readEnum(ModularNameKind.values); |
| Object? data; |
| Set<ClassEntity>? set; |
| switch (kind) { |
| case ModularNameKind.rtiField: |
| break; |
| case ModularNameKind.className: |
| case ModularNameKind.operatorIs: |
| case ModularNameKind.globalPropertyNameForClass: |
| data = source.readClass(); |
| break; |
| case ModularNameKind.aliasedSuperMember: |
| case ModularNameKind.staticClosure: |
| case ModularNameKind.methodProperty: |
| case ModularNameKind.instanceField: |
| case ModularNameKind.instanceMethod: |
| case ModularNameKind.lazyInitializer: |
| case ModularNameKind.globalPropertyNameForMember: |
| data = source.readMember(); |
| break; |
| case ModularNameKind.invocation: |
| data = Selector.readFromDataSource(source); |
| break; |
| case ModularNameKind.globalNameForInterfaceTypeVariable: |
| data = source.readTypeVariable(); |
| break; |
| case ModularNameKind.nameForGetInterceptor: |
| set = source.readClasses().toSet(); |
| break; |
| case ModularNameKind.nameForOneShotInterceptor: |
| data = Selector.readFromDataSource(source); |
| set = source.readClasses().toSet(); |
| break; |
| case ModularNameKind.asName: |
| data = source.readString(); |
| break; |
| } |
| source.end(tag); |
| return ModularName(kind, data: data, set: set); |
| } |
| |
| void writeToDataSink(DataSinkWriter sink) { |
| sink.begin(tag); |
| sink.writeEnum(kind); |
| switch (kind) { |
| case ModularNameKind.rtiField: |
| break; |
| case ModularNameKind.className: |
| case ModularNameKind.operatorIs: |
| case ModularNameKind.globalPropertyNameForClass: |
| sink.writeClass(data as ClassEntity); |
| break; |
| case ModularNameKind.aliasedSuperMember: |
| case ModularNameKind.staticClosure: |
| case ModularNameKind.methodProperty: |
| case ModularNameKind.instanceField: |
| case ModularNameKind.instanceMethod: |
| case ModularNameKind.lazyInitializer: |
| case ModularNameKind.globalPropertyNameForMember: |
| sink.writeMember(data as MemberEntity); |
| break; |
| case ModularNameKind.invocation: |
| final selector = data as Selector; |
| selector.writeToDataSink(sink); |
| break; |
| case ModularNameKind.globalNameForInterfaceTypeVariable: |
| final typeVariable = data as TypeVariableEntity; |
| sink.writeTypeVariable(typeVariable); |
| break; |
| case ModularNameKind.nameForGetInterceptor: |
| sink.writeClasses(set!); |
| break; |
| case ModularNameKind.nameForOneShotInterceptor: |
| final selector = data as Selector; |
| selector.writeToDataSink(sink); |
| sink.writeClasses(set!); |
| break; |
| case ModularNameKind.asName: |
| sink.writeString(data as String); |
| break; |
| } |
| sink.end(tag); |
| } |
| |
| @override |
| bool get isFinalized => _value != null; |
| |
| js.Name get value { |
| assert(isFinalized, 'value not set for $this'); |
| return _value!; |
| } |
| |
| set value(js.Name node) { |
| assert(!isFinalized); |
| _value = node.withSourceInformation(sourceInformation) as js.Name; |
| } |
| |
| @override |
| String get key { |
| assert(isFinalized); |
| return _value!.key; |
| } |
| |
| @override |
| String get name { |
| assert(isFinalized, 'value not set for $this'); |
| return _value!.name; |
| } |
| |
| @override |
| bool get allowRename { |
| assert(isFinalized, 'value not set for $this'); |
| return _value!.allowRename; |
| } |
| |
| @override |
| Iterable<js.Node> get containedNodes { |
| return _value != null ? [_value!] : const []; |
| } |
| |
| @override |
| int get hashCode { |
| return Hashing.setHash(set, Hashing.objectsHash(kind, data)); |
| } |
| |
| @override |
| bool operator ==(Object other) { |
| if (identical(this, other)) return true; |
| return other is ModularName && |
| kind == other.kind && |
| data == other.data && |
| equalSets(set, other.set); |
| } |
| |
| @override |
| String toString() => |
| 'ModularName(kind=$kind, data=$data, value=${_value?.key})'; |
| |
| @override |
| String nonfinalizedDebugText() { |
| switch (kind) { |
| case ModularNameKind.rtiField: |
| return r'ModularName"$ti"'; |
| case ModularNameKind.instanceField: |
| return 'ModularName"field:${(data as Entity).name}"'; |
| case ModularNameKind.instanceMethod: |
| return 'ModularName"${_instanceMethodName(data as MemberEntity)}"'; |
| case ModularNameKind.methodProperty: |
| return 'ModularName"methodProperty:${(data as Entity).name}"'; |
| case ModularNameKind.operatorIs: |
| return 'ModularName"is:${_className(data as ClassEntity)}"'; |
| case ModularNameKind.className: |
| return 'ModularName"class:${_className(data as ClassEntity)}"'; |
| case ModularNameKind.globalPropertyNameForClass: |
| return 'ModularName"classref:${_className(data as ClassEntity)}"'; |
| case ModularNameKind.aliasedSuperMember: |
| MemberEntity member = (data as MemberEntity); |
| String className = _className(member.enclosingClass!); |
| String invocationName = operatorNameToIdentifier(member.name)!; |
| final description = "$className.$invocationName"; |
| return 'ModularName"alias:$description"'; |
| case ModularNameKind.staticClosure: |
| return 'ModularName"closure:${_qualifiedStaticName(data as MemberEntity)}"'; |
| case ModularNameKind.lazyInitializer: |
| return 'ModularName"lazy:${(data as MemberEntity).name}"'; |
| case ModularNameKind.globalPropertyNameForMember: |
| MemberEntity member = data as MemberEntity; |
| return 'ModularName"ref:${_qualifiedStaticName(member)}"'; |
| case ModularNameKind.invocation: |
| return 'ModularName"selector:${_selectorText(data as Selector)}"'; |
| case ModularNameKind.nameForOneShotInterceptor: |
| return 'ModularName"oneshot:${_selectorText(data as Selector)}"'; |
| case ModularNameKind.globalNameForInterfaceTypeVariable: |
| break; |
| case ModularNameKind.nameForGetInterceptor: |
| return 'ModularName"getInterceptor"'; |
| case ModularNameKind.asName: |
| return 'ModularName"asName:$data"'; |
| } |
| return super.nonfinalizedDebugText(); |
| } |
| |
| String _className(ClassEntity cls) { |
| return cls.name.replaceAll('&', '_'); |
| } |
| |
| String _qualifiedStaticName(MemberEntity member) { |
| if (member is ConstructorEntity || member.isStatic) { |
| return '${_className(member.enclosingClass!)}.${member.name!}'; |
| } |
| return member.name!; |
| } |
| |
| String _instanceMethodInvocationName(MemberEntity member) { |
| String invocationName = operatorNameToIdentifier(member.name)!; |
| if (member.isGetter) invocationName = r'get$' + invocationName; |
| if (member.isSetter) invocationName = r'set$' + invocationName; |
| return invocationName; |
| } |
| |
| String _instanceMethodName(MemberEntity member) { |
| if (member is ConstructorBodyEntity) { |
| return 'constructorBody:${_qualifiedStaticName(member.constructor)}'; |
| } |
| if (member is JGeneratorBody) { |
| MemberEntity function = member.function; |
| return 'generatorBody:' |
| '${_className(function.enclosingClass!)}.' |
| '${_instanceMethodInvocationName(function)}'; |
| } |
| return 'instanceMethod:${_instanceMethodInvocationName(member)}'; |
| } |
| |
| String _selectorText(Selector selector) { |
| // Approximation to unminified selector. |
| if (selector.isGetter) return r'get$' + selector.name; |
| if (selector.isSetter) return r'set$' + selector.name; |
| if (selector.isOperator || selector.isIndex || selector.isIndexSet) { |
| return operatorNameToIdentifier(selector.name)!; |
| } |
| List<String> parts = [ |
| selector.name, |
| if (selector.callStructure.typeArgumentCount > 0) |
| '${selector.callStructure.typeArgumentCount}', |
| '${selector.callStructure.argumentCount}', |
| ...selector.callStructure.getOrderedNamedArguments(), |
| ]; |
| return parts.join(r'$'); |
| } |
| } |
| |
| /// Interface for reading the code generation results for all [MemberEntity]s. |
| abstract class CodegenResults { |
| CodegenInputs get codegenInputs; |
| ({CodegenResult result, bool isGenerated}) getCodegenResults( |
| MemberEntity member, |
| ); |
| } |
| |
| /// Deserialized code generation results. |
| /// |
| /// This is used for modular code generation. |
| class DeserializedCodegenResults implements CodegenResults { |
| @override |
| final CodegenInputs codegenInputs; |
| final FunctionCompiler _functionCompiler; |
| |
| final Map<MemberEntity, CodegenResult> _map; |
| |
| DeserializedCodegenResults( |
| this.codegenInputs, |
| this._map, |
| this._functionCompiler, |
| ); |
| |
| @override |
| ({CodegenResult result, bool isGenerated}) getCodegenResults( |
| MemberEntity member, |
| ) { |
| // We only access these results once as it is picked up by the work queue |
| // so it is safe to remove and free up space in the map. With deferred |
| // deserialization this will also free the Deferrable holder. |
| // Some entities such as parameter stubs are generated lazily and so we have |
| // to compile them on the fly. |
| final deserialized = _map.remove(member); |
| if (deserialized != null) return (result: deserialized, isGenerated: false); |
| return (result: _functionCompiler.compile(member), isGenerated: true); |
| } |
| } |