| // 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 '../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/deferred_holder_expression.dart' | |
| show DeferredHolderExpression; | |
| import '../js_backend/namer.dart' | |
| show AsyncName, Namer, operatorNameToIdentifier, StringBackedName; | |
| 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/recorded_use.dart' show RecordedUse; | |
| 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'; | |
| import 'elements.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, | |
| restParameter, | |
| 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 restParameter = 'js-rest-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, recordUse } | |
| /// 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 RecordedUse) { | |
| sink.writeEnum(JsAnnotationKind.recordUse); | |
| 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 visitRestParameter(js.RestParameter node) { | |
| sink.writeEnum(JsNodeKind.restParameter); | |
| sink.begin(JsNodeTags.restParameter); | |
| sink.writeString(node.name); | |
| sink.end(JsNodeTags.restParameter); | |
| _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.restParameter: | |
| source.begin(JsNodeTags.restParameter); | |
| node = js.RestParameter(source.readString()); | |
| source.end(JsNodeTags.restParameter); | |
| 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() { | |
| return switch (source.readEnum(JsAnnotationKind.values)) { | |
| JsAnnotationKind.string => source.readString(), | |
| JsAnnotationKind.recordUse => RecordedUse.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); | |
| } | |
| } |