58e577da15
Change-Id: Idaed02bb747670413f63e7f7b0797eb591e12f57 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/231523 Reviewed-by: Mark Zhou <markzipan@google.com> Commit-Queue: Joshua Litt <joshualitt@google.com>
2279 lines
72 KiB
Dart
2279 lines
72 KiB
Dart
// 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 dart2js.common.codegen;
|
|
|
|
import 'package:js_ast/src/precedence.dart' as js show PRIMARY;
|
|
|
|
import '../common.dart';
|
|
import '../common/elements.dart';
|
|
import '../constants/values.dart';
|
|
import '../deferred_load/output_unit.dart';
|
|
import '../elements/entities.dart';
|
|
import '../elements/types.dart' show DartType, InterfaceType;
|
|
import '../inferrer/abstract_value_domain.dart';
|
|
import '../inferrer/types.dart';
|
|
import '../io/source_information.dart';
|
|
import '../js/js.dart' as js;
|
|
import '../js_backend/backend.dart';
|
|
import '../js_backend/namer.dart';
|
|
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/code_emitter_task.dart' show Emitter;
|
|
import '../js_model/elements.dart' show JGeneratorBody;
|
|
import '../js_model/type_recipe.dart' show TypeRecipe;
|
|
import '../native/behavior.dart';
|
|
import '../serialization/serialization.dart';
|
|
import '../ssa/ssa.dart';
|
|
import '../universe/feature.dart';
|
|
import '../universe/selector.dart';
|
|
import '../universe/use.dart' show ConstantUse, DynamicUse, StaticUse, TypeUse;
|
|
import '../universe/world_impact.dart'
|
|
show WorldImpact, WorldImpactBuilderImpl, WorldImpactVisitor;
|
|
import '../util/enumset.dart';
|
|
import '../util/util.dart';
|
|
import '../world.dart';
|
|
|
|
class CodegenImpact extends WorldImpact {
|
|
const CodegenImpact();
|
|
|
|
factory CodegenImpact.readFromDataSource(DataSource source) =
|
|
_CodegenImpact.readFromDataSource;
|
|
|
|
void writeToDataSink(DataSink sink) {
|
|
throw UnsupportedError('CodegenImpact.writeToDataSink');
|
|
}
|
|
|
|
Iterable<Pair<DartType, DartType>> get typeVariableBoundsSubtypeChecks {
|
|
return const <Pair<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';
|
|
|
|
@override
|
|
final MemberEntity member;
|
|
Set<Pair<DartType, DartType>> _typeVariableBoundsSubtypeChecks;
|
|
Set<String> _constSymbols;
|
|
List<Set<ClassEntity>> _specializedGetInterceptors;
|
|
bool _usesInterceptor = false;
|
|
EnumSet<AsyncMarker> _asyncMarkers;
|
|
Set<GenericInstantiation> _genericInstantiations;
|
|
List<NativeBehavior> _nativeBehaviors;
|
|
Set<FunctionEntity> _nativeMethods;
|
|
Set<Selector> _oneShotInterceptors;
|
|
|
|
_CodegenImpact(this.member);
|
|
|
|
_CodegenImpact.internal(
|
|
this.member,
|
|
Set<DynamicUse> dynamicUses,
|
|
Set<StaticUse> staticUses,
|
|
Set<TypeUse> typeUses,
|
|
Set<ConstantUse> constantUses,
|
|
this._typeVariableBoundsSubtypeChecks,
|
|
this._constSymbols,
|
|
this._specializedGetInterceptors,
|
|
this._usesInterceptor,
|
|
this._asyncMarkers,
|
|
this._genericInstantiations,
|
|
this._nativeBehaviors,
|
|
this._nativeMethods,
|
|
this._oneShotInterceptors)
|
|
: super.internal(dynamicUses, staticUses, typeUses, constantUses);
|
|
|
|
factory _CodegenImpact.readFromDataSource(DataSource source) {
|
|
source.begin(tag);
|
|
MemberEntity member = source.readMember();
|
|
Set<DynamicUse> dynamicUses = source
|
|
.readList(() => DynamicUse.readFromDataSource(source),
|
|
emptyAsNull: true)
|
|
?.toSet();
|
|
Set<StaticUse> staticUses = source
|
|
.readList(() => StaticUse.readFromDataSource(source), emptyAsNull: true)
|
|
?.toSet();
|
|
Set<TypeUse> typeUses = source
|
|
.readList(() => TypeUse.readFromDataSource(source), emptyAsNull: true)
|
|
?.toSet();
|
|
Set<ConstantUse> constantUses = source
|
|
.readList(() => ConstantUse.readFromDataSource(source),
|
|
emptyAsNull: true)
|
|
?.toSet();
|
|
Set<Pair<DartType, DartType>> typeVariableBoundsSubtypeChecks =
|
|
source.readList(() {
|
|
return Pair(source.readDartType(), source.readDartType());
|
|
}, emptyAsNull: true)?.toSet();
|
|
Set<String> constSymbols = source.readStrings(emptyAsNull: true)?.toSet();
|
|
List<Set<ClassEntity>> specializedGetInterceptors = source.readList(() {
|
|
return source.readClasses().toSet();
|
|
}, emptyAsNull: true);
|
|
bool usesInterceptor = source.readBool();
|
|
int asyncMarkersValue = source.readIntOrNull();
|
|
EnumSet<AsyncMarker> asyncMarkers =
|
|
asyncMarkersValue != null ? EnumSet.fromValue(asyncMarkersValue) : null;
|
|
Set<GenericInstantiation> genericInstantiations = source
|
|
.readList(() => GenericInstantiation.readFromDataSource(source),
|
|
emptyAsNull: true)
|
|
?.toSet();
|
|
List<NativeBehavior> nativeBehaviors = source.readList(
|
|
() => NativeBehavior.readFromDataSource(source),
|
|
emptyAsNull: true);
|
|
Set<FunctionEntity> nativeMethods =
|
|
source.readMembers<FunctionEntity>(emptyAsNull: true)?.toSet();
|
|
Set<Selector> oneShotInterceptors = source
|
|
.readList(() => Selector.readFromDataSource(source), emptyAsNull: true)
|
|
?.toSet();
|
|
source.end(tag);
|
|
return _CodegenImpact.internal(
|
|
member,
|
|
dynamicUses,
|
|
staticUses,
|
|
typeUses,
|
|
constantUses,
|
|
typeVariableBoundsSubtypeChecks,
|
|
constSymbols,
|
|
specializedGetInterceptors,
|
|
usesInterceptor,
|
|
asyncMarkers,
|
|
genericInstantiations,
|
|
nativeBehaviors,
|
|
nativeMethods,
|
|
oneShotInterceptors);
|
|
}
|
|
|
|
@override
|
|
void writeToDataSink(DataSink sink) {
|
|
sink.begin(tag);
|
|
sink.writeMember(member);
|
|
sink.writeList(dynamicUses, (DynamicUse use) => use.writeToDataSink(sink),
|
|
allowNull: true);
|
|
sink.writeList(staticUses, (StaticUse use) => use.writeToDataSink(sink),
|
|
allowNull: true);
|
|
sink.writeList(typeUses, (TypeUse use) => use.writeToDataSink(sink),
|
|
allowNull: true);
|
|
sink.writeList(constantUses, (ConstantUse use) => use.writeToDataSink(sink),
|
|
allowNull: true);
|
|
sink.writeList<Pair<DartType, DartType>>(_typeVariableBoundsSubtypeChecks,
|
|
(pair) {
|
|
sink.writeDartType(pair.a);
|
|
sink.writeDartType(pair.b);
|
|
}, allowNull: true);
|
|
sink.writeStrings(_constSymbols, allowNull: true);
|
|
sink.writeList(_specializedGetInterceptors, sink.writeClasses,
|
|
allowNull: true);
|
|
sink.writeBool(_usesInterceptor);
|
|
sink.writeIntOrNull(_asyncMarkers?.value);
|
|
sink.writeList(
|
|
_genericInstantiations,
|
|
(GenericInstantiation instantiation) =>
|
|
instantiation.writeToDataSink(sink),
|
|
allowNull: true);
|
|
sink.writeList(_nativeBehaviors,
|
|
(NativeBehavior behavior) => behavior.writeToDataSink(sink),
|
|
allowNull: true);
|
|
sink.writeMembers(_nativeMethods, allowNull: true);
|
|
sink.writeList(_oneShotInterceptors,
|
|
(Selector selector) => selector.writeToDataSink(sink),
|
|
allowNull: true);
|
|
sink.end(tag);
|
|
}
|
|
|
|
@override
|
|
void apply(WorldImpactVisitor visitor) {
|
|
staticUses.forEach((StaticUse use) => visitor.visitStaticUse(member, use));
|
|
dynamicUses.forEach((DynamicUse use) => visitor.visitDynamicUse);
|
|
typeUses.forEach((TypeUse use) => visitor.visitTypeUse(member, use));
|
|
constantUses
|
|
.forEach((ConstantUse use) => visitor.visitConstantUse(member, use));
|
|
}
|
|
|
|
void registerTypeVariableBoundsSubtypeCheck(
|
|
DartType subtype, DartType supertype) {
|
|
_typeVariableBoundsSubtypeChecks ??= {};
|
|
_typeVariableBoundsSubtypeChecks
|
|
.add(Pair<DartType, DartType>(subtype, supertype));
|
|
}
|
|
|
|
@override
|
|
Iterable<Pair<DartType, DartType>> get typeVariableBoundsSubtypeChecks {
|
|
return _typeVariableBoundsSubtypeChecks ??
|
|
const <Pair<DartType, DartType>>[];
|
|
}
|
|
|
|
void registerConstSymbol(String name) {
|
|
_constSymbols ??= {};
|
|
_constSymbols.add(name);
|
|
}
|
|
|
|
@override
|
|
Iterable<String> get constSymbols {
|
|
return _constSymbols ?? const <String>[];
|
|
}
|
|
|
|
void registerSpecializedGetInterceptor(Set<ClassEntity> classes) {
|
|
_specializedGetInterceptors ??= <Set<ClassEntity>>[];
|
|
_specializedGetInterceptors.add(classes);
|
|
}
|
|
|
|
@override
|
|
Iterable<Set<ClassEntity>> get specializedGetInterceptors {
|
|
return _specializedGetInterceptors ?? const <Set<ClassEntity>>[];
|
|
}
|
|
|
|
void registerUseInterceptor() {
|
|
_usesInterceptor = true;
|
|
}
|
|
|
|
@override
|
|
bool get usesInterceptor => _usesInterceptor;
|
|
|
|
void registerAsyncMarker(AsyncMarker asyncMarker) {
|
|
_asyncMarkers ??= EnumSet();
|
|
_asyncMarkers.add(asyncMarker);
|
|
}
|
|
|
|
@override
|
|
Iterable<AsyncMarker> get asyncMarkers {
|
|
return _asyncMarkers != null
|
|
? _asyncMarkers.iterable(AsyncMarker.values)
|
|
: const <AsyncMarker>[];
|
|
}
|
|
|
|
void registerGenericInstantiation(GenericInstantiation instantiation) {
|
|
_genericInstantiations ??= {};
|
|
_genericInstantiations.add(instantiation);
|
|
}
|
|
|
|
@override
|
|
Iterable<GenericInstantiation> get genericInstantiations {
|
|
return _genericInstantiations ?? const <GenericInstantiation>[];
|
|
}
|
|
|
|
void registerNativeBehavior(NativeBehavior nativeBehavior) {
|
|
_nativeBehaviors ??= [];
|
|
_nativeBehaviors.add(nativeBehavior);
|
|
}
|
|
|
|
@override
|
|
Iterable<NativeBehavior> get nativeBehaviors {
|
|
return _nativeBehaviors ?? const <NativeBehavior>[];
|
|
}
|
|
|
|
void registerNativeMethod(FunctionEntity function) {
|
|
_nativeMethods ??= {};
|
|
_nativeMethods.add(function);
|
|
}
|
|
|
|
@override
|
|
Iterable<FunctionEntity> get nativeMethods {
|
|
return _nativeMethods ?? const [];
|
|
}
|
|
|
|
void registerOneShotInterceptor(Selector selector) {
|
|
_oneShotInterceptors ??= {};
|
|
_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 iterable) {
|
|
if (iterable.isNotEmpty) {
|
|
sb.write('\n $title:');
|
|
iterable.forEach((e) => 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;
|
|
List<ModularName> _names;
|
|
List<ModularExpression> _expressions;
|
|
|
|
CodegenRegistry(this._elementEnvironment, this._currentElement)
|
|
: this._worldImpact = _CodegenImpact(_currentElement);
|
|
|
|
@override
|
|
String toString() => 'CodegenRegistry for $_currentElement';
|
|
|
|
@deprecated
|
|
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(ModularName name) {
|
|
_names ??= [];
|
|
_names.add(name);
|
|
}
|
|
|
|
void registerModularExpression(ModularExpression expression) {
|
|
_expressions ??= [];
|
|
_expressions.add(expression);
|
|
}
|
|
|
|
CodegenResult close(js.Fun code) {
|
|
return CodegenResult(
|
|
code, _worldImpact, _names ?? const [], _expressions ?? const []);
|
|
}
|
|
}
|
|
|
|
/// Interface for reading the code generation results for all [MemberEntity]s.
|
|
abstract class CodegenResults {
|
|
GlobalTypeInferenceResults get globalTypeInferenceResults;
|
|
CodegenInputs get codegenInputs;
|
|
CodegenResult getCodegenResults(MemberEntity member);
|
|
}
|
|
|
|
/// Code generation results computed on-demand.
|
|
///
|
|
/// This is used in the non-modular codegen enqueuer driving code generation.
|
|
class OnDemandCodegenResults extends CodegenResults {
|
|
@override
|
|
final GlobalTypeInferenceResults globalTypeInferenceResults;
|
|
@override
|
|
final CodegenInputs codegenInputs;
|
|
final SsaFunctionCompiler _functionCompiler;
|
|
|
|
OnDemandCodegenResults(this.globalTypeInferenceResults, this.codegenInputs,
|
|
this._functionCompiler);
|
|
|
|
@override
|
|
CodegenResult getCodegenResults(MemberEntity member) {
|
|
return _functionCompiler.compile(member);
|
|
}
|
|
}
|
|
|
|
/// Deserialized code generation results.
|
|
///
|
|
/// This is used for modular code generation.
|
|
class DeserializedCodegenResults extends CodegenResults {
|
|
@override
|
|
final GlobalTypeInferenceResults globalTypeInferenceResults;
|
|
@override
|
|
final CodegenInputs codegenInputs;
|
|
|
|
final Map<MemberEntity, CodegenResult> _map;
|
|
|
|
DeserializedCodegenResults(
|
|
this.globalTypeInferenceResults, this.codegenInputs, this._map);
|
|
|
|
@override
|
|
CodegenResult getCodegenResults(MemberEntity member) {
|
|
CodegenResult result = _map[member];
|
|
if (result == null) {
|
|
failedAt(member,
|
|
"No codegen results from $member (${identityHashCode(member)}).");
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/// The code generation result for a single [MemberEntity].
|
|
class CodegenResult {
|
|
static const String tag = 'codegen-result';
|
|
|
|
final js.Fun code;
|
|
final CodegenImpact impact;
|
|
final Iterable<ModularName> modularNames;
|
|
final Iterable<ModularExpression> modularExpressions;
|
|
|
|
CodegenResult(this.code, this.impact, List<ModularName> modularNames,
|
|
List<ModularExpression> modularExpressions)
|
|
: this.modularNames =
|
|
modularNames.isEmpty ? const [] : List.unmodifiable(modularNames),
|
|
this.modularExpressions = modularExpressions.isEmpty
|
|
? const []
|
|
: List.unmodifiable(modularExpressions);
|
|
|
|
/// Reads a [CodegenResult] object from [source].
|
|
///
|
|
/// The [ModularName] and [ModularExpression] nodes read during
|
|
/// deserialization are collected in [modularNames] and [modularExpressions]
|
|
/// to avoid the need for visiting the [code] node post deserialization.
|
|
factory CodegenResult.readFromDataSource(
|
|
DataSource source,
|
|
List<ModularName> modularNames,
|
|
List<ModularExpression> modularExpressions) {
|
|
source.begin(tag);
|
|
js.Fun code = source.readJsNodeOrNull();
|
|
CodegenImpact impact = CodegenImpact.readFromDataSource(source);
|
|
source.end(tag);
|
|
return CodegenResult(code, impact, modularNames, modularExpressions);
|
|
}
|
|
|
|
/// Writes the [CodegenResult] object to [sink].
|
|
///
|
|
/// The [modularNames] and [modularExpressions] fields are not directly
|
|
/// serializes because these are embedded in the [code] node and collected
|
|
/// through this during deserialization.
|
|
void writeToDataSink(DataSink sink) {
|
|
sink.begin(tag);
|
|
sink.writeJsNodeOrNull(code);
|
|
impact.writeToDataSink(sink);
|
|
sink.end(tag);
|
|
}
|
|
|
|
void applyModularState(Namer namer, Emitter emitter) {
|
|
for (ModularName name in modularNames) {
|
|
switch (name.kind) {
|
|
case ModularNameKind.rtiField:
|
|
name.value = namer.rtiFieldJsName;
|
|
break;
|
|
case ModularNameKind.className:
|
|
name.value = namer.className(name.data);
|
|
break;
|
|
case ModularNameKind.aliasedSuperMember:
|
|
name.value = namer.aliasedSuperMemberPropertyName(name.data);
|
|
break;
|
|
case ModularNameKind.staticClosure:
|
|
name.value = namer.staticClosureName(name.data);
|
|
break;
|
|
case ModularNameKind.methodProperty:
|
|
name.value = namer.methodPropertyName(name.data);
|
|
break;
|
|
case ModularNameKind.operatorIs:
|
|
name.value = namer.operatorIs(name.data);
|
|
break;
|
|
case ModularNameKind.instanceMethod:
|
|
name.value = namer.instanceMethodName(name.data);
|
|
break;
|
|
case ModularNameKind.instanceField:
|
|
name.value = namer.instanceFieldPropertyName(name.data);
|
|
break;
|
|
case ModularNameKind.invocation:
|
|
name.value = namer.invocationName(name.data);
|
|
break;
|
|
case ModularNameKind.lazyInitializer:
|
|
name.value = namer.lazyInitializerName(name.data);
|
|
break;
|
|
case ModularNameKind.globalPropertyNameForClass:
|
|
name.value = namer.globalPropertyNameForClass(name.data);
|
|
break;
|
|
case ModularNameKind.globalPropertyNameForMember:
|
|
name.value = namer.globalPropertyNameForMember(name.data);
|
|
break;
|
|
case ModularNameKind.globalNameForInterfaceTypeVariable:
|
|
name.value = namer.globalNameForInterfaceTypeVariable(name.data);
|
|
break;
|
|
case ModularNameKind.nameForGetInterceptor:
|
|
name.value = namer.nameForGetInterceptor(name.set);
|
|
break;
|
|
case ModularNameKind.nameForOneShotInterceptor:
|
|
name.value = namer.nameForOneShotInterceptor(name.data, name.set);
|
|
break;
|
|
case ModularNameKind.asName:
|
|
name.value = namer.asName(name.data);
|
|
break;
|
|
}
|
|
}
|
|
for (ModularExpression expression in modularExpressions) {
|
|
switch (expression.kind) {
|
|
case ModularExpressionKind.constant:
|
|
expression.value = emitter
|
|
.constantReference(expression.data)
|
|
.withSourceInformation(expression.sourceInformation);
|
|
break;
|
|
case ModularExpressionKind.embeddedGlobalAccess:
|
|
expression.value = emitter
|
|
.generateEmbeddedGlobalAccess(expression.data)
|
|
.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=$modularNames,');
|
|
sb.write('modularExpressions=$modularExpressions');
|
|
sb.write(')');
|
|
return sb.toString();
|
|
}
|
|
}
|
|
|
|
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(DataSource 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(DataSink 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);
|
|
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);
|
|
break;
|
|
case ModularNameKind.invocation:
|
|
Selector selector = data;
|
|
selector.writeToDataSink(sink);
|
|
break;
|
|
case ModularNameKind.globalNameForInterfaceTypeVariable:
|
|
TypeVariableEntity typeVariable = data;
|
|
sink.writeTypeVariable(typeVariable);
|
|
break;
|
|
case ModularNameKind.nameForGetInterceptor:
|
|
sink.writeClasses(set);
|
|
break;
|
|
case ModularNameKind.nameForOneShotInterceptor:
|
|
Selector selector = data;
|
|
selector.writeToDataSink(sink);
|
|
sink.writeClasses(set);
|
|
break;
|
|
case ModularNameKind.asName:
|
|
sink.writeString(data);
|
|
break;
|
|
}
|
|
sink.end(tag);
|
|
}
|
|
|
|
@override
|
|
bool get isFinalized => _value != null;
|
|
|
|
js.Name get value {
|
|
assert(isFinalized, 'value not set for $this');
|
|
return _value;
|
|
}
|
|
|
|
void set value(js.Name node) {
|
|
assert(!isFinalized);
|
|
assert(node != null);
|
|
_value = node.withSourceInformation(sourceInformation);
|
|
}
|
|
|
|
@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)}"';
|
|
case ModularNameKind.methodProperty:
|
|
return 'ModularName"methodProperty:${(data as Entity).name}"';
|
|
case ModularNameKind.operatorIs:
|
|
return 'ModularName"is:${_className(data)}"';
|
|
case ModularNameKind.className:
|
|
return 'ModularName"class:${_className(data)}"';
|
|
case ModularNameKind.globalPropertyNameForClass:
|
|
return 'ModularName"classref:${_className(data)}"';
|
|
case ModularNameKind.aliasedSuperMember:
|
|
MemberEntity member = (data as MemberEntity);
|
|
String className = _className(member.enclosingClass);
|
|
String invocationName = Namer.operatorNameToIdentifier(member.name);
|
|
final description = "$className.$invocationName";
|
|
return 'ModularName"alias:$description"';
|
|
case ModularNameKind.staticClosure:
|
|
return 'ModularName"closure:${_qualifiedStaticName(data)}"';
|
|
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.isConstructor || member.isStatic) {
|
|
return '${_className(member.enclosingClass)}.${member.name}';
|
|
}
|
|
return member.name;
|
|
}
|
|
|
|
String _instanceMethodInvocationName(MemberEntity member) {
|
|
String invocationName = Namer.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 Namer.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'$');
|
|
}
|
|
}
|
|
|
|
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(DataSource 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(DataSink sink) {
|
|
sink.begin(tag);
|
|
sink.writeEnum(kind);
|
|
switch (kind) {
|
|
case ModularExpressionKind.constant:
|
|
sink.writeConstant(data);
|
|
break;
|
|
case ModularExpressionKind.embeddedGlobalAccess:
|
|
sink.writeString(data);
|
|
break;
|
|
}
|
|
sink.end(tag);
|
|
}
|
|
|
|
@override
|
|
bool get isFinalized => _value != null;
|
|
|
|
@override
|
|
js.Expression get value {
|
|
assert(isFinalized);
|
|
return _value;
|
|
}
|
|
|
|
void set value(js.Expression node) {
|
|
assert(!isFinalized);
|
|
assert(node != null);
|
|
_value = node.withSourceInformation(sourceInformation);
|
|
}
|
|
|
|
@override
|
|
int get precedenceLevel => _value?.precedenceLevel ?? js.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"';
|
|
}
|
|
return super.nonfinalizedDebugText();
|
|
}
|
|
}
|
|
|
|
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';
|
|
}
|
|
|
|
/// Visitor that serializes a [js.Node] into a [DataSink].
|
|
class JsNodeSerializer implements js.NodeVisitor<void> {
|
|
final DataSink sink;
|
|
|
|
JsNodeSerializer._(this.sink);
|
|
|
|
static void writeToDataSink(DataSink sink, js.Node node) {
|
|
sink.begin(JsNodeTags.tag);
|
|
JsNodeSerializer serializer = JsNodeSerializer._(sink);
|
|
serializer.visit(node);
|
|
sink.end(JsNodeTags.tag);
|
|
}
|
|
|
|
void visit(js.Node node, {bool allowNull = false}) {
|
|
if (allowNull) {
|
|
sink.writeBool(node != null);
|
|
if (node != null) {
|
|
node.accept(this);
|
|
}
|
|
} else {
|
|
node.accept(this);
|
|
}
|
|
}
|
|
|
|
void visitList(Iterable<js.Node> nodes) {
|
|
sink.writeList(nodes, visit);
|
|
}
|
|
|
|
void _writeInfo(js.Node node) {
|
|
sink.writeCached<SourceInformation>(node.sourceInformation,
|
|
(SourceInformation sourceInformation) {
|
|
SourceInformation.writeToDataSink(sink, sourceInformation);
|
|
});
|
|
}
|
|
|
|
@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);
|
|
node.writeToDataSink(sink);
|
|
sink.end(JsNodeTags.modularName);
|
|
_writeInfo(node);
|
|
} 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);
|
|
node.writeToDataSink(sink);
|
|
sink.end(JsNodeTags.modularExpression);
|
|
_writeInfo(node);
|
|
} else if (node is TypeReference) {
|
|
sink.writeEnum(JsNodeKind.typeReference);
|
|
sink.begin(JsNodeTags.typeReference);
|
|
node.writeToDataSink(sink);
|
|
sink.end(JsNodeTags.typeReference);
|
|
_writeInfo(node);
|
|
} else if (node is StringReference) {
|
|
sink.writeEnum(JsNodeKind.stringReference);
|
|
sink.begin(JsNodeTags.stringReference);
|
|
node.writeToDataSink(sink);
|
|
sink.end(JsNodeTags.stringReference);
|
|
_writeInfo(node);
|
|
} else if (node is DeferredHolderExpression) {
|
|
sink.writeEnum(JsNodeKind.deferredHolderExpression);
|
|
sink.begin(JsNodeTags.deferredHolderExpression);
|
|
node.writeToDataSink(sink);
|
|
sink.end(JsNodeTags.deferredHolderExpression);
|
|
_writeInfo(node);
|
|
} 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);
|
|
visit(node.body);
|
|
sink.writeEnum(node.asyncModifier);
|
|
sink.end(JsNodeTags.function);
|
|
_writeInfo(node);
|
|
}
|
|
|
|
@override
|
|
void visitArrowFunction(js.ArrowFunction node) {
|
|
sink.writeEnum(JsNodeKind.arrowFunction);
|
|
sink.begin(JsNodeTags.function);
|
|
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);
|
|
visit(node.value, allowNull: true);
|
|
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);
|
|
visit(node.catchPart, allowNull: true);
|
|
visit(node.finallyPart, allowNull: true);
|
|
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);
|
|
visit(node.value, allowNull: true);
|
|
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);
|
|
visit(node.init, allowNull: true);
|
|
visit(node.condition, allowNull: true);
|
|
visit(node.update, allowNull: true);
|
|
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 [DataSource].
|
|
///
|
|
/// Deserialized [ModularName]s and [ModularExpression]s are collected in the
|
|
/// [modularNames] and [modularExpressions] lists.
|
|
class JsNodeDeserializer {
|
|
final DataSource source;
|
|
final List<ModularName> modularNames;
|
|
final List<ModularExpression> modularExpressions;
|
|
|
|
JsNodeDeserializer._(this.source, this.modularNames, this.modularExpressions);
|
|
|
|
static js.Node readFromDataSource(
|
|
DataSource source,
|
|
List<ModularName> modularNames,
|
|
List<ModularExpression> modularExpressions) {
|
|
source.begin(JsNodeTags.tag);
|
|
JsNodeDeserializer deserializer =
|
|
JsNodeDeserializer._(source, modularNames, modularExpressions);
|
|
js.Node node = deserializer.read();
|
|
source.end(JsNodeTags.tag);
|
|
return node;
|
|
}
|
|
|
|
T read<T extends js.Node>({bool allowNull = false}) {
|
|
if (allowNull) {
|
|
bool hasValue = source.readBool();
|
|
if (!hasValue) return null;
|
|
}
|
|
JsNodeKind kind = source.readEnum(JsNodeKind.values);
|
|
js.Node node;
|
|
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();
|
|
js.Expression function = read();
|
|
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);
|
|
ModularName modularName = ModularName.readFromDataSource(source);
|
|
modularNames.add(modularName);
|
|
node = modularName;
|
|
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);
|
|
ModularExpression modularExpression =
|
|
ModularExpression.readFromDataSource(source);
|
|
modularExpressions.add(modularExpression);
|
|
node = modularExpression;
|
|
source.end(JsNodeTags.modularExpression);
|
|
break;
|
|
case JsNodeKind.function:
|
|
source.begin(JsNodeTags.function);
|
|
List<js.Parameter> params = readList();
|
|
js.Block body = read();
|
|
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.Block 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();
|
|
js.Expression value = source.readValueOrNull(read);
|
|
node = js.VariableInitialization(declaration, value);
|
|
source.end(JsNodeTags.variableInitialization);
|
|
break;
|
|
case JsNodeKind.assignment:
|
|
source.begin(JsNodeTags.assignment);
|
|
js.Expression leftHandSide = read();
|
|
String 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();
|
|
js.Catch catchPart = source.readValueOrNull(read);
|
|
js.Block finallyPart = source.readValueOrNull(read);
|
|
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);
|
|
js.Expression value = source.readValueOrNull(read);
|
|
node = js.Return(value);
|
|
source.end(JsNodeTags.returnStatement);
|
|
break;
|
|
case JsNodeKind.breakStatement:
|
|
source.begin(JsNodeTags.breakStatement);
|
|
String targetLabel = source.readStringOrNull();
|
|
node = js.Break(targetLabel);
|
|
source.end(JsNodeTags.breakStatement);
|
|
break;
|
|
case JsNodeKind.continueStatement:
|
|
source.begin(JsNodeTags.continueStatement);
|
|
String 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);
|
|
js.Expression init = read(allowNull: true);
|
|
js.Expression condition = read(allowNull: true);
|
|
js.Expression update = read(allowNull: true);
|
|
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);
|
|
node = StringReference.readFromDataSource(source);
|
|
source.end(JsNodeTags.stringReference);
|
|
break;
|
|
case JsNodeKind.typeReference:
|
|
source.begin(JsNodeTags.typeReference);
|
|
node = TypeReference.readFromDataSource(source);
|
|
source.end(JsNodeTags.typeReference);
|
|
break;
|
|
case JsNodeKind.deferredHolderExpression:
|
|
source.begin(JsNodeTags.deferredHolderExpression);
|
|
node = DeferredHolderExpression.readFromDataSource(source);
|
|
source.end(JsNodeTags.deferredHolderExpression);
|
|
break;
|
|
}
|
|
SourceInformation sourceInformation =
|
|
source.readCached<SourceInformation>(() {
|
|
return SourceInformation.readFromDataSource(source);
|
|
});
|
|
if (sourceInformation != null) {
|
|
node = node.withSourceInformation(sourceInformation);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
List<T> readList<T extends js.Node>({bool emptyAsNull = false}) {
|
|
return source.readList(read, emptyAsNull: emptyAsNull);
|
|
}
|
|
}
|
|
|
|
class CodegenReaderImpl implements CodegenReader {
|
|
final JClosedWorld closedWorld;
|
|
final List<ModularName> modularNames;
|
|
final List<ModularExpression> modularExpressions;
|
|
|
|
CodegenReaderImpl(
|
|
this.closedWorld, this.modularNames, this.modularExpressions);
|
|
|
|
@override
|
|
AbstractValue readAbstractValue(DataSource source) {
|
|
return closedWorld.abstractValueDomain
|
|
.readAbstractValueFromDataSource(source);
|
|
}
|
|
|
|
@override
|
|
js.Node readJsNode(DataSource source) {
|
|
return JsNodeDeserializer.readFromDataSource(
|
|
source, modularNames, modularExpressions);
|
|
}
|
|
|
|
@override
|
|
OutputUnit readOutputUnitReference(DataSource source) {
|
|
return closedWorld.outputUnitData.outputUnits[source.readInt()];
|
|
}
|
|
|
|
@override
|
|
TypeRecipe readTypeRecipe(DataSource source) {
|
|
return TypeRecipe.readFromDataSource(source);
|
|
}
|
|
}
|
|
|
|
class CodegenWriterImpl implements CodegenWriter {
|
|
final JClosedWorld closedWorld;
|
|
|
|
CodegenWriterImpl(this.closedWorld);
|
|
|
|
@override
|
|
void writeAbstractValue(DataSink sink, AbstractValue value) {
|
|
closedWorld.abstractValueDomain.writeAbstractValueToDataSink(sink, value);
|
|
}
|
|
|
|
@override
|
|
void writeJsNode(DataSink sink, js.Node node) {
|
|
JsNodeSerializer.writeToDataSink(sink, node);
|
|
}
|
|
|
|
@override
|
|
void writeOutputUnitReference(DataSink sink, OutputUnit value) {
|
|
sink.writeInt(closedWorld.outputUnitData.outputUnits.indexOf(value));
|
|
}
|
|
|
|
@override
|
|
void writeTypeRecipe(DataSink sink, TypeRecipe recipe) {
|
|
recipe.writeToDataSink(sink);
|
|
}
|
|
}
|