Skip to content

Commit fbaebf6

Browse files
Nicolas Laurenteregon
authored andcommitted
Create CallTargets lazily instead of using LazyRubyNode
* Use a separate CachedSupplier to avoid retaining lazy state when the result has been computed. CachedSupplier#get() can be called repetitively and caches the result.
1 parent 200ce6e commit fbaebf6

File tree

11 files changed

+208
-218
lines changed

11 files changed

+208
-218
lines changed

src/main/java/org/truffleruby/builtins/CoreMethodNodeManager.java

Lines changed: 74 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
import java.util.function.Function;
1414

1515
import com.oracle.truffle.api.CompilerDirectives;
16+
import com.oracle.truffle.api.source.SourceSection;
1617
import org.truffleruby.RubyContext;
1718
import org.truffleruby.RubyLanguage;
19+
import org.truffleruby.collections.CachedSupplier;
1820
import org.truffleruby.core.array.ArrayUtils;
1921
import org.truffleruby.core.cast.TaintResultNode;
2022
import org.truffleruby.core.klass.RubyClass;
@@ -24,7 +26,6 @@
2426
import org.truffleruby.core.numeric.FixnumLowerNodeGen;
2527
import org.truffleruby.core.string.StringUtils;
2628
import org.truffleruby.core.support.TypeNodes;
27-
import org.truffleruby.language.LazyRubyNode;
2829
import org.truffleruby.language.LexicalScope;
2930
import org.truffleruby.language.NotProvided;
3031
import org.truffleruby.language.RubyNode;
@@ -121,118 +122,117 @@ private RubyClass getSingletonClass(Object object) {
121122
}
122123

123124
private void addCoreMethod(RubyModule module, MethodDetails methodDetails) {
124-
final CoreMethod method = methodDetails.getMethodAnnotation();
125+
final CoreMethod annotation = methodDetails.getMethodAnnotation();
125126

126-
final String[] names = method.names();
127+
final String[] names = annotation.names();
127128
assert names.length >= 1;
128129

129-
final Visibility visibility = method.visibility();
130-
verifyUsage(module, methodDetails, method, visibility);
130+
final Visibility visibility = annotation.visibility();
131+
verifyUsage(module, methodDetails, annotation, visibility);
132+
133+
final String keywordAsOptional = annotation.keywordAsOptional().isEmpty()
134+
? null
135+
: annotation.keywordAsOptional();
136+
final Arity arity = createArity(
137+
annotation.required(),
138+
annotation.optional(),
139+
annotation.rest(),
140+
keywordAsOptional);
141+
final NodeFactory<? extends RubyNode> nodeFactory = methodDetails.getNodeFactory();
142+
final boolean onSingleton = annotation.onSingleton() || annotation.constructor();
143+
final boolean isModuleFunc = annotation.isModuleFunction();
144+
final Split split = context.getOptions().CORE_ALWAYS_CLONE ? Split.ALWAYS : annotation.split();
131145

132-
final String keywordAsOptional = method.keywordAsOptional().isEmpty() ? null : method.keywordAsOptional();
133-
final Arity arity = createArity(method.required(), method.optional(), method.rest(), keywordAsOptional);
146+
final Function<SharedMethodInfo, RootCallTarget> callTargetFactory = sharedMethodInfo -> {
147+
final RubyNode methodNode = createCoreMethodNode(nodeFactory, annotation, sharedMethodInfo);
148+
return createCallTarget(context, sharedMethodInfo, methodNode, split);
149+
};
134150

135-
final NodeFactory<? extends RubyNode> nodeFactory = methodDetails.getNodeFactory();
136-
final Function<SharedMethodInfo, RubyNode> methodNodeFactory = sharedMethodInfo -> createCoreMethodNode(
137-
nodeFactory,
138-
method,
139-
sharedMethodInfo);
140-
141-
final boolean onSingleton = method.onSingleton() || method.constructor();
142-
addMethods(
143-
module,
144-
method.isModuleFunction(),
145-
onSingleton,
146-
names,
147-
arity,
148-
visibility,
149-
methodNodeFactory,
150-
method.split());
151+
addMethods(module, isModuleFunc, onSingleton, names, arity, visibility, callTargetFactory);
151152
}
152153

