createProvider method
Expression
createProvider(
- String propName,
- CompileDirectiveMetadata? directiveMetadata,
- ProviderAst provider,
- List<
Expression> providerValueExpressions, - bool isMulti,
- bool isEager,
- CompileElement compileElement,
Creates a class field and assigns the resolvedProviderValueExpr.
Eager Example: _TemplateRef_9_4 = new TemplateRef(_appEl_9,viewFactory_SampleComponent7);
Lazy:
TemplateRef _TemplateRef_9_4;
Implementation
o.Expression createProvider(
String propName,
CompileDirectiveMetadata? directiveMetadata,
ProviderAst provider,
List<o.Expression> providerValueExpressions,
bool isMulti,
bool isEager,
CompileElement compileElement,
) {
o.Expression resolvedProviderValueExpr;
o.OutputType? type;
if (isMulti) {
resolvedProviderValueExpr = o.literalArr(providerValueExpressions);
type = o.ArrayType(provider.typeArgument != null
? o.importType(
provider.typeArgument,
provider.typeArgument!.typeArguments,
)
: o.DYNAMIC_TYPE);
} else {
resolvedProviderValueExpr = providerValueExpressions.first;
if (directiveMetadata != null) {
// If the provider is backed by a directive, use the directive type
// alongside any specified type arguments to type the field.
type = o.importType(
directiveMetadata.originType,
lookupTypeArgumentsOf(
directiveMetadata.originType!,
compileElement.sourceAst,
),
);
} else if (provider.typeArgument != null) {
type = o.importType(
provider.typeArgument,
provider.typeArgument!.typeArguments,
);
} else {
type = resolvedProviderValueExpr.type;
}
}
type ??= o.DYNAMIC_TYPE;
// TODO(b/198420237): remove this explicit `bool` type when no longer needed
// to work around https://github.com/dart-lang/language/issues/1785
bool providerHasChangeDetector = // ignore: omit_local_variable_types
provider.providerType == ProviderAstType.Directive &&
directiveMetadata != null &&
directiveMetadata.requiresDirectiveChangeDetector;
late CompileIdentifierMetadata changeDetectorClass;
o.OutputType? changeDetectorType;
if (providerHasChangeDetector) {
changeDetectorClass = CompileIdentifierMetadata(
name: directiveMetadata.identifier!.name + 'NgCd',
moduleUrl:
toTemplateExtension(directiveMetadata.identifier!.moduleUrl));
changeDetectorType = o.importType(
changeDetectorClass,
lookupTypeArgumentsOf(
directiveMetadata.originType!,
compileElement.sourceAst,
),
);
}
late List<o.Expression> changeDetectorParams;
if (providerHasChangeDetector) {
changeDetectorParams = [resolvedProviderValueExpr];
}
if (isEager) {
// Check if we need to reach this directive or component beyond the
// contents of the build() function. Otherwise allocate locally.
if (provider.isReferencedOutsideBuild) {
if (providerHasChangeDetector) {
var item = storage.allocate(
propName,
outputType: changeDetectorType,
modifiers: const [
o.StmtModifier.Private,
o.StmtModifier.Late,
o.StmtModifier.Final,
],
);
_createMethod.addStmt(storage
.buildWriteExpr(
item,
o
.importExpr(changeDetectorClass)
.instantiate(changeDetectorParams))
.toStmt());
return o.ReadPropExpr(
o.ReadClassMemberExpr(propName, changeDetectorType),
'instance',
outputType: type,
);
} else {
if (viewType == ViewType.host &&
provider.providerType == ProviderAstType.Component) {
// Host views always have a exactly one component instance, so when
// the provider type is a component, it must be this instance.
// There's no need to allocate a new field for this provider, as
// `HostView` already has a dedicated field for it.
propName = hostViewComponentFieldName;
_createMethod.addStmt(o.ReadClassMemberExpr(propName)
.set(resolvedProviderValueExpr)
.toStmt());
} else {
var item = storage.allocate(
propName,
outputType: type,
modifiers: const [
o.StmtModifier.Private,
o.StmtModifier.Late,
o.StmtModifier.Final,
],
);
_createMethod.addStmt(storage
.buildWriteExpr(item, resolvedProviderValueExpr)
.toStmt());
}
}
} else {
// Since provider is not dynamically reachable and we only need
// the provider locally in build, create a local var.
var localVar = o.variable(propName, type);
_createMethod
.addStmt(localVar.set(resolvedProviderValueExpr).toDeclStmt());
return localVar;
}
} else {
// We don't have to eagerly initialize this object. Add an uninitialized
// class field and provide a getter to construct the provider on demand.
final cachedType = providerHasChangeDetector ? changeDetectorType! : type;
if (providerHasChangeDetector) {
resolvedProviderValueExpr =
o.importExpr(changeDetectorClass).instantiate(changeDetectorParams);
}
if (CompileContext.current.emitNullSafeCode) {
// If null-safety is enabled, use `late` to implement a lazily
// initialized field.
final field = storage.allocate(
propName,
// TODO(b/190556639) - Use final.
modifiers: const [o.StmtModifier.Late],
outputType: type,
initializer: resolvedProviderValueExpr,
);
return storage.buildReadExpr(field);
}
// If null-safety is disabled, manually implement a lazily initialized
// field.
final internalField = storage.allocate(
'_$propName',
outputType: cachedType.asNullable(),
modifiers: const [o.StmtModifier.Private],
);
final getter = CompileMethod()
..addStmts([
o.DeclareVarStmt(
'result',
storage.buildReadExpr(internalField),
),
o.IfStmt(
o.ReadVarExpr('result').equals(o.NULL_EXPR),
[
storage
.buildWriteExpr(
internalField,
o.WriteVarExpr('result', resolvedProviderValueExpr),
)
.toStmt(),
],
),
o.ReturnStatement(
o.ReadVarExpr('result'),
),
]);
getters.add(
o.ClassGetter(
propName,
getter.finish(),
providerHasChangeDetector ? changeDetectorType : type,
),
);
}
return o.ReadClassMemberExpr(propName, type);
}