blob: e5792cdc5135245b218e3c5501964775d02963c2 [file] [log] [blame]
// Copyright (c) 2017, 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.graph;
import static com.android.tools.r8.utils.FileUtils.DEFAULT_DEX_FILENAME;
import com.android.tools.r8.Resource;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.google.common.io.Closer;
import java.io.IOException;
// Lazily loads a class file represented by resource.
public final class LazyClassFileLoader implements DexClassPromise {
// Resource representing file definition.
private final Resource resource;
// Class kind to be created.
private final ClassKind classKind;
// Application reader to be used. Note that the reader may be reused in
// many loaders and may be used concurrently, it is considered to be
// thread-safe since its only state is internal options which we
// consider immutable after they are initialized (barring dex factory
// which is thread-safe).
private final JarApplicationReader reader;
// Dex type of the class to be created.
private final DexType type;
// Cached loaded class if get(...) method has already been called, this
// field is only accessed in context synchronized on `this`.
private DexClass loadedClass = null;
public LazyClassFileLoader(DexType type,
Resource resource, ClassKind classKind, JarApplicationReader reader) {
this.resource = resource;
this.reader = reader;
this.type = type;
this.classKind = classKind;
assert classKind != ClassKind.PROGRAM;
}
// Callback method for JarClassFileReader, is always called in synchronized context.
private void addClass(DexClass clazz) {
assert clazz != null;
assert loadedClass == null;
loadedClass = clazz;
}
@Override
public DexType getType() {
return type;
}
@Override
public Resource.Kind getOrigin() {
return Resource.Kind.CLASSFILE;
}
@Override
public boolean isProgramClass() {
return false;
}
@Override
public boolean isClasspathClass() {
return classKind == ClassKind.CLASSPATH;
}
@Override
public boolean isLibraryClass() {
return classKind == ClassKind.LIBRARY;
}
// Loads the class from the resource. Synchronized on `this` to avoid
// unnecessary complications, thus all threads trying to load a class with
// this loader will wait for the first load to finish.
@Override
public synchronized DexClass get() {
if (loadedClass != null) {
return loadedClass;
}
try (Closer closer = Closer.create()) {
JarClassFileReader reader = new JarClassFileReader(this.reader, this::addClass);
reader.read(DEFAULT_DEX_FILENAME, classKind, resource.getStream(closer));
} catch (IOException e) {
throw new CompilationError("Failed to load class: " + type.toSourceString(), e);
}
if (loadedClass == null) {
throw new Unreachable("Class is supposed to be loaded: " + type.toSourceString());
}
if (loadedClass.type != type) {
throw new CompilationError("Class content provided for type descriptor "
+ type.toSourceString() + " actually defines class " + loadedClass.type.toSourceString());
}
return loadedClass;
}
}