153-
public void addLazyCoreMethod(String nodeFactoryName, String moduleName, boolean isClass, Visibility visibility,
154-
boolean isModuleFunction, boolean onSingleton,
155-
Split split, int required, int optional, boolean rest, String keywordAsOptional, String... names) {
154+
public void addLazyCoreMethod(
155+
String nodeFactoryName,
156+
String moduleName,
157+
boolean isClass,
158+
Visibility visibility,
159+
boolean isModuleFunc,
160+
boolean onSingleton,
161+
Split split,
162+
int required,
163+
int optional,
164+
boolean rest,
165+
String keywordAsOptional,
166+
String... names) {
167+
156168
final RubyModule module = getModule(moduleName, isClass);
157169
final Arity arity = createArity(required, optional, rest, keywordAsOptional);
170+
final Split finalSplit = context.getOptions().CORE_ALWAYS_CLONE ? Split.ALWAYS : split;
158171

159-
Function<SharedMethodInfo, RubyNode> methodNodeFactory = sharedMethodInfo -> new LazyRubyNode(language, () -> {
172+
final Function<SharedMethodInfo, RootCallTarget> callTargetFactory = sharedMethodInfo -> {
160173
final NodeFactory<? extends RubyNode> nodeFactory = loadNodeFactory(nodeFactoryName);
161-
final CoreMethod methodAnnotation = nodeFactory.getNodeClass().getAnnotation(CoreMethod.class);
162-
return createCoreMethodNode(nodeFactory, methodAnnotation, sharedMethodInfo);
163-
});
174+
final CoreMethod annotation = nodeFactory.getNodeClass().getAnnotation(CoreMethod.class);
175+
final RubyNode methodNode = createCoreMethodNode(nodeFactory, annotation, sharedMethodInfo);
176+
return createCallTarget(context, sharedMethodInfo, methodNode, finalSplit);
177+
};
164178

165-
addMethods(module, isModuleFunction, onSingleton, names, arity, visibility, methodNodeFactory, split);
179+
addMethods(module, isModuleFunc, onSingleton, names, arity, visibility, callTargetFactory);
166180
}
167181

168-
private void addMethods(RubyModule module, boolean isModuleFunction, boolean onSingleton, String[] names,
169-
Arity arity, Visibility visibility,
170-
Function<SharedMethodInfo, RubyNode> methodNodeFactory, Split split) {
182+
private void addMethods(
183+
RubyModule module,
184+
boolean isModuleFunction,
185+
boolean onSingleton,
186+
String[] names,
187+
Arity arity,
188+
Visibility visibility,
189+
Function<SharedMethodInfo, RootCallTarget> callTargetFactory) {
171190
if (isModuleFunction) {
172-
addMethod(context, module, methodNodeFactory, names, Visibility.PRIVATE, arity, split);
173-
addMethod(
174-
context,
175-
getSingletonClass(module),
176-
methodNodeFactory,
177-
names,
178-
Visibility.PUBLIC,
179-
arity,
180-
split);
191+
addMethod(context, module, callTargetFactory, names, arity, Visibility.PRIVATE);
192+
addMethod(context, getSingletonClass(module), callTargetFactory, names, arity, Visibility.PUBLIC);
181193
} else if (onSingleton) {
182-
addMethod(context, getSingletonClass(module), methodNodeFactory, names, visibility, arity, split);
194+
addMethod(context, getSingletonClass(module), callTargetFactory, names, arity, visibility);
183195
} else {
184-
addMethod(context, module, methodNodeFactory, names, visibility, arity, split);
196+
addMethod(context, module, callTargetFactory, names, arity, visibility);
185197
}
186198
}
187199

188-
private static void addMethod(RubyContext context, RubyModule module,
189-
Function<SharedMethodInfo, RubyNode> methodNodeFactory, String[] names, Visibility originalVisibility,
200+
private static void addMethod(
201+
RubyContext context,
202+
RubyModule module,
203+
Function<SharedMethodInfo, RootCallTarget> callTargetFactory,
204+
String[] names,
190205
Arity arity,
191-
Split split) {
206+
Visibility visibility) {
207+
192208
final LexicalScope lexicalScope = new LexicalScope(context.getRootLexicalScope(), module);
193209

194210
for (String name : names) {
195-
Visibility visibility = originalVisibility;
196-
if (ModuleOperations.isMethodPrivateFromName(name)) {
197-
visibility = Visibility.PRIVATE;
198-
}
199211
final SharedMethodInfo sharedMethodInfo = makeSharedMethodInfo(context, lexicalScope, module, name, arity);
200-
final RubyNode methodNode = methodNodeFactory.apply(sharedMethodInfo);
201-
split = context.getOptions().CORE_ALWAYS_CLONE ? Split.ALWAYS : split;
202-
final RootCallTarget callTarget = createCallTarget(context, sharedMethodInfo, methodNode, split);
203-
final InternalMethod method = new InternalMethod(
212+
213+
module.fields.addMethod(context, null, new InternalMethod(
204214
context,
205215
sharedMethodInfo,
206216
sharedMethodInfo.getLexicalScope(),
207217
DeclarationContext.NONE,
208218
name,
209219
module,
210-
visibility,
211-
callTarget);
212-
213-
module.fields.addMethod(context, null, method);
220+
ModuleOperations.isMethodPrivateFromName(name) ? Visibility.PRIVATE : visibility,
221+
null,
222+
new CachedSupplier<>(() -> callTargetFactory.apply(sharedMethodInfo))));
214223
}
215224
}
216225

217226
private static SharedMethodInfo makeSharedMethodInfo(RubyContext context, LexicalScope lexicalScope,
218227
RubyModule module, String name, Arity arity) {
219-
return new SharedMethodInfo(
220-
context.getCoreLibrary().sourceSection,
221-
lexicalScope,
222-
arity,
223-
module,
224-
name,
225-
0,
226-
"builtin",
227-
null);
228+
final SourceSection sourceSection = context.getCoreLibrary().sourceSection;
229+
return new SharedMethodInfo(sourceSection, lexicalScope, arity, module, name, 0, "builtin", null);
228230
}
229231

230232
private static Arity createArity(int required, int optional, boolean rest, String keywordAsOptional) {
231-
if (keywordAsOptional == null) {
232-
return new Arity(required, optional, rest);
233-
} else {
234-
return new Arity(required, optional, rest, 0, new String[]{ keywordAsOptional }, true, false);
235-
}
233+
return keywordAsOptional == null
234+
? new Arity(required, optional, rest)
235+
: new Arity(required, optional, rest, 0, new String[]{ keywordAsOptional }, true, false);
236236
}
237237

238238
private static RootCallTarget createCallTarget(RubyContext context, SharedMethodInfo sharedMethodInfo,
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. This
3+
* code is released under a tri EPL/GPL/LGPL license. You can use it,
4+
* redistribute it and/or modify it under the terms of the:
5+
*
6+
* Eclipse Public License version 2.0, or
7+
* GNU General Public License version 2, or
8+
* GNU Lesser General Public License version 2.1.
9+
*/
10+
package org.truffleruby.collections;
11+
12+
import java.util.function.Supplier;
13+
14+
public class CachedSupplier<T> implements Supplier<T> {
15+
16+
private T value = null;
17+
private Supplier<T> supplier;
18+
19+
public CachedSupplier(Supplier<T> supplier) {
20+
this.supplier = supplier;
21+
}
22+
23+
@Override
24+
public T get() {
25+
if (value != null) {
26+
return value;
27+
}
28+
synchronized (this) {
29+
if (value == null) {
30+
value = supplier.get();
31+
supplier = null;
32+
}
33+
return value;
34+
}
35+
}
36+
}

src/main/java/org/truffleruby/language/LazyRubyNode.java

Lines changed: 0 additions & 114 deletions
This file was deleted.

0 commit comments

Comments
 (0)