blob: 1e49886b9525812c4df42e0dc7b56e6f76a24449 [file] [log] [blame]
Igor Murashkin457e8742015-10-22 17:37:50 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include "lambda/box_class_table.h"
17
18#include "base/mutex.h"
19#include "common_throws.h"
20#include "gc_root-inl.h"
21#include "lambda/closure.h"
22#include "lambda/leaking_allocator.h"
23#include "mirror/method.h"
24#include "mirror/object-inl.h"
25#include "thread.h"
26
27#include <string>
28#include <vector>
29
30namespace art {
31namespace lambda {
32
33// Create the lambda proxy class given the name of the lambda interface (e.g. Ljava/lang/Runnable;)
34// Also needs a proper class loader (or null for bootclasspath) where the proxy will be created
35// into.
36//
37// The class must **not** have already been created.
38// Returns a non-null ptr on success, otherwise returns null and has an exception set.
39static mirror::Class* CreateClass(Thread* self,
40 const std::string& class_name,
41 const Handle<mirror::ClassLoader>& class_loader)
42 SHARED_REQUIRES(Locks::mutator_lock_) {
43 ScopedObjectAccessUnchecked soa(self);
44 StackHandleScope<2> hs(self);
45
46 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
47
48 // Find the java.lang.Class for our class name (from the class loader).
49 Handle<mirror::Class> lambda_interface =
50 hs.NewHandle(class_linker->FindClass(self, class_name.c_str(), class_loader));
51 // TODO: use LookupClass in a loop
52 // TODO: DCHECK That this doesn't actually cause the class to be loaded,
53 // since the create-lambda should've loaded it already
54 DCHECK(lambda_interface.Get() != nullptr) << "CreateClass with class_name=" << class_name;
55 DCHECK(lambda_interface->IsInterface()) << "CreateClass with class_name=" << class_name;
56 jobject lambda_interface_class = soa.AddLocalReference<jobject>(lambda_interface.Get());
57
58 // Look up java.lang.reflect.Proxy#getLambdaProxyClass method.
59 Handle<mirror::Class> java_lang_reflect_proxy =
60 hs.NewHandle(class_linker->FindSystemClass(soa.Self(), "Ljava/lang/reflect/Proxy;"));
61 jclass java_lang_reflect_proxy_class =
62 soa.AddLocalReference<jclass>(java_lang_reflect_proxy.Get());
63 DCHECK(java_lang_reflect_proxy.Get() != nullptr);
64
65 jmethodID proxy_factory_method_id =
66 soa.Env()->GetStaticMethodID(java_lang_reflect_proxy_class,
67 "getLambdaProxyClass",
68 "(Ljava/lang/ClassLoader;Ljava/lang/Class;)Ljava/lang/Class;");
69 DCHECK(!soa.Env()->ExceptionCheck());
70
71 // Call into the java code to do the hard work of figuring out which methods and throws
72 // our lambda interface proxy needs to implement. It then calls back into the class linker
73 // on our behalf to make the proxy itself.
74 jobject generated_lambda_proxy_class =
75 soa.Env()->CallStaticObjectMethod(java_lang_reflect_proxy_class,
76 proxy_factory_method_id,
77 class_loader.ToJObject(),
78 lambda_interface_class);
79
80 // This can throw in which case we return null. Caller must handle.
81 return soa.Decode<mirror::Class*>(generated_lambda_proxy_class);
82}
83
84BoxClassTable::BoxClassTable() {
85}
86
87BoxClassTable::~BoxClassTable() {
88 // Don't need to do anything, classes are deleted automatically by GC
89 // when the classloader is deleted.
90 //
91 // Our table will not outlive the classloader since the classloader owns it.
92}
93
94mirror::Class* BoxClassTable::GetOrCreateBoxClass(const char* class_name,
95 const Handle<mirror::ClassLoader>& class_loader) {
96 DCHECK(class_name != nullptr);
97
98 Thread* self = Thread::Current();
99
100 std::string class_name_str = class_name;
101
102 {
103 MutexLock mu(self, *Locks::lambda_class_table_lock_);
104
105 // Attempt to look up this class, it's possible it was already created previously.
106 // If this is the case we *must* return the same class as before to maintain
107 // referential equality between box instances.
108 //
109 // In managed code:
110 // Functional f = () -> 5; // vF = create-lambda
111 // Object a = f; // vA = box-lambda vA
112 // Object b = f; // vB = box-lambda vB
113 // assert(a.getClass() == b.getClass())
114 // assert(a == b)
115 ValueType value = FindBoxedClass(class_name_str);
116 if (!value.IsNull()) {
117 return value.Read();
118 }
119 }
120
121 // Otherwise we need to generate a class ourselves and insert it into the hash map
122
123 // Release the table lock here, which implicitly allows other threads to suspend
124 // (since the GC callbacks will not block on trying to acquire our lock).
125 // We also don't want to call into the class linker with the lock held because
126 // our lock level is lower.
127 self->AllowThreadSuspension();
128
129 // Create a lambda proxy class, within the specified class loader.
130 mirror::Class* lambda_proxy_class = CreateClass(self, class_name_str, class_loader);
131
132 // There are no thread suspension points after this, so we don't need to put it into a handle.
133 ScopedAssertNoThreadSuspension soants{self, "BoxClassTable::GetOrCreateBoxClass"}; // NOLINT: [readability/braces] [4]
134
135 if (UNLIKELY(lambda_proxy_class == nullptr)) {
136 // Most likely an OOM has occurred.
137 CHECK(self->IsExceptionPending());
138 return nullptr;
139 }
140
141 {
142 MutexLock mu(self, *Locks::lambda_class_table_lock_);
143
144 // Possible, but unlikely, that someone already came in and made a proxy class
145 // on another thread.
146 ValueType value = FindBoxedClass(class_name_str);
147 if (UNLIKELY(!value.IsNull())) {
148 DCHECK_EQ(lambda_proxy_class, value.Read());
149 return value.Read();
150 }
151
152 // Otherwise we made a brand new proxy class.
153 // The class itself is cleaned up by the GC (e.g. class unloading) later.
154
155 // Actually insert into the table.
156 map_.Insert({std::move(class_name_str), ValueType(lambda_proxy_class)});
157 }
158
159 return lambda_proxy_class;
160}
161
162BoxClassTable::ValueType BoxClassTable::FindBoxedClass(const std::string& class_name) const {
163 auto map_iterator = map_.Find(class_name);
164 if (map_iterator != map_.end()) {
165 const std::pair<UnorderedMapKeyType, ValueType>& key_value_pair = *map_iterator;
166 const ValueType& value = key_value_pair.second;
167
168 DCHECK(!value.IsNull()); // Never store null boxes.
169 return value;
170 }
171
172 return ValueType(nullptr);
173}
174
175void BoxClassTable::EmptyFn::MakeEmpty(std::pair<UnorderedMapKeyType, ValueType>& item) const {
176 item.first.clear();
177
178 Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
179 item.second = ValueType(); // Also clear the GC root.
180}
181
182bool BoxClassTable::EmptyFn::IsEmpty(const std::pair<UnorderedMapKeyType, ValueType>& item) const {
183 bool is_empty = item.first.empty();
184 DCHECK_EQ(item.second.IsNull(), is_empty);
185
186 return is_empty;
187}
188
189bool BoxClassTable::EqualsFn::operator()(const UnorderedMapKeyType& lhs,
190 const UnorderedMapKeyType& rhs) const {
191 // Be damn sure the classes don't just move around from under us.
192 Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
193
194 // Being the same class name isn't enough, must also have the same class loader.
195 // When we are in the same class loader, classes are equal via the pointer.
196 return lhs == rhs;
197}
198
199size_t BoxClassTable::HashFn::operator()(const UnorderedMapKeyType& key) const {
200 return std::hash<std::string>()(key);
201}
202
203} // namespace lambda
204} // namespace art