aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/skyframe/SkyFunctionName.java
blob: 107181ed1d01d7f17cd82debb9bc549d25f00572 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2014 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.skyframe;

import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.io.Serializable;
import java.util.Set;
import java.util.concurrent.ExecutionException;

/** An identifier for a {@code SkyFunction}. */
public final class SkyFunctionName implements Serializable {
  private static final Cache<NameOnlyWrapper, SkyFunctionName> interner =
      CacheBuilder.newBuilder().build();

  /**
   * A well-known key type intended for testing only. The associated SkyKey should have a String
   * argument.
   */
  // Needs to be after the cache is initialized.
  public static final SkyFunctionName FOR_TESTING = SkyFunctionName.createHermetic("FOR_TESTING");

  /**
   * Create a SkyFunctionName identified by {@code name} whose evaluation is non-hermetic (its value
   * may not be a pure function of its dependencies. Only use this if the evaluation explicitly
   * consumes data outside of Skyframe, or if the node can be directly invalidated (as opposed to
   * transitively invalidated).
   */
  public static SkyFunctionName createNonHermetic(String name) {
    return create(name, ShareabilityOfValue.ALWAYS, FunctionHermeticity.NONHERMETIC);
  }

  /**
   * Creates a SkyFunctionName identified by {@code name} whose evaluation is hermetic (guaranteed
   * to be a deterministic function of its dependencies, not doing any external operations).
   */
  public static SkyFunctionName createHermetic(String name) {
    return create(name, ShareabilityOfValue.ALWAYS, FunctionHermeticity.HERMETIC);
  }

  public static SkyFunctionName create(
      String name, ShareabilityOfValue shareabilityOfValue, FunctionHermeticity hermeticity) {
    SkyFunctionName result = new SkyFunctionName(name, shareabilityOfValue, hermeticity);
    SkyFunctionName cached;
    try {
      cached = interner.get(new NameOnlyWrapper(result), () -> result);
    } catch (ExecutionException e) {
      throw new IllegalStateException(e);
    }
    Preconditions.checkState(
        result.equals(cached),
        "Tried to create SkyFunctionName objects with same name but different properties: %s %s",
        result,
        cached);
    return cached;
  }

  private final String name;
  private final ShareabilityOfValue shareabilityOfValue;
  private final FunctionHermeticity hermeticity;

  private SkyFunctionName(
      String name, ShareabilityOfValue shareabilityOfValue, FunctionHermeticity hermeticity) {
    this.name = name;
    this.shareabilityOfValue = shareabilityOfValue;
    this.hermeticity = hermeticity;
  }

  public String getName() {
    return name;
  }

  public ShareabilityOfValue getShareabilityOfValue() {
    return shareabilityOfValue;
  }

  public FunctionHermeticity getHermeticity() {
    return hermeticity;
  }

  @Override
  public String toString() {
    return name
        + (shareabilityOfValue.equals(ShareabilityOfValue.ALWAYS) ? "" : " " + shareabilityOfValue);
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (!(obj instanceof SkyFunctionName)) {
      return false;
    }
    SkyFunctionName other = (SkyFunctionName) obj;
    return name.equals(other.name) && shareabilityOfValue.equals(other.shareabilityOfValue);
  }

  @Override
  public int hashCode() {
    // Don't bother incorporating serializabilityOfValue into hashCode: should always be the same.
    return name.hashCode();
  }

  /**
   * A predicate that returns true for {@link SkyKey}s that have the given {@link SkyFunctionName}.
   */
  public static Predicate<SkyKey> functionIs(final SkyFunctionName functionName) {
    return skyKey -> functionName.equals(skyKey.functionName());
  }

  /**
   * A predicate that returns true for {@link SkyKey}s that have the given {@link SkyFunctionName}.
   */
  public static Predicate<SkyKey> functionIsIn(final Set<SkyFunctionName> functionNames) {
    return skyKey -> functionNames.contains(skyKey.functionName());
  }

  private static class NameOnlyWrapper {
    private final SkyFunctionName skyFunctionName;

    private NameOnlyWrapper(SkyFunctionName skyFunctionName) {
      this.skyFunctionName = skyFunctionName;
    }

    @Override
    public boolean equals(Object obj) {
      if (!(obj instanceof NameOnlyWrapper)) {
        return false;
      }
      SkyFunctionName thatFunctionName = ((NameOnlyWrapper) obj).skyFunctionName;
      return this.skyFunctionName.getName().equals(thatFunctionName.name);
    }

    @Override
    public int hashCode() {
      return skyFunctionName.getName().hashCode();
    }
  }
}