createProvider method

Expression createProvider(
  1. String propName,
  2. CompileDirectiveMetadata? directiveMetadata,
  3. ProviderAst provider,
  4. List<Expression> providerValueExpressions,
  5. bool isMulti,
  6. bool isEager,
  7. 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.dynamicType,
    );
  } 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.dynamicType;

  // 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.privateStmt,
            o.StmtModifier.lateStmt,
            o.StmtModifier.finalStmt,
          ],
        );
        _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.privateStmt,
              o.StmtModifier.lateStmt,
              o.StmtModifier.finalStmt,
            ],
          );
          _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.lateStmt],
        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.privateStmt],
    );

    final getter = CompileMethod()
      ..addStmts([
        o.DeclareVarStmt('result', storage.buildReadExpr(internalField)),
        o.IfStmt(o.ReadVarExpr('result').equals(o.nullExpr), [
          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);
}