| // Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file | 
 | // for details. All rights reserved. Use of this source code is governed by a | 
 | // BSD-style license that can be found in the LICENSE file. | 
 | package com.android.tools.r8; | 
 |  | 
 | import com.android.tools.r8.ProgramResource.Kind; | 
 | import com.android.tools.r8.ToolHelper.ProcessResult; | 
 | import com.android.tools.r8.debug.CfDebugTestConfig; | 
 | import com.android.tools.r8.debug.DebugTestConfig; | 
 | import com.android.tools.r8.errors.Unimplemented; | 
 | import com.android.tools.r8.origin.Origin; | 
 | import com.android.tools.r8.origin.PathOrigin; | 
 | import com.android.tools.r8.utils.AndroidApp; | 
 | import com.android.tools.r8.utils.DescriptorUtils; | 
 | import com.android.tools.r8.utils.FileUtils; | 
 | import com.android.tools.r8.utils.ListUtils; | 
 | import java.io.IOException; | 
 | import java.io.InputStream; | 
 | import java.nio.file.Files; | 
 | import java.nio.file.Path; | 
 | import java.util.ArrayList; | 
 | import java.util.Arrays; | 
 | import java.util.Collection; | 
 | import java.util.Collections; | 
 | import java.util.List; | 
 | import java.util.Set; | 
 |  | 
 | public class JvmTestBuilder extends TestBuilder<JvmTestRunResult, JvmTestBuilder> { | 
 |  | 
 |   private static class ClassFileResource implements ProgramResource { | 
 |  | 
 |     private final Path file; | 
 |     private final String descriptor; | 
 |     private final Origin origin; | 
 |  | 
 |     ClassFileResource(Class<?> clazz) { | 
 |       this( | 
 |           ToolHelper.getClassFileForTestClass(clazz), | 
 |           DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName())); | 
 |     } | 
 |  | 
 |     ClassFileResource(Path file, String descriptor) { | 
 |       this.file = file; | 
 |       this.descriptor = descriptor; | 
 |       origin = new PathOrigin(file); | 
 |     } | 
 |  | 
 |     @Override | 
 |     public Kind getKind() { | 
 |       return Kind.CF; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public InputStream getByteStream() throws ResourceException { | 
 |       try { | 
 |         return Files.newInputStream(file); | 
 |       } catch (IOException e) { | 
 |         throw new ResourceException(getOrigin(), e); | 
 |       } | 
 |     } | 
 |  | 
 |     @Override | 
 |     public Set<String> getClassDescriptors() { | 
 |       return Collections.singleton(descriptor); | 
 |     } | 
 |  | 
 |     @Override | 
 |     public Origin getOrigin() { | 
 |       return origin; | 
 |     } | 
 |   } | 
 |  | 
 |   private static class ClassFileResourceProvider implements ProgramResourceProvider { | 
 |  | 
 |     private final List<ProgramResource> resources; | 
 |  | 
 |     public ClassFileResourceProvider(List<ProgramResource> resources) { | 
 |       this.resources = resources; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public Collection<ProgramResource> getProgramResources() throws ResourceException { | 
 |       return resources; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public DataResourceProvider getDataResourceProvider() { | 
 |       return null; | 
 |     } | 
 |   } | 
 |  | 
 |   // Ordered list of classpath entries. | 
 |   private List<Path> classpath = new ArrayList<>(); | 
 |  | 
 |   private AndroidApp.Builder builder = AndroidApp.builder(); | 
 |  | 
 |   private JvmTestBuilder(TestState state) { | 
 |     super(state); | 
 |   } | 
 |  | 
 |   public static JvmTestBuilder create(TestState state) { | 
 |     return new JvmTestBuilder(state); | 
 |   } | 
 |  | 
 |   @Override | 
 |   JvmTestBuilder self() { | 
 |     return this; | 
 |   } | 
 |  | 
 |   @Override | 
 |   public JvmTestRunResult run(String mainClass) throws IOException { | 
 |     ProcessResult result = ToolHelper.runJava(classpath, mainClass); | 
 |     return new JvmTestRunResult(builder.build(), result); | 
 |   } | 
 |  | 
 |   public JvmTestRunResult run(TestRuntime runtime, String mainClass) throws IOException { | 
 |     assert runtime.isCf(); | 
 |     ProcessResult result = ToolHelper.runJava(runtime.asCf().getVm(), classpath, mainClass); | 
 |     return new JvmTestRunResult(builder.build(), result); | 
 |   } | 
 |  | 
 |   @Override | 
 |   public DebugTestConfig debugConfig() { | 
 |     return new CfDebugTestConfig().addPaths(classpath); | 
 |   } | 
 |  | 
 |   @Override | 
 |   public JvmTestBuilder addLibraryFiles(Collection<Path> files) { | 
 |     throw new Unimplemented("No support for changing the Java runtime library."); | 
 |   } | 
 |  | 
 |   @Override | 
 |   public JvmTestBuilder addLibraryClasses(Collection<Class<?>> classes) { | 
 |     throw new Unimplemented("No support for changing the Java runtime library."); | 
 |   } | 
 |  | 
 |   @Override | 
 |   public JvmTestBuilder addProgramClasses(Collection<Class<?>> classes) { | 
 |     addProgramResources(ListUtils.map(classes, ClassFileResource::new)); | 
 |     return self(); | 
 |   } | 
 |  | 
 |   @Override | 
 |   public JvmTestBuilder addProgramFiles(Collection<Path> files) { | 
 |     for (Path file : files) { | 
 |       if (FileUtils.isArchive(file)) { | 
 |         classpath.add(file); | 
 |       } else { | 
 |         throw new Unimplemented( | 
 |             "No support for adding paths directly (we need to compute the descriptor)"); | 
 |       } | 
 |     } | 
 |     return self(); | 
 |   } | 
 |  | 
 |   @Override | 
 |   public JvmTestBuilder addProgramClassFileData(Collection<byte[]> files) { | 
 |     addProgramResources( | 
 |         ListUtils.map(files, data -> | 
 |             ProgramResource.fromBytes( | 
 |                 Origin.unknown(), | 
 |                 Kind.CF, | 
 |                 data, | 
 |                 Collections.singleton(TestBase.extractClassDescriptor(data))))); | 
 |     return self(); | 
 |   } | 
 |  | 
 |   @Override | 
 |   public JvmTestBuilder addProgramDexFileData(Collection<byte[]> data) { | 
 |     throw new Unimplemented("No support for adding dex file data directly"); | 
 |   } | 
 |  | 
 |   public JvmTestBuilder addClasspath(Path... paths) { | 
 |     return addClasspath(Arrays.asList(paths)); | 
 |   } | 
 |  | 
 |   public JvmTestBuilder addClasspath(List<Path> paths) { | 
 |     for (Path path : paths) { | 
 |       assert Files.isDirectory(path) || FileUtils.isArchive(path); | 
 |       classpath.add(path); | 
 |     } | 
 |     return self(); | 
 |   } | 
 |  | 
 |   public JvmTestBuilder addTestClasspath() { | 
 |     return addClasspath(ToolHelper.getClassPathForTests()); | 
 |   } | 
 |  | 
 |   // Adding a collection of resources will build a jar of exactly those classes so that no other | 
 |   // classes are made available via a too broad classpath directory. | 
 |   private void addProgramResources(List<ProgramResource> resources) { | 
 |     AndroidApp build = | 
 |         AndroidApp.builder() | 
 |             .addProgramResourceProvider(new ClassFileResourceProvider(resources)) | 
 |             .build(); | 
 |     Path out; | 
 |     try { | 
 |       out = getState().getNewTempFolder().resolve("out.zip"); | 
 |       build.writeToZip(out, OutputMode.ClassFile); | 
 |     } catch (IOException e) { | 
 |       throw new RuntimeException(e); | 
 |     } | 
 |     classpath.add(out); | 
 |     builder.addProgramFiles(out); | 
 |   } | 
 | } |