Skip to content

Commit 37dce96

Browse files
committed
Re-factoring the LanguageCache so that languages can be re-initialized with reflection.
This feature is required for running an AOT compilation server that can process requests for different sets of languages. On each compilation the server must remove the unused languages in order to remove them from compilation.
1 parent ece7af2 commit 37dce96

File tree

2 files changed

+87
-25
lines changed

2 files changed

+87
-25
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package com.oracle.truffle.api.vm;
24+
25+
import java.lang.reflect.Field;
26+
import java.lang.reflect.InvocationTargetException;
27+
import java.lang.reflect.Method;
28+
import java.util.Map;
29+
30+
import org.junit.Test;
31+
32+
public class LanguageCacheReflectiveReinitializationTest {
33+
34+
@Test
35+
@SuppressWarnings("unchecked")
36+
public void canReinitializeLanguages() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
37+
System.setProperty("com.oracle.truffle.aot", "true");
38+
39+
Class<?> languageCacheClass = Class.forName("com.oracle.truffle.api.vm.LanguageCache");
40+
Field languageCacheField = languageCacheClass.getDeclaredField("CACHE");
41+
languageCacheField.setAccessible(true);
42+
assert languageCacheField.get(null) == null;
43+
44+
Method initMethod = languageCacheClass.getDeclaredMethod("initializeLanguages", ClassLoader.class);
45+
initMethod.setAccessible(true);
46+
Map<String, Object> languages = (Map<String, Object>) initMethod.invoke(null, Thread.currentThread().getContextClassLoader());
47+
48+
assert languages.containsKey("application/x-test") : "Re-initialized languages must contain application/x-test language after reflective initialization.";
49+
}
50+
}

truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/LanguageCache.java

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
* cache with languages found in application classloader.
4747
*/
4848
final class LanguageCache {
49-
static final boolean PRELOAD;
49+
private static final boolean PRELOAD;
5050
private static final Map<String, LanguageCache> CACHE;
5151
private TruffleLanguage<?> language;
5252
private final String className;
@@ -55,18 +55,29 @@ final class LanguageCache {
5555
private final String version;
5656

5757
static {
58-
Map<String, LanguageCache> map = null;
59-
if (TruffleOptions.AOT) {
60-
map = languages();
61-
for (LanguageCache info : map.values()) {
62-
info.getImpl(true);
63-
}
64-
}
65-
CACHE = map;
58+
CACHE = TruffleOptions.AOT ? initializeLanguages(loader()) : null;
6659
PRELOAD = CACHE != null;
6760
}
6861

69-
LanguageCache(String prefix, Properties info, TruffleLanguage<?> language) {
62+
/**
63+
* This method initializes all languages under the provided classloader.
64+
*
65+
* NOTE: Method's signature should not be changed as it is reflectively invoked from AOT
66+
* compilation.
67+
*
68+
* @param loader The classloader to be used for finding languages.
69+
* @return A map of initialized languages.
70+
*/
71+
private static Map<String, LanguageCache> initializeLanguages(ClassLoader loader) {
72+
Map<String, LanguageCache> map;
73+
map = createLanguages(loader);
74+
for (LanguageCache info : map.values()) {
75+
info.createLanguage(loader);
76+
}
77+
return map;
78+
}
79+
80+
private LanguageCache(String prefix, Properties info, TruffleLanguage<?> language) {
7081
this.className = info.getProperty(prefix + "className");
7182
this.name = info.getProperty(prefix + "name");
7283
this.version = info.getProperty(prefix + "version");
@@ -90,20 +101,14 @@ private static ClassLoader loader() {
90101
return l;
91102
}
92103

93-
private static TruffleLanguage<?> find(String name, ClassLoader loader) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
94-
if (PRELOAD) {
95-
return CACHE.get(name).language;
96-
} else {
97-
Class<?> langClazz = Class.forName(name, true, loader);
98-
return (TruffleLanguage<?>) langClazz.getField("INSTANCE").get(null);
99-
}
100-
}
101-
102104
static Map<String, LanguageCache> languages() {
103105
if (PRELOAD) {
104106
return CACHE;
105107
}
106-
ClassLoader loader = loader();
108+
return createLanguages(loader());
109+
}
110+
111+
private static Map<String, LanguageCache> createLanguages(ClassLoader loader) {
107112
Map<String, LanguageCache> map = new LinkedHashMap<>();
108113
Enumeration<URL> en;
109114
try {
@@ -154,13 +159,20 @@ TruffleLanguage<?> getImpl(boolean create) {
154159
return language;
155160
}
156161
if (create) {
157-
try {
158-
language = LanguageCache.find(className, loader());
159-
} catch (Exception ex) {
160-
throw new IllegalStateException("Cannot initialize " + getName() + " language with implementation " + className, ex);
161-
}
162+
createLanguage(loader());
162163
}
163164
return language;
164165
}
165166

167+
private void createLanguage(ClassLoader loader) {
168+
try {
169+
TruffleLanguage<?> result;
170+
Class<?> langClazz = Class.forName(className, true, loader);
171+
result = (TruffleLanguage<?>) langClazz.getField("INSTANCE").get(null);
172+
language = result;
173+
} catch (Exception ex) {
174+
throw new IllegalStateException("Cannot initialize " + getName() + " language with implementation " + className, ex);
175+
}
176+
}
177+
166178
}

0 commit comments

Comments
 (0)