下面列出了java.lang.invoke.WrongMethodTypeException#com.google.devtools.build.lib.syntax.EvalUtils 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
private Object computeValue(EventHandler eventHandler, AttributeMap rule)
throws EvalException, InterruptedException {
Map<String, Object> attrValues = new HashMap<>();
for (String attrName : rule.getAttributeNames()) {
Attribute attr = rule.getAttributeDefinition(attrName);
if (!attr.hasComputedDefault()) {
Object value = rule.get(attrName, attr.getType());
if (!EvalUtils.isNullOrNone(value)) {
// Some attribute values are not valid Starlark values:
// visibility is an ImmutableList, for example.
attrValues.put(attr.getName(), Starlark.fromJava(value, /*mutability=*/ null));
}
}
}
return invokeCallback(eventHandler, attrValues);
}
@Override
public boolean isImmutable() {
// If the provider is not yet exported, the hash code of the object is subject to change.
// TODO(adonovan): implement isHashable?
if (!getProvider().isExported()) {
return false;
}
// TODO(bazel-team): If we export at the end of a full module's evaluation, instead of at the
// end of every top-level statement, then we can assume that exported implies frozen, and just
// return true here without a traversal.
for (int i = table.length / 2; i < table.length; i++) {
if (!EvalUtils.isImmutable(table[i])) {
return false;
}
}
return true;
}
/**
* Executes the Starlark statements code in the environment defined by the provided {@link
* StarlarkThread}. If the last statement is an expression, doEvaluate returns its value,
* otherwise it returns null.
*
* <p>The caller is responsible for ensuring that the associated Starlark thread isn't currently
* running.
*/
private Object doEvaluate(StarlarkThread thread, String content)
throws SyntaxError.Exception, EvalException, InterruptedException {
try {
servicingEvalRequest.set(true);
// TODO(adonovan): opt: don't parse and resolve the expression every time we hit a breakpoint
// (!).
ParserInput input = ParserInput.create(content, "<debug eval>");
// TODO(adonovan): the module or call frame should be a parameter.
Module module = Module.ofInnermostEnclosingStarlarkFunction(thread);
return EvalUtils.exec(input, FileOptions.DEFAULT, module, thread);
} finally {
servicingEvalRequest.set(false);
}
}
/**
* Creates and starts a worker thread parsing, resolving, and executing the given Starlark file to
* populate the specified module, or if none is given, in a fresh module with a default
* environment.
*/
private static Thread execInWorkerThread(ParserInput input, @Nullable Module module) {
Thread javaThread =
new Thread(
() -> {
try (Mutability mu = Mutability.create("test")) {
StarlarkThread thread = new StarlarkThread(mu, StarlarkSemantics.DEFAULT);
EvalUtils.exec(
input, FileOptions.DEFAULT, module != null ? module : Module.create(), thread);
} catch (SyntaxError.Exception | EvalException | InterruptedException ex) {
throw new AssertionError(ex);
}
});
javaThread.start();
return javaThread;
}
@Nullable
private PatchTransformation maybeGetPatchTransformation(Object patch) throws EvalException {
if (EvalUtils.isNullOrNone(patch)) {
return null;
}
check(
patch instanceof PatchTransformation,
"'%s' is not a patch.apply(...) transformation",
PATCH_FIELD);
return (PatchTransformation) patch;
}
/**
* Converts an object that can be the NoneType to the actual object if it is not
* or returns the default value if none.
*/
@SuppressWarnings("unchecked")
public static <T> T convertFromNoneable(Object obj, @Nullable T defaultValue) {
if (EvalUtils.isNullOrNone(obj)) {
return defaultValue;
}
return (T) obj;
}
/** Evaluates the AST from a single Starlark file, given the already-resolved imports. */
private static Module evalStarlarkBody(
StarlarkSemantics semantics,
StarlarkFile file,
Map<String, Module> imports,
List<RuleInfoWrapper> ruleInfoList,
List<ProviderInfoWrapper> providerInfoList,
List<AspectInfoWrapper> aspectInfoList)
throws InterruptedException, StarlarkEvaluationException {
Module module =
Module.withPredeclared(
semantics, getPredeclaredEnvironment(ruleInfoList, providerInfoList, aspectInfoList));
Resolver.resolveFile(file, module);
if (!file.ok()) {
throw new StarlarkEvaluationException(file.errors().get(0).toString());
}
// execute
try (Mutability mu = Mutability.create("Skydoc")) {
StarlarkThread thread = new StarlarkThread(mu, semantics);
// We use the default print handler, which writes to stderr.
thread.setLoader(imports::get);
EvalUtils.exec(file, module, thread);
} catch (EvalException | InterruptedException ex) {
// This exception class seems a bit unnecessary. Replace with EvalException?
throw new StarlarkEvaluationException("Starlark evaluation error", ex);
}
return module;
}
private static void checkElement(Object x, boolean strict) throws EvalException {
// Historically the requirement for a depset element was isImmutable(x).
// However, this check is neither necessary not sufficient.
// It is unnecessary because elements need only be hashable,
// as with dicts, whose keys may be mutable so long as mutations
// don't affect the hash code. (Elements of a NestedSet must be
// hashable because a hash-based set is used to de-duplicate
// elements during iteration.)
// And it is insufficient because some values are immutable
// but not Starlark-hashable, such as frozen lists.
// NestedSet calls its hashCode method regardless.
//
// TODO(adonovan): use this check instead:
// EvalUtils.checkHashable(x);
// and delete the StarlarkValue.isImmutable and EvalUtils.isImmutable.
// Unfortunately this is a breaking change because some users
// construct depsets whose elements contain lists of strings,
// which are Starlark-unhashable even if frozen.
// TODO(adonovan): also remove StarlarkList.hashCode.
if (strict && !EvalUtils.isImmutable(x)) {
// TODO(adonovan): improve this error message to include type(x).
throw Starlark.errorf("depset elements must not be mutable values");
}
// Even the looser regime forbids the top-level class to be list or dict.
if (x instanceof StarlarkList || x instanceof Dict) {
throw Starlark.errorf("depsets cannot contain items of type '%s'", Starlark.type(x));
}
}
/**
* Like {@link #convert(Object, Object, Object)}, but converts Starlark {@code None} to given
* {@code defaultValue}.
*/
@Nullable
public final T convertOptional(Object x, String what, @Nullable Object context, T defaultValue)
throws ConversionException {
if (EvalUtils.isNullOrNone(x)) {
return defaultValue;
}
return convert(x, what, context);
}
/**
* Converts an object that can be the NoneType to the actual object if it is not or returns the
* default value if none.
*
* <p>This operation is wildly unsound. It performs no dymamic checks (casts), it simply lies
* about the type.
*/
@SuppressWarnings("unchecked")
protected static <T> T convertFromNoneable(Object obj, @Nullable T defaultValue) {
if (EvalUtils.isNullOrNone(obj)) {
return defaultValue;
}
return (T) obj; // totally unsafe
}
private static Object execAndEval(String... lines) {
try {
return EvalUtils.exec(
ParserInput.fromLines(lines), FileOptions.DEFAULT, Module.create(), thread);
} catch (Exception ex) { // SyntaxError | EvalException | InterruptedException
throw new AssertionError("exec failed", ex);
}
}
private static Object eval(String expr)
throws SyntaxError.Exception, EvalException, InterruptedException {
ParserInput input = ParserInput.fromLines(expr);
Module module =
Module.withPredeclared(StarlarkSemantics.DEFAULT, /*predeclared=*/ StarlarkLibrary.COMMON);
try (Mutability mu = Mutability.create()) {
StarlarkThread thread = new StarlarkThread(mu, StarlarkSemantics.DEFAULT);
return EvalUtils.eval(input, FileOptions.DEFAULT, module, thread);
}
}
private void exec(String... lines)
throws SyntaxError.Exception, EvalException, InterruptedException {
ParserInput input = ParserInput.create(Joiner.on("\n").join(lines), "a.star");
Module module =
Module.withPredeclared(
StarlarkSemantics.DEFAULT,
ImmutableMap.of(
"sample", new SamplerValue(),
"myrule", new MyRuleFunction()));
try (Mutability mu = Mutability.create("test")) {
StarlarkThread thread = new StarlarkThread(mu, StarlarkSemantics.DEFAULT);
EvalUtils.exec(input, FileOptions.DEFAULT, module, thread);
}
}
private static void evalAndExport(EvaluationTestCase ev, String... lines) throws Exception {
ParserInput input = ParserInput.fromLines(lines);
Module module = ev.getModule();
StarlarkFile file = EvalUtils.parseAndValidate(input, FileOptions.DEFAULT, module);
if (!file.ok()) {
throw new SyntaxError.Exception(file.errors());
}
BzlLoadFunction.execAndExport(
file, FAKE_LABEL, ev.getEventHandler(), module, ev.getStarlarkThread());
}
@Test
public void testStructMutabilityDeep() throws Exception {
assertThat(EvalUtils.isImmutable(Tuple.<Object>of(makeList(null)))).isTrue();
assertThat(EvalUtils.isImmutable(makeStruct("a", makeList(null)))).isTrue();
assertThat(EvalUtils.isImmutable(makeBigStruct(null))).isTrue();
Mutability mu = Mutability.create("test");
assertThat(EvalUtils.isImmutable(Tuple.<Object>of(makeList(mu)))).isFalse();
assertThat(EvalUtils.isImmutable(makeStruct("a", makeList(mu)))).isFalse();
assertThat(EvalUtils.isImmutable(makeBigStruct(mu))).isFalse();
}
@Override
public boolean isImmutable() {
// Once something's gone immutable, it cannot be made mutable again.
if (immutable) {
return true;
}
immutable =
fieldValues.values().stream()
.allMatch(o -> EvalUtils.isImmutable(Objects.requireNonNull(o)));
return immutable;
}
/**
* Attempt to get a deeply immutable instance of a value passed in from Skylark
*
* <p>Note that if mutable objects are passed in (namely {@link SkylarkList} or {@link
* SkylarkDict}, a copy may be made to get an immutable instance. This may happen in very deeply
* nested structures, so the runtime is variable based on how mutable the objects are. For the
* best performance, only immutable structures should be passed in, as that turns into a simple
* identity function.
*
* @param arg A value from the skylark interpreter. This should only be primitive objects, or
* {@link com.google.devtools.build.lib.skylarkinterface.SkylarkValue} instances
* @return
* <li>The original object if it was already an immutable {@link
* com.google.devtools.build.lib.skylarkinterface.SkylarkValue} or a primitive value
* <li>an immutable {@link SkylarkList} if the original object is a {@link SkylarkList} and
* all values were immutable or could be made immutable. As above, this may be a copy, or
* inner elements may have had to be copied if they were mutable
* <li>An immutable {@link SkylarkDict} if the original object is a {@link SkylarkDict} and
* all keys and values were immutable, or could be made so. Again, note that copies may be
* made in order to make mutable objects immutable
* @throws MutableObjectException If a nested or top level object was mutable, and could not be
* made immutable. This generally only applies to incorrectly implemented native data types
* that are exported to Skylark.
*/
public static Object asDeepImmutable(Object arg) throws MutableObjectException {
// NOTE: For most built in types, this should be reliable. However, if isImmutable is improperly
// implemented on our custom types (that is, it returns "true" when a sub-object is not actually
// immutable), this has the potential to allow an immutable SkylarkList/Dict that contains
// mutable objects. We would rather not pay to iterate and double check for immutability here,
// so for now, just assume things are implemented correctly. The number of user-accessible
// custom objects should be vanishingly small.
// Grab frozen objects and primitives
if (EvalUtils.isImmutable(arg)) {
return arg;
}
if (arg instanceof SkylarkList) {
SkylarkList<?> listArg = ((SkylarkList<?>) arg);
return listArg.stream()
.filter(a -> !EvalUtils.isImmutable(a))
.findFirst()
// if we have a mutable element, like a sub list, try to make it immutable
.map(
mutableElement ->
SkylarkList.createImmutable(
Iterables.transform(listArg, element -> asDeepImmutable(element))))
// Else just copy the list elements into a list with an immutable mutability
// We can't just freeze the list, as it may be mutated elsewhere, but this at least
// elides a copy.
.orElseGet(() -> SkylarkList.createImmutable(listArg));
} else if (arg instanceof SkylarkDict) {
SkylarkDict<?, ?> dictArg = (SkylarkDict<?, ?>) arg;
ImmutableMap.Builder<Object, Object> tempBuilder =
ImmutableMap.builderWithExpectedSize(dictArg.size());
for (Map.Entry<?, ?> entry : dictArg.entrySet()) {
tempBuilder.put(asDeepImmutable(entry.getKey()), asDeepImmutable(entry.getValue()));
}
return SkylarkDict.copyOf(null, tempBuilder.build());
} else {
throw new MutableObjectException(arg);
}
}
/**
* Checks if a given value is 'Immutable'. This mostly works like {@link
* com.google.devtools.build.lib.syntax.EvalUtils#isImmutable(java.lang.Object)}, but it can also
* handle {@link com.google.common.collect.ImmutableCollection} and {@link
* com.google.common.collect.ImmutableMap}
*/
public static boolean isImmutable(Object o) {
if (o instanceof ImmutableCollection<?>) {
return ((ImmutableCollection<?>) o).stream().allMatch(BuckSkylarkTypes::isImmutable);
} else if (o instanceof ImmutableMap<?, ?>) {
return ((ImmutableMap<?, ?>) o).values().stream().allMatch(BuckSkylarkTypes::isImmutable);
} else {
return EvalUtils.isImmutable(o);
}
}
private void execute(
StarlarkFile file,
Map<String, Module> additionalLoadedModules,
StoredEventHandler localReporter,
WorkspaceFileValue.WorkspaceFileKey workspaceFileKey)
throws InterruptedException {
loadedModules.putAll(additionalLoadedModules);
// set up predeclared environment
HashMap<String, Object> predeclared = new HashMap<>();
predeclared.putAll(getDefaultEnvironment());
predeclared.putAll(bindings); // (may shadow bindings in default environment)
Module module = Module.withPredeclared(starlarkSemantics, predeclared);
// resolve
Resolver.resolveFile(file, module);
// create thread
StarlarkThread thread = new StarlarkThread(mutability, starlarkSemantics);
thread.setLoader(loadedModules::get);
thread.setPrintHandler(Event.makeDebugPrintHandler(localReporter));
thread.setThreadLocal(
PackageFactory.PackageContext.class,
new PackageFactory.PackageContext(builder, null, localReporter));
// The workspace environment doesn't need the tools repository or the fragment map
// because executing workspace rules happens before analysis and it doesn't need a
// repository mapping because calls to the Label constructor in the WORKSPACE file
// are, by definition, not in an external repository and so they don't need the mapping
new BazelStarlarkContext(
BazelStarlarkContext.Phase.WORKSPACE,
/*toolsRepository=*/ null,
/*fragmentNameToClass=*/ null,
/*repoMapping=*/ ImmutableMap.of(),
new SymbolGenerator<>(workspaceFileKey),
/*analysisRuleLabel=*/ null)
.storeInThread(thread);
List<String> globs = new ArrayList<>(); // unused
if (!file.ok()) {
Event.replayEventsOn(localReporter, file.errors());
} else if (PackageFactory.checkBuildSyntax(
file, globs, globs, new HashMap<>(), localReporter)) {
try {
EvalUtils.exec(file, module, thread);
} catch (EvalException ex) {
localReporter.handle(Event.error(ex.getLocation(), ex.getMessage()));
}
}
// Accumulate the global bindings created by this chunk of the WORKSPACE file,
// for use in the next chunk. This set does not include the bindings
// added by getDefaultEnvironment; but it does include bindings created by load,
// so we will need to set the legacy load-binds-globally flag for this file in due course.
this.bindings.putAll(module.getGlobals());
builder.addPosts(localReporter.getPosts());
builder.addEvents(localReporter.getEvents());
if (localReporter.hasErrors()) {
builder.setContainsErrors();
}
localReporter.clear();
}
@Override
public NoneType exportsFiles(
Sequence<?> srcs, Object visibilityO, Object licensesO, StarlarkThread thread)
throws EvalException {
BazelStarlarkContext.from(thread).checkLoadingPhase("native.exports_files");
Package.Builder pkgBuilder = getContext(thread).pkgBuilder;
List<String> files = Type.STRING_LIST.convert(srcs, "'exports_files' operand");
RuleVisibility visibility =
EvalUtils.isNullOrNone(visibilityO)
? ConstantRuleVisibility.PUBLIC
: PackageUtils.getVisibility(
pkgBuilder.getBuildFileLabel(),
BuildType.LABEL_LIST.convert(
visibilityO, "'exports_files' operand", pkgBuilder.getBuildFileLabel()));
// TODO(bazel-team): is licenses plural or singular?
License license = BuildType.LICENSE.convertOptional(licensesO, "'exports_files' operand");
Location loc = thread.getCallerLocation();
for (String file : files) {
String errorMessage = LabelValidator.validateTargetName(file);
if (errorMessage != null) {
throw Starlark.errorf("%s", errorMessage);
}
try {
InputFile inputFile = pkgBuilder.createInputFile(file, loc);
if (inputFile.isVisibilitySpecified() && inputFile.getVisibility() != visibility) {
throw Starlark.errorf(
"visibility for exported file '%s' declared twice", inputFile.getName());
}
if (license != null && inputFile.isLicenseSpecified()) {
throw Starlark.errorf(
"licenses for exported file '%s' declared twice", inputFile.getName());
}
// See if we should check third-party licenses: first checking for any hard-coded policy,
// then falling back to user-settable flags.
boolean checkLicenses;
if (pkgBuilder.getThirdPartyLicenseExistencePolicy()
== ThirdPartyLicenseExistencePolicy.ALWAYS_CHECK) {
checkLicenses = true;
} else if (pkgBuilder.getThirdPartyLicenseExistencePolicy()
== ThirdPartyLicenseExistencePolicy.NEVER_CHECK) {
checkLicenses = false;
} else {
checkLicenses = !thread.getSemantics().incompatibleDisableThirdPartyLicenseChecking();
}
if (checkLicenses
&& license == null
&& !pkgBuilder.getDefaultLicense().isSpecified()
&& RuleClass.isThirdPartyPackage(pkgBuilder.getPackageIdentifier())) {
throw Starlark.errorf(
"third-party file '%s' lacks a license declaration with one of the following types:"
+ " notice, reciprocal, permissive, restricted, unencumbered, by_exception_only",
inputFile.getName());
}
pkgBuilder.setVisibilityAndLicense(inputFile, visibility, license);
} catch (Package.Builder.GeneratedLabelConflict e) {
throw Starlark.errorf("%s", e.getMessage());
}
}
return Starlark.NONE;
}
@Override
@Nullable
public SkyValue compute(SkyKey skyKey, Environment env)
throws InterruptedException, SkyFunctionException {
ResolvedFileKey key = (ResolvedFileKey) skyKey;
StarlarkSemantics starlarkSemantics = PrecomputedValue.STARLARK_SEMANTICS.get(env);
if (starlarkSemantics == null) {
return null;
}
FileValue fileValue = (FileValue) env.getValue(FileValue.key(key.getPath()));
if (fileValue == null) {
return null;
}
try {
if (!fileValue.exists()) {
throw new ResolvedFileFunctionException(
new NoSuchThingException("Specified resolved file '" + key.getPath() + "' not found."));
} else {
// read
byte[] bytes =
FileSystemUtils.readWithKnownFileSize(
key.getPath().asPath(), key.getPath().asPath().getFileSize());
// parse
StarlarkFile file =
StarlarkFile.parse(ParserInput.create(bytes, key.getPath().asPath().toString()));
if (!file.ok()) {
Event.replayEventsOn(env.getListener(), file.errors());
throw resolvedValueError("Failed to parse resolved file " + key.getPath());
}
Module module = Module.create();
// resolve
Resolver.resolveFile(file, module);
if (!file.ok()) {
Event.replayEventsOn(env.getListener(), file.errors());
throw resolvedValueError("Failed to validate resolved file " + key.getPath());
}
// execute
try (Mutability mu = Mutability.create("resolved file", key.getPath())) {
StarlarkThread thread = new StarlarkThread(mu, starlarkSemantics);
EvalUtils.exec(file, module, thread);
} catch (EvalException ex) {
env.getListener().handle(Event.error(ex.getLocation(), ex.getMessage()));
throw resolvedValueError("Failed to evaluate resolved file " + key.getPath());
}
Object resolved = module.getGlobal("resolved");
if (resolved == null) {
throw resolvedValueError(
"Symbol 'resolved' not exported in resolved file " + key.getPath());
}
if (!(resolved instanceof List)) {
throw resolvedValueError(
"Symbol 'resolved' in resolved file " + key.getPath() + " not a list");
}
ImmutableList.Builder<Map<String, Object>> result
= new ImmutableList.Builder<Map<String, Object>>();
for (Object entry : (List) resolved) {
if (!(entry instanceof Map)) {
throw resolvedValueError(
"Symbol 'resolved' in resolved file "
+ key.getPath()
+ " contains a non-map entry");
}
ImmutableMap.Builder<String, Object> entryBuilder
= new ImmutableMap.Builder<String, Object>();
for (Map.Entry<?, ?> keyValue : ((Map<?, ?>) entry).entrySet()) {
Object attribute = keyValue.getKey();
if (!(attribute instanceof String)) {
throw resolvedValueError(
"Symbol 'resolved' in resolved file "
+ key.getPath()
+ " contains a non-string key in one of its entries");
}
entryBuilder.put((String) attribute, keyValue.getValue());
}
result.add(entryBuilder.build());
}
return new ResolvedFileValue(result.build());
}
} catch (IOException e) {
throw new ResolvedFileFunctionException(e);
}
}
@Override
public Tuple<Object> resolveCommand(
String command,
Object attributeUnchecked,
Boolean expandLocations,
Object makeVariablesUnchecked,
Sequence<?> tools,
Dict<?, ?> labelDictUnchecked,
Dict<?, ?> executionRequirementsUnchecked,
StarlarkThread thread)
throws ConversionException, EvalException {
checkMutable("resolve_command");
Label ruleLabel = getLabel();
Map<Label, Iterable<Artifact>> labelDict = checkLabelDict(labelDictUnchecked);
// The best way to fix this probably is to convert CommandHelper to Starlark.
CommandHelper helper =
CommandHelper.builder(getRuleContext())
.addToolDependencies(Sequence.cast(tools, TransitiveInfoCollection.class, "tools"))
.addLabelMap(labelDict)
.build();
String attribute = Type.STRING.convertOptional(attributeUnchecked, "attribute", ruleLabel);
if (expandLocations) {
command =
helper.resolveCommandAndExpandLabels(command, attribute, /*allowDataInLabel=*/ false);
}
if (!EvalUtils.isNullOrNone(makeVariablesUnchecked)) {
Map<String, String> makeVariables =
Type.STRING_DICT.convert(makeVariablesUnchecked, "make_variables", ruleLabel);
command = expandMakeVariables(attribute, command, makeVariables);
}
List<Artifact> inputs = new ArrayList<>();
// TODO(lberki): This flattens a NestedSet.
// However, we can't turn this into a Depset because it's an incompatible change to
// Starlark.
inputs.addAll(helper.getResolvedTools().toList());
ImmutableMap<String, String> executionRequirements =
ImmutableMap.copyOf(
Dict.noneableCast(
executionRequirementsUnchecked,
String.class,
String.class,
"execution_requirements"));
PathFragment shExecutable = ShToolchain.getPathOrError(ruleContext);
BashCommandConstructor constructor =
CommandHelper.buildBashCommandConstructor(
executionRequirements,
shExecutable,
// Hash the command-line to prevent multiple actions from the same rule invocation
// conflicting with each other.
"." + Hashing.murmur3_32().hashUnencodedChars(command).toString() + SCRIPT_SUFFIX);
List<String> argv = helper.buildCommandLine(command, inputs, constructor);
return Tuple.<Object>of(
StarlarkList.copyOf(thread.mutability(), inputs),
StarlarkList.copyOf(thread.mutability(), argv),
helper.getToolsRunfilesSuppliers());
}
@Test
public void testFilesetTypeDefinition() throws Exception {
assertThat(Starlark.type(makeFilesetEntry())).isEqualTo("FilesetEntry");
assertThat(EvalUtils.isImmutable(makeFilesetEntry())).isTrue();
}
/** Joins the lines, parses them as an expression, and evaluates it. */
public final Object eval(String... lines) throws Exception {
ParserInput input = ParserInput.fromLines(lines);
return EvalUtils.eval(input, FileOptions.DEFAULT, getModule(), getStarlarkThread());
}
/** Joins the lines, parses them as a file, and executes it. */
public final void exec(String... lines)
throws SyntaxError.Exception, EvalException, InterruptedException {
ParserInput input = ParserInput.fromLines(lines);
EvalUtils.exec(input, FileOptions.DEFAULT, getModule(), getStarlarkThread());
}
@Test
public void testStructMutabilityShallow() throws Exception {
assertThat(EvalUtils.isImmutable(makeStruct("a", 1))).isTrue();
}
private void verifyKeyIsProvider(Object key, Location loc, String s) throws EvalException {
if (!(key instanceof Provider)) {
throw new EvalException(loc, String.format(s, EvalUtils.getDataTypeName(key)));
}
}
private void verifyKeyIsProvider(Object key, Location loc, String s) throws EvalException {
if (!(key instanceof Provider)) {
throw new EvalException(loc, String.format(s, EvalUtils.getDataTypeName(key)));
}
}
@Override
public void run(
SkylarkList<Object> arguments, Object shortName, Object userEnv, Location location)
throws EvalException {
Map<String, String> userEnvValidated =
SkylarkDict.castSkylarkDictOrNoneToDict(userEnv, String.class, String.class, null);
CommandLineArgs argumentsValidated;
Object firstArgument;
try {
argumentsValidated =
CommandLineArgsFactory.from(
arguments.stream()
.map(SkylarkRuleContextActions::getImmutableArg)
.collect(ImmutableList.toImmutableList()));
firstArgument =
argumentsValidated
.getArgsAndFormatStrings()
.findFirst()
.orElseThrow(
() ->
new EvalException(
location, "At least one argument must be provided to 'run()'"))
.getObject();
} catch (CommandLineException e) {
throw new EvalException(
location,
String.format(
"Invalid type for %s. Must be one of string, int, Artifact, Label, or the result of ctx.actions.args()",
e.getHumanReadableErrorMessage()));
}
String shortNameValidated;
if (EvalUtils.isNullOrNone(shortName)) {
if (firstArgument instanceof Artifact) {
shortNameValidated =
String.format("run action %s", ((Artifact) firstArgument).getBasename());
} else if (firstArgument instanceof OutputArtifact) {
shortNameValidated =
String.format(
"run action %s", ((OutputArtifact) firstArgument).getArtifact().getBasename());
} else {
shortNameValidated = String.format("run action %s", firstArgument);
}
} else {
shortNameValidated = (String) shortName;
}
new RunAction(
registry, shortNameValidated, argumentsValidated, ImmutableMap.copyOf(userEnvValidated));
}
@Test
public void isImmutableWorks() throws InterruptedException, EvalException, LabelSyntaxException {
String buildFile =
"ui1 = UserInfo(value=\"foo\", immutable=immutable_list, mutable=mutable_list)\n"
+ "ui1.mutable.append(7)\n"
+ "ui2 = UserInfo(value=ui1, immutable=immutable_list, mutable=mutable_list)\n"
+ "ui2.mutable.append(8)\n"
+ "ui2";
SkylarkRuleFunctions functions = new SkylarkRuleFunctions(LabelCache.newLabelCache());
UserDefinedProvider userInfo =
functions.provider(
"",
SkylarkList.createImmutable(ImmutableList.of("value", "immutable", "mutable")),
Location.BUILTIN);
userInfo.export(Label.parseAbsolute("//:foo.bzl", ImmutableMap.of()), "UserInfo");
SkylarkList<Integer> immutableList = SkylarkList.createImmutable(ImmutableList.of(1, 2, 3));
UserDefinedProviderInfo out1;
UserDefinedProviderInfo out2;
try (TestMutableEnv env =
new TestMutableEnv(
ImmutableMap.of("immutable_list", immutableList, "UserInfo", userInfo))) {
SkylarkList.MutableList<Integer> mutableList =
SkylarkList.MutableList.of(env.getEnv(), 4, 5, 6);
env.getEnv().updateAndExport("mutable_list", mutableList);
assertFalse(mutableList.isImmutable());
out2 = (UserDefinedProviderInfo) BuildFileAST.eval(env.getEnv(), buildFile);
assertNotNull(out2);
out1 = (UserDefinedProviderInfo) out2.getValue("value");
assertNotNull(out1);
assertFalse(out1.isImmutable());
assertTrue(EvalUtils.isImmutable(Objects.requireNonNull(out1.getValue("value"))));
assertTrue(EvalUtils.isImmutable(Objects.requireNonNull(out1.getValue("immutable"))));
assertFalse(EvalUtils.isImmutable(Objects.requireNonNull(out1.getValue("mutable"))));
assertFalse(out2.isImmutable());
assertFalse(EvalUtils.isImmutable(Objects.requireNonNull(out2.getValue("value"))));
assertTrue(EvalUtils.isImmutable(Objects.requireNonNull(out2.getValue("immutable"))));
assertFalse(EvalUtils.isImmutable(Objects.requireNonNull(out2.getValue("mutable"))));
assertEquals(
ImmutableList.of(1, 2, 3),
((SkylarkList<?>) out1.getValue("immutable")).getImmutableList());
assertEquals(
ImmutableList.of(4, 5, 6, 7, 8),
((SkylarkList<?>) out1.getValue("mutable")).getImmutableList());
assertEquals(
ImmutableList.of(1, 2, 3),
((SkylarkList<?>) out2.getValue("immutable")).getImmutableList());
assertEquals(
ImmutableList.of(4, 5, 6, 7, 8),
((SkylarkList<?>) out2.getValue("mutable")).getImmutableList());
}
assertTrue(out1.isImmutable());
assertTrue(EvalUtils.isImmutable(Objects.requireNonNull(out1.getValue("value"))));
assertTrue(EvalUtils.isImmutable(Objects.requireNonNull(out1.getValue("immutable"))));
assertTrue(EvalUtils.isImmutable(Objects.requireNonNull(out1.getValue("mutable"))));
assertTrue(out2.isImmutable());
assertTrue(EvalUtils.isImmutable(Objects.requireNonNull(out2.getValue("value"))));
assertTrue(EvalUtils.isImmutable(Objects.requireNonNull(out2.getValue("immutable"))));
assertTrue(EvalUtils.isImmutable(Objects.requireNonNull(out2.getValue("mutable"))));
}