• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The Bazel Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //    http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 package com.google.devtools.build.android.desugar;
15 
16 import static com.google.common.base.Preconditions.checkArgument;
17 import static com.google.common.collect.Iterables.getOnlyElement;
18 
19 import com.google.common.collect.ImmutableList;
20 import com.google.common.collect.ImmutableMap;
21 import java.io.IOException;
22 import java.lang.invoke.MethodHandle;
23 import java.nio.file.Files;
24 import java.nio.file.Path;
25 import java.util.ArrayList;
26 import java.util.HashSet;
27 import java.util.LinkedHashMap;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.stream.Stream;
31 
32 class LambdaClassMaker {
33 
34   static final String LAMBDA_METAFACTORY_DUMPER_PROPERTY = "jdk.internal.lambda.dumpProxyClasses";
35 
36   private final Path rootDirectory;
37   private final Map<Path, LambdaInfo> generatedClasses = new LinkedHashMap<>();
38   private final Set<Path> existingPaths = new HashSet<>();
39 
LambdaClassMaker(Path rootDirectory)40   public LambdaClassMaker(Path rootDirectory) {
41     checkArgument(
42         Files.isDirectory(rootDirectory), "The argument '%s' is not a directory.", rootDirectory);
43     this.rootDirectory = rootDirectory;
44   }
45 
generateLambdaClass(String invokerInternalName, LambdaInfo lambdaInfo, MethodHandle bootstrapMethod, ArrayList<Object> bsmArgs)46   public void generateLambdaClass(String invokerInternalName, LambdaInfo lambdaInfo,
47       MethodHandle bootstrapMethod, ArrayList<Object> bsmArgs) throws IOException {
48     // Invoking the bootstrap method will dump the generated class.  Ignore any pre-existing
49     // matching files, which can come from desugar's implementation using classes being desugared.
50     existingPaths.addAll(findUnprocessed(invokerInternalName + "$$Lambda$"));
51     try {
52       bootstrapMethod.invokeWithArguments(bsmArgs);
53     } catch (Throwable e) {
54       throw new IllegalStateException("Failed to generate lambda class for class "
55           + invokerInternalName + " using " + bootstrapMethod + " with arguments " + bsmArgs, e);
56     }
57 
58     Path generatedClassFile = getOnlyElement(findUnprocessed(invokerInternalName + "$$Lambda$"));
59     generatedClasses.put(generatedClassFile, lambdaInfo);
60     existingPaths.add(generatedClassFile);
61   }
62 
63   /**
64    * Returns absolute paths to .class files generated since the last call to this method together
65    * with a string descriptor of the factory method.
66    */
drain()67   public ImmutableMap<Path, LambdaInfo> drain() {
68     ImmutableMap<Path, LambdaInfo> result = ImmutableMap.copyOf(generatedClasses);
69     generatedClasses.clear();
70     return result;
71   }
72 
findUnprocessed(String pathPrefix)73   private ImmutableList<Path> findUnprocessed(String pathPrefix) throws IOException {
74     // pathPrefix is an internal class name prefix containing '/', but paths obtained on Windows
75     // will not contain '/' and searches will fail.  So, construct an absolute path from the given
76     // string and use its string representation to find the file we need regardless of host
77     // system's file system
78     Path rootPathPrefix = rootDirectory.resolve(pathPrefix);
79     final String rootPathPrefixStr = rootPathPrefix.toString();
80 
81     if (!Files.exists(rootPathPrefix.getParent())) {
82       return ImmutableList.of();
83     }
84     try (Stream<Path> paths = Files.list(rootPathPrefix.getParent())) {
85       return paths
86           .filter(
87               path ->
88                   path.toString().startsWith(rootPathPrefixStr) && !existingPaths.contains(path))
89           .collect(ImmutableList.toImmutableList());
90     }
91   }
92 }
93