Skip to content

Commit 814d3c1

Browse files
committedJun 12, 2018
Prototype of load/require hook for #5206.
1 parent 459b812 commit 814d3c1

File tree

5 files changed

+114
-20
lines changed

5 files changed

+114
-20
lines changed
 

Diff for: ‎core/src/main/java/org/jruby/embed/osgi/internal/OSGiLoadService.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ protected LoadServiceResource findFileInClasspath(String name) {
103103
if (bundle != null) {
104104
URL url = bundle.getEntry(sb.toString());
105105
if (url != null) {
106-
return new LoadServiceResource(
106+
return new LoadServiceResource(runtime.getLoadService(),
107107
OSGiFileLocator.getLocalURL(url), name);
108108
}
109109
}

Diff for: ‎core/src/main/java/org/jruby/runtime/load/LibrarySearcher.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,10 @@ public void load(Ruby runtime, boolean wrap) {
245245
try {
246246
ris = resource.inputStream();
247247

248-
if (runtime.getInstanceConfig().getCompileMode().shouldPrecompileAll()) {
248+
RubyString processed = runtime.getLoadService().runLoadHooks(scriptName, ris);
249+
if (processed != null) {
250+
runtime.loadFile(scriptName, new LoadServiceResourceInputStream(processed.getBytes()), wrap);
251+
} else if (runtime.getInstanceConfig().getCompileMode().shouldPrecompileAll()) {
249252
runtime.compileAndLoadFile(scriptName, ris, wrap);
250253
} else {
251254
runtime.loadFile(scriptName, new LoadServiceResourceInputStream(ris), wrap);

Diff for: ‎core/src/main/java/org/jruby/runtime/load/LoadService.java

+71-8
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,21 @@
3434

3535
package org.jruby.runtime.load;
3636

37+
import java.io.ByteArrayOutputStream;
3738
import java.io.File;
3839
import java.io.FileNotFoundException;
3940
import java.io.IOException;
41+
import java.io.InputStream;
4042
import java.net.MalformedURLException;
4143
import java.net.URISyntaxException;
4244
import java.net.URI;
4345
import java.net.URL;
4446
import java.security.AccessControlException;
4547
import java.util.HashMap;
48+
import java.util.HashSet;
4649
import java.util.List;
4750
import java.util.Map;
51+
import java.util.Set;
4852
import java.util.concurrent.ConcurrentHashMap;
4953
import java.util.concurrent.atomic.AtomicInteger;
5054
import java.util.concurrent.locks.ReentrantLock;
@@ -192,6 +196,8 @@ public String[] getSuffixes() {
192196
protected final Ruby runtime;
193197
protected final LibrarySearcher librarySearcher;
194198

199+
protected final Set<LoadHook> loadHooks = new HashSet<>();
200+
195201
public LoadService(Ruby runtime) {
196202
this.runtime = runtime;
197203
if (RubyInstanceConfig.DEBUG_LOAD_TIMINGS) {
@@ -1084,7 +1090,7 @@ protected LoadServiceResource tryResourceFromCWD(SearchState state, String baseN
10841090
debugLogTry("resourceFromCWD", file.toString());
10851091
if (file.isFile() && file.isAbsolute() && file.canRead()) {
10861092
boolean absolute = true;
1087-
foundResource = new LoadServiceResource(file, getFileName(file, namePlusSuffix), absolute);
1093+
foundResource = new LoadServiceResource(this, file, getFileName(file, namePlusSuffix), absolute);
10881094
debugLogFound(foundResource);
10891095
state.setLoadName(resolveLoadName(foundResource, namePlusSuffix));
10901096
break;
@@ -1138,7 +1144,7 @@ protected LoadServiceResource tryResourceFromHome(SearchState state, String base
11381144
boolean absolute = true;
11391145

11401146
state.setLoadName(file.getPath());
1141-
foundResource = new LoadServiceResource(file, state.loadName, absolute);
1147+
foundResource = new LoadServiceResource(this, file, state.loadName, absolute);
11421148
debugLogFound(foundResource);
11431149
break;
11441150
}
@@ -1164,7 +1170,7 @@ protected LoadServiceResource tryResourceFromJarURL(SearchState state, String ba
11641170
URL url = resourceUri.toURL();
11651171
debugLogTry("resourceFromJarURL", url.toString());
11661172
if (url.openStream() != null) {
1167-
foundResource = new LoadServiceResource(url, namePlusSuffix);
1173+
foundResource = new LoadServiceResource(this, url, namePlusSuffix);
11681174
debugLogFound(foundResource);
11691175
}
11701176
} catch (FileNotFoundException e) {
@@ -1191,7 +1197,7 @@ protected LoadServiceResource tryResourceFromJarURL(SearchState state, String ba
11911197
debugLogTry("resourceFromJarURL", expandedFilename);
11921198
if(file.getJarEntry(expandedFilename) != null) {
11931199
URI resourceUri = new URI("jar", "file:" + jarFile + "!/" + expandedFilename, null);
1194-
foundResource = new LoadServiceResource(resourceUri.toURL(), namePlusSuffix);
1200+
foundResource = new LoadServiceResource(this, resourceUri.toURL(), namePlusSuffix);
11951201
debugLogFound(foundResource);
11961202
}
11971203
} catch (URISyntaxException e) {
@@ -1316,7 +1322,7 @@ protected LoadServiceResource tryResourceFromJarURLWithLoadPath(String namePlusS
13161322
if (current.getJarEntry(canonicalEntry) != null) {
13171323
try {
13181324
URI resourceUri = new URI("jar", "file:" + jarFileName + "!/" + canonicalEntry, null);
1319-
foundResource = new LoadServiceResource(resourceUri.toURL(), resourceUri.getSchemeSpecificPart());
1325+
foundResource = new LoadServiceResource(this, resourceUri.toURL(), resourceUri.getSchemeSpecificPart());
13201326
debugLogFound(foundResource);
13211327
} catch (URISyntaxException e) {
13221328
throw runtime.newIOError(e.getMessage());
@@ -1405,7 +1411,7 @@ protected LoadServiceResource tryResourceFromLoadPath( String namePlusSuffix,Str
14051411
debugLogTry("resourceFromLoadPath", "'" + actualPath.toString() + "' " + actualPath.isFile() + " " + actualPath.canRead());
14061412
}
14071413
if (actualPath.canRead()) {
1408-
foundResource = new LoadServiceResource(actualPath, reportedPath, absolute);
1414+
foundResource = new LoadServiceResource(this, actualPath, reportedPath, absolute);
14091415
debugLogFound(foundResource);
14101416
}
14111417
}
@@ -1452,7 +1458,7 @@ protected LoadServiceResource tryResourceAsIs(String namePlusSuffix, String debu
14521458
}
14531459

14541460
if (actualPath.isFile() && actualPath.canRead()) {
1455-
foundResource = new LoadServiceResource(actualPath, reportedPath);
1461+
foundResource = new LoadServiceResource(this, actualPath, reportedPath);
14561462
debugLogFound(foundResource);
14571463
}
14581464
}
@@ -1597,7 +1603,7 @@ public LoadServiceResource getClassPathResource(ClassLoader classLoader, String
15971603

15981604
if (loc != null) { // got it
15991605
String path = classpathFilenameFromURL(name, loc, isClasspathScheme);
1600-
LoadServiceResource foundResource = new LoadServiceResource(loc, path);
1606+
LoadServiceResource foundResource = new LoadServiceResource(this, loc, path);
16011607
debugLogFound(foundResource);
16021608
return foundResource;
16031609
}
@@ -1655,4 +1661,61 @@ protected String resolveLoadName(LoadServiceResource foundResource, String previ
16551661
protected String getFileName(JRubyFile file, String namePlusSuffix) {
16561662
return file.getAbsolutePath();
16571663
}
1664+
1665+
public interface LoadHook {
1666+
RubyString callback(String file, RubyString src);
1667+
}
1668+
1669+
public void addLoadHook(LoadHook loadHook) {
1670+
synchronized (loadHooks) {
1671+
loadHooks.add(loadHook);
1672+
}
1673+
}
1674+
1675+
public void removeLoadHook(LoadHook loadHook) {
1676+
synchronized (loadHooks) {
1677+
loadHooks.remove(loadHook);
1678+
}
1679+
}
1680+
1681+
public RubyString runLoadHooks(String file, byte[] src) {
1682+
if (loadHooks.size() > 0) {
1683+
synchronized (loadHooks) {
1684+
RubyString string = RubyString.newString(runtime, src);
1685+
for (LoadHook loadHook : loadHooks) {
1686+
string = loadHook.callback(file, string);
1687+
}
1688+
return string;
1689+
}
1690+
}
1691+
1692+
return null;
1693+
}
1694+
1695+
public RubyString runLoadHooks(String file, InputStream src) throws IOException {
1696+
if (loadHooks.size() > 0) {
1697+
synchronized (loadHooks) {
1698+
1699+
byte[] bytes = new byte[8192];
1700+
1701+
ByteArrayOutputStream baos = new ByteArrayOutputStream(8192);
1702+
1703+
int len;
1704+
while ((len = src.read(bytes)) != -1) {
1705+
baos.write(bytes, 0, len);
1706+
}
1707+
bytes = baos.toByteArray();
1708+
1709+
RubyString string = RubyString.newString(runtime, bytes);
1710+
1711+
for (LoadHook loadHook : loadHooks) {
1712+
string = loadHook.callback(file, string);
1713+
}
1714+
1715+
return string;
1716+
}
1717+
}
1718+
1719+
return null;
1720+
}
16581721
}

Diff for: ‎core/src/main/java/org/jruby/runtime/load/LoadServiceResource.java

+27-10
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
package org.jruby.runtime.load;
3030

31+
import org.jruby.RubyString;
3132
import org.jruby.util.URLUtil;
3233

3334
import java.io.File;
@@ -50,29 +51,26 @@ public class LoadServiceResource {
5051
private final String name;
5152
private final boolean absolute;
5253
private String absolutePath;
54+
private LoadService loadService;
5355

54-
public LoadServiceResource(URL resource, String name) {
56+
public LoadServiceResource(LoadService loadService, URL resource, String name) {
57+
this.loadService = loadService;
5558
this.resource = resource;
5659
this.path = null;
5760
this.name = name;
5861
this.absolute = false;
5962
}
60-
61-
public LoadServiceResource(URL resource, String name, boolean absolute) {
62-
this.resource = resource;
63-
this.path = null;
64-
this.name = name;
65-
this.absolute = absolute;
66-
}
6763

68-
public LoadServiceResource(File path, String name) {
64+
public LoadServiceResource(LoadService loadService, File path, String name) {
65+
this.loadService = loadService;
6966
this.resource = null;
7067
this.path = path;
7168
this.name = name;
7269
this.absolute = false;
7370
}
7471

75-
public LoadServiceResource(File path, String name, boolean absolute) {
72+
public LoadServiceResource(LoadService loadService, File path, String name, boolean absolute) {
73+
this.loadService = loadService;
7674
this.resource = null;
7775
this.path = path;
7876
this.name = name;
@@ -83,17 +81,28 @@ public InputStream getInputStream() throws IOException {
8381
if (resource != null) {
8482
InputStream is = resource.openStream();
8583
try {
84+
RubyString processed = loadService.runLoadHooks(resource.getPath(), is);
85+
if (processed != null) {
86+
return new LoadServiceResourceInputStream(processed.getBytes());
87+
}
8688
return new LoadServiceResourceInputStream(is);
8789
} finally {
8890
is.close();
8991
}
9092
}
93+
9194
byte[] bytes = new byte[(int)path.length()];
9295
ByteBuffer buffer = ByteBuffer.wrap(bytes);
9396
FileInputStream fis = new FileInputStream(path);
9497
FileChannel fc = fis.getChannel();
9598
fc.read(buffer);
9699
fis.close();
100+
101+
RubyString processed = loadService.runLoadHooks(path.getAbsolutePath(), bytes);
102+
if (processed != null) {
103+
bytes = processed.getBytes();
104+
}
105+
97106
return new LoadServiceResourceInputStream(bytes);
98107
}
99108

@@ -148,4 +157,12 @@ public String getAbsolutePath() {
148157
public boolean isAbsolute() {
149158
return absolute;
150159
}
160+
161+
@Deprecated
162+
public LoadServiceResource(URL resource, String name, boolean absolute) {
163+
this.resource = resource;
164+
this.path = null;
165+
this.name = name;
166+
this.absolute = absolute;
167+
}
151168
}

Diff for: ‎core/src/main/ruby/jruby/jruby.rb

+11
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,15 @@ def inspect_bytecode
112112

113113
autoload :ASM, 'jruby/asm.rb'
114114

115+
module VM
116+
def self.add_load_hook(&block)
117+
# kinda gross; interface wants RubyString but RubyNil does not coerce to null due to looking like an IRubyObject
Has a conversation. Original line has a conversation.
118+
new_block = ->(file, src) do
119+
new_src = block.call(file, src)
120+
new_src.nil? ? nil.to_java : new_src
121+
end
122+
JRuby.runtime.load_service.add_load_hook(new_block)
123+
end
124+
end
125+
115126
end

0 commit comments

Comments
 (0)
Please sign in to comment.