Igor Murashkin | 457e874 | 2015-10-22 17:37:50 -0700 | [diff] [blame] | 1 | /* |
| 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 | |
| 30 | namespace art { |
| 31 | namespace 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. |
| 39 | static 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 | |
| 84 | BoxClassTable::BoxClassTable() { |
| 85 | } |
| 86 | |
| 87 | BoxClassTable::~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 | |
| 94 | mirror::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 | |
| 162 | BoxClassTable::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 | |
| 175 | void 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 | |
| 182 | bool 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 | |
| 189 | bool 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 | |
| 199 | size_t BoxClassTable::HashFn::operator()(const UnorderedMapKeyType& key) const { |
| 200 | return std::hash<std::string>()(key); |
| 201 | } |
| 202 | |
| 203 | } // namespace lambda |
| 204 | } // namespace art |