1/*
2 * Copyright 2013, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32package org.jf.dexlib2.analysis;
33
34import com.google.common.base.Supplier;
35import com.google.common.base.Suppliers;
36import com.google.common.cache.CacheBuilder;
37import com.google.common.cache.CacheLoader;
38import com.google.common.cache.LoadingCache;
39import com.google.common.collect.ImmutableSet;
40import com.google.common.collect.Lists;
41import org.jf.dexlib2.Opcodes;
42import org.jf.dexlib2.analysis.reflection.ReflectionClassDef;
43import org.jf.dexlib2.iface.ClassDef;
44import org.jf.dexlib2.immutable.ImmutableDexFile;
45
46import javax.annotation.Nonnull;
47import java.io.IOException;
48import java.io.Serializable;
49import java.util.Arrays;
50import java.util.List;
51
52public class ClassPath {
53    @Nonnull private final TypeProto unknownClass;
54    @Nonnull private List<ClassProvider> classProviders;
55    private final boolean checkPackagePrivateAccess;
56    public final int oatVersion;
57
58    public static final int NOT_ART = -1;
59
60    /**
61     * Creates a new ClassPath instance that can load classes from the given providers
62     *
63     * @param classProviders A varargs array of ClassProviders. When loading a class, these providers will be searched
64     *                       in order
65     */
66    public ClassPath(ClassProvider... classProviders) throws IOException {
67        this(Arrays.asList(classProviders), false, NOT_ART);
68    }
69
70    /**
71     * Creates a new ClassPath instance that can load classes from the given providers
72     *
73     * @param classProviders An iterable of ClassProviders. When loading a class, these providers will be searched in
74     *                       order
75     */
76    public ClassPath(Iterable<ClassProvider> classProviders) throws IOException {
77        this(classProviders, false, NOT_ART);
78    }
79
80    /**
81     * Creates a new ClassPath instance that can load classes from the given providers
82     *
83     * @param classProviders An iterable of ClassProviders. When loading a class, these providers will be searched in
84     *                       order
85     * @param checkPackagePrivateAccess Whether checkPackagePrivateAccess is needed, enabled for ONLY early API 17 by
86     *                                  default
87     * @param oatVersion The applicable oat version, or NOT_ART
88     */
89    public ClassPath(@Nonnull Iterable<? extends ClassProvider> classProviders, boolean checkPackagePrivateAccess,
90                     int oatVersion) {
91        // add fallbacks for certain special classes that must be present
92        unknownClass = new UnknownClassProto(this);
93        loadedClasses.put(unknownClass.getType(), unknownClass);
94        this.checkPackagePrivateAccess = checkPackagePrivateAccess;
95        this.oatVersion = oatVersion;
96
97        loadPrimitiveType("Z");
98        loadPrimitiveType("B");
99        loadPrimitiveType("S");
100        loadPrimitiveType("C");
101        loadPrimitiveType("I");
102        loadPrimitiveType("J");
103        loadPrimitiveType("F");
104        loadPrimitiveType("D");
105        loadPrimitiveType("L");
106
107        this.classProviders = Lists.newArrayList(classProviders);
108        this.classProviders.add(getBasicClasses());
109    }
110
111    private void loadPrimitiveType(String type) {
112        loadedClasses.put(type, new PrimitiveProto(this, type));
113    }
114
115    private static ClassProvider getBasicClasses() {
116        // fallbacks for some special classes that we assume are present
117        return new DexClassProvider(new ImmutableDexFile(Opcodes.getDefault(), ImmutableSet.of(
118                new ReflectionClassDef(Class.class),
119                new ReflectionClassDef(Cloneable.class),
120                new ReflectionClassDef(Object.class),
121                new ReflectionClassDef(Serializable.class),
122                new ReflectionClassDef(String.class),
123                new ReflectionClassDef(Throwable.class))));
124    }
125
126    public boolean isArt() {
127        return oatVersion != NOT_ART;
128    }
129
130    @Nonnull
131    public TypeProto getClass(@Nonnull CharSequence type) {
132        return loadedClasses.getUnchecked(type.toString());
133    }
134
135    private final CacheLoader<String, TypeProto> classLoader = new CacheLoader<String, TypeProto>() {
136        @Override public TypeProto load(String type) throws Exception {
137            if (type.charAt(0) == '[') {
138                return new ArrayProto(ClassPath.this, type);
139            } else {
140                return new ClassProto(ClassPath.this, type);
141            }
142        }
143    };
144
145    @Nonnull private LoadingCache<String, TypeProto> loadedClasses = CacheBuilder.newBuilder().build(classLoader);
146
147    @Nonnull
148    public ClassDef getClassDef(String type) {
149        for (ClassProvider provider: classProviders) {
150            ClassDef classDef = provider.getClassDef(type);
151            if (classDef != null) {
152                return classDef;
153            }
154        }
155        throw new UnresolvedClassException("Could not resolve class %s", type);
156    }
157
158    @Nonnull
159    public TypeProto getUnknownClass() {
160        return unknownClass;
161    }
162
163    public boolean shouldCheckPackagePrivateAccess() {
164        return checkPackagePrivateAccess;
165    }
166
167    private final Supplier<OdexedFieldInstructionMapper> fieldInstructionMapperSupplier = Suppliers.memoize(
168            new Supplier<OdexedFieldInstructionMapper>() {
169                @Override public OdexedFieldInstructionMapper get() {
170                    return new OdexedFieldInstructionMapper(isArt());
171                }
172            });
173
174    @Nonnull
175    public OdexedFieldInstructionMapper getFieldInstructionMapper() {
176        return fieldInstructionMapperSupplier.get();
177    }
178}
179