private doFingerprint()

in src/jsii/fingerprinting.ts [44:147]


  private doFingerprint(fqn: string, recursionBreaker: Set<string>) {
    // eslint-disable-next-line @typescript-eslint/no-this-alias
    const self = this;

    const existing = this.cache.get(fqn);
    if (existing) {
      return existing;
    }

    const hash = crypto.createHash('sha256');
    hash.update(fqn);

    const type = this.findType(fqn);
    if (type) {
      hash.update(type.kind);
      switch (type.kind) {
        case spec.TypeKind.Enum:
          for (const member of sortedByName(type.members)) {
            hash.update(member.name);
          }
          break;
        case spec.TypeKind.Class:
        case spec.TypeKind.Interface:
          if (type.kind === spec.TypeKind.Class) {
            visitType(type.base);
            visitCallable(type.initializer);
          }

          for (const prop of sortedByName(type.properties ?? [])) {
            hash.update(prop.name);
            visitBools(prop.immutable, prop.static, prop.optional, prop.protected);
            visitTypeReference(prop.type);
          }
          for (const method of sortedByName(type.methods ?? [])) {
            hash.update(method.name);
            visitCallable(method);
            visitBools(method.returns?.optional);
            visitTypeReference(method.returns?.type);
          }
          for (const implint of type.interfaces ?? []) {
            visitType(implint);
          }

          break;
      }
    }

    const ret = hash.digest('hex');
    this.cache.set(fqn, ret);
    return ret;

    function visitType(fqnStr?: string) {
      if (!fqnStr) {
        return;
      }

      if (recursionBreaker.has(fqnStr)) {
        hash.update('$RECURSION$');
        return;
      }

      recursionBreaker.add(fqnStr);
      hash.update(self.doFingerprint(fqnStr, recursionBreaker));
      recursionBreaker.delete(fqnStr);
    }

    function visitCallable(callable?: spec.Callable) {
      if (!callable) {
        return;
      }

      visitBools(callable.protected);
      for (const param of callable.parameters ?? []) {
        visitBools(param.optional, param.variadic);
        visitTypeReference(param.type);
      }
    }

    function visitTypeReference(typeRef?: spec.TypeReference) {
      if (!typeRef) {
        return;
      }

      if (spec.isPrimitiveTypeReference(typeRef)) {
        hash.update(typeRef.primitive);
      }
      if (spec.isNamedTypeReference(typeRef)) {
        visitType(typeRef.fqn);
      }
      if (spec.isCollectionTypeReference(typeRef)) {
        hash.update(typeRef.collection.kind);
        visitTypeReference(typeRef.collection.elementtype);
      }
      if (spec.isUnionTypeReference(typeRef)) {
        for (const t of typeRef.union.types) {
          visitTypeReference(t);
        }
      }
    }

    function visitBools(...vs: Array<boolean | undefined>) {
      hash.update(vs.map((v) => (v ? '1' : '0')).join(''));
    }
  }