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.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);
}