diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java index 990cfce958303755520c57f071ad0a844f7b1ec2..edba93cf227be044180e76fbb148bbc26b1ff9d8 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -142,7 +142,7 @@ public final class AccessArgumentNode extends RNode { private void checkPromiseFactory() { if (factory == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - Closure defaultClosure = formals.getOrCreatePromiseClosure(formals.getDefaultArgument(index)); + Closure defaultClosure = formals.getClosureCache().getOrCreatePromiseClosure(formals.getDefaultArgument(index)); factory = RPromiseFactory.create(PromiseState.Default, defaultClosure); } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java index c9baefdb00e4c3eaa15eb028458d5ee2468b3bd7..b4093331fb475e1187560f1609c512fa197ced46 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,6 +55,7 @@ import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor; import com.oracle.truffle.r.runtime.builtins.RBuiltinKind; import com.oracle.truffle.r.runtime.data.Closure; import com.oracle.truffle.r.runtime.data.ClosureCache; +import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.REmpty; import com.oracle.truffle.r.runtime.data.RFunction; @@ -163,7 +164,7 @@ public class ArgumentMatcher { argNodes = suppliedArgs.getArguments(); signature = suppliedArgs.getSignature(); } - return ArgumentMatcher.matchNodes(target, argNodes, signature, s3DefaultArguments, callingNode, arguments, noOpt); + return ArgumentMatcher.matchNodes(target, argNodes, signature, s3DefaultArguments, callingNode, arguments.getClosureCache(), noOpt); } public static MatchPermutation matchArguments(ArgumentsSignature supplied, ArgumentsSignature formal, RBaseNode callingNode, RBuiltinDescriptor builtin) { @@ -327,7 +328,7 @@ public class ArgumentMatcher { * accordingly. */ private static Arguments<RNode> matchNodes(RRootNode target, RNode[] suppliedArgs, ArgumentsSignature suppliedSignature, S3DefaultArguments s3DefaultArguments, RBaseNode callingNode, - ClosureCache closureCache, boolean noOpt) { + RNodeClosureCache closureCache, boolean noOpt) { CompilerAsserts.neverPartOfCompilation(); assert suppliedArgs.length == suppliedSignature.getLength(); @@ -494,13 +495,13 @@ public class ArgumentMatcher { * InfixEmulationFunctions.tilde). */ RNode defaultArg = formals.getDefaultArgument(formalIndex); - Closure defaultClosure = formals.getOrCreatePromiseClosure(defaultArg); + Closure defaultClosure = formals.getClosureCache().getOrCreatePromiseClosure(defaultArg); return PromiseNode.create(RPromiseFactory.create(PromiseState.Default, defaultClosure), noOpt, false); } return ConstantNode.create(formals.getInternalDefaultArgumentAt(formalIndex)); } - private static RNode wrapMatched(FormalArguments formals, RBuiltinDescriptor builtin, ClosureCache closureCache, RNode suppliedArg, int formalIndex, boolean noOpt, FastPathFactory fastPath) { + private static RNode wrapMatched(FormalArguments formals, RBuiltinDescriptor builtin, RNodeClosureCache closureCache, RNode suppliedArg, int formalIndex, boolean noOpt, FastPathFactory fastPath) { // Create promise, unless it's the empty value if (suppliedArg instanceof ConstantNode) { ConstantNode a = (ConstantNode) suppliedArg; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java index f5f5cf0093a4ce1a615879d794cbe79ecb83dad9..aa7f399b5cd68256b509ad420d882144e43b01d9 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,6 @@ package com.oracle.truffle.r.nodes.function; import java.util.Arrays; -import java.util.IdentityHashMap; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; @@ -34,15 +33,14 @@ import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.profiles.BranchProfile; -import com.oracle.truffle.r.nodes.access.FrameSlotNode; import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode; import com.oracle.truffle.r.runtime.Arguments; import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RInternalError; -import com.oracle.truffle.r.runtime.data.Closure; import com.oracle.truffle.r.runtime.data.ClosureCache; +import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RMissing; import com.oracle.truffle.r.runtime.nodes.RBaseNode; @@ -57,7 +55,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; * {@link RootCallTarget} for every argument. * </p> */ -public final class CallArgumentsNode extends RBaseNode implements UnmatchedArguments { +public final class CallArgumentsNode extends RBaseNode { /** * A list of arguments. Single arguments may be <code>null</code>; semantics have to be * specified by implementing classes @@ -66,24 +64,22 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum protected final ArgumentsSignature signature; - @Child private FrameSlotNode varArgsSlotNode; @Child private PromiseCheckHelperNode promiseHelper; + private final RNodeClosureCache closureCache = new RNodeClosureCache(); + /** * If a supplied argument is a {@link ReadVariableNode} whose name is "...", this field contains * the index of the name. Otherwise it is an empty list. */ @CompilationFinal(dimensions = 1) private final int[] varArgsSymbolIndices; - private final IdentityHashMap<RNode, Closure> closureCache = new IdentityHashMap<>(); - private CallArgumentsNode(RNode[] arguments, ArgumentsSignature signature, int[] varArgsSymbolIndices) { assert signature != null && signature.getLength() == arguments.length : Arrays.toString(arguments) + " " + signature; this.arguments = arguments; this.signature = signature; assert signature != null; this.varArgsSymbolIndices = varArgsSymbolIndices; - this.varArgsSlotNode = !containsVarArgsSymbol() ? null : FrameSlotNode.create(ArgumentsSignature.VARARG_NAME); } /** @@ -126,6 +122,10 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum return varArgs; } + public RNodeClosureCache getClosureCache() { + return closureCache; + } + /** * This methods unrolls all "..." in the argument list. The result varies if the number of * arguments in the varargs or their names change. @@ -262,25 +262,14 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum return varArgsSymbolIndices.length > 0; } - public int[] getVarArgsSymbolIndices() { - return varArgsSymbolIndices; - } - - @Override - public IdentityHashMap<RNode, Closure> getContent() { - return closureCache; - } - /** * @return The {@link RNode}s of the arguments given to a function call, in the same order. A * single argument being <code>null</code> means 'argument not provided'. */ - @Override public RNode[] getArguments() { return arguments; } - @Override public ArgumentsSignature getSignature() { return signature; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FormalArguments.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FormalArguments.java index 6e6db26cb0fccd899fb10c9f7fd4c50d8b6b4f7e..bd6d0baf292d733a7ecd2c8e449d959acfa2d817 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FormalArguments.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FormalArguments.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,6 @@ package com.oracle.truffle.r.nodes.function; import java.util.Arrays; -import java.util.IdentityHashMap; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.RootCallTarget; @@ -31,8 +30,8 @@ import com.oracle.truffle.r.nodes.access.ConstantNode; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.runtime.Arguments; import com.oracle.truffle.r.runtime.ArgumentsSignature; -import com.oracle.truffle.r.runtime.data.Closure; import com.oracle.truffle.r.runtime.data.ClosureCache; +import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RMissing; import com.oracle.truffle.r.runtime.data.RNull; @@ -50,12 +49,10 @@ import com.oracle.truffle.r.runtime.nodes.RNode; * ever one {@link RootCallTarget} for every default argument. * </p> */ -public final class FormalArguments extends Arguments<RNode> implements ClosureCache { +public final class FormalArguments extends Arguments<RNode> { public static final FormalArguments NO_ARGS = new FormalArguments(new RNode[0], new Object[0], ArgumentsSignature.empty(0)); - private final IdentityHashMap<RNode, Closure> closureCache = new IdentityHashMap<>(); - /** * These argument constants define what will be passed along in case there is no supplied * argument for the given argument slot. In the case of normal functions (as opposed to @@ -64,6 +61,8 @@ public final class FormalArguments extends Arguments<RNode> implements ClosureCa */ @CompilationFinal(dimensions = 1) private final Object[] internalDefaultArguments; + private final RNodeClosureCache closureCache = new RNodeClosureCache(); + private FormalArguments(RNode[] defaultArguments, Object[] internalDefaultArguments, ArgumentsSignature signature) { super(defaultArguments, signature); this.internalDefaultArguments = internalDefaultArguments; @@ -117,8 +116,7 @@ public final class FormalArguments extends Arguments<RNode> implements ClosureCa value == RArgsValuesAndNames.EMPTY; } - @Override - public IdentityHashMap<RNode, Closure> getContent() { + public RNodeClosureCache getClosureCache() { return closureCache; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java index 8001fc01002329f63db99348295bd5e6c34fd54c..e0dbba8ff097a1738b959f3d45146a6656cc1aee 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java @@ -72,6 +72,7 @@ import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.context.TruffleRLanguage; import com.oracle.truffle.r.runtime.data.Closure; import com.oracle.truffle.r.runtime.data.ClosureCache; +import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RPairList; import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; @@ -87,7 +88,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup; import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; import com.oracle.truffle.r.runtime.nodes.RSyntaxVisitor; -public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNode, RSyntaxFunction, ClosureCache { +public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNode, RSyntaxFunction { private final FormalArguments formalArguments; @@ -108,6 +109,8 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo private SourceSection sourceSectionR; private final SourceSection[] argSourceSections; + private final RNodeClosureCache closureCache = new RNodeClosureCache(); + @Child private RNode saveArguments; @Child private FrameSlotNode onExitSlot; @Child private InlineCacheNode onExitExpressionCache; @@ -380,7 +383,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo CompilerDirectives.transferToInterpreter(); RInternalError.shouldNotReachHere("unexpected type for on.exit entry: " + expr.car()); } - onExitExpressionCache.execute(frame, getOrCreateLanguageClosure((RNode) expr.car())); + onExitExpressionCache.execute(frame, closureCache.getOrCreateLanguageClosure((RNode) expr.car())); } } } catch (ReturnException ex) { @@ -565,13 +568,4 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo } return handlerStackSlot; } - - @Override - public IdentityHashMap<RNode, Closure> getContent() { - if (languageClosureCache == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - languageClosureCache = new IdentityHashMap<>(); - } - return languageClosureCache; - } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java index 8167d69768ac27941ebe9caae88aff77e3cb21f6..adb31fe1b3da2c65c83c82b723655ac7d6fd04ea 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,7 +48,7 @@ import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RType; import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.data.Closure; -import com.oracle.truffle.r.runtime.data.ClosureCache; +import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RMissing; import com.oracle.truffle.r.runtime.data.RPromise; @@ -356,7 +356,7 @@ public abstract class PromiseNode extends RNode { } @TruffleBoundary - static RNode createVarArgs(RNode[] nodes, ArgumentsSignature signature, ClosureCache closureCache, boolean forcedEager) { + static RNode createVarArgs(RNode[] nodes, ArgumentsSignature signature, RNodeClosureCache closureCache, boolean forcedEager) { return new VarArgsPromiseNode(nodes, signature, closureCache, forcedEager); } @@ -368,7 +368,7 @@ public abstract class PromiseNode extends RNode { private final Closure[] closures; private final ArgumentsSignature signature; - private VarArgsPromiseNode(RNode[] nodes, ArgumentsSignature signature, ClosureCache closureCache, boolean forcedEager) { + private VarArgsPromiseNode(RNode[] nodes, ArgumentsSignature signature, RNodeClosureCache closureCache, boolean forcedEager) { this.promised = new RNode[nodes.length]; this.closures = new Closure[nodes.length]; boolean noOpt = false; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnmatchedArguments.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnmatchedArguments.java deleted file mode 100644 index fd0461f0dc9fac10eeb915e7d01ccc980dd7bfb0..0000000000000000000000000000000000000000 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnmatchedArguments.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.r.nodes.function; - -import com.oracle.truffle.r.runtime.ArgumentsSignature; -import com.oracle.truffle.r.runtime.data.ClosureCache; -import com.oracle.truffle.r.runtime.nodes.RNode; - -/** - * An interface all arguments that are going to be matched need to implement. - */ -public interface UnmatchedArguments extends ClosureCache { - /** - * @return The arguments to be matched. Individual {@link RNode}s may be <code>null</code> to - * denote "missingness"! - */ - RNode[] getArguments(); - - ArgumentsSignature getSignature(); -} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ClosureCache.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ClosureCache.java index a8976382cd81765d33f10da5b45468cc6983709f..ef41f3c88e4d5422105bc6e8ac9ae520b4902c60 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ClosureCache.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ClosureCache.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,49 +22,64 @@ */ package com.oracle.truffle.r.runtime.data; -import java.util.IdentityHashMap; -import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.nodes.RNode; +import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; /** - * A trait that enables the caching of {@link Closure}s for certain expressions ({@link RNode}s). + * Class that enables the caching of {@link Closure}s for certain expressions ({@link RNode}s). + * Instance of this class is supposed to be a field in AST node, which is using the cache. The field + * must be initialized in the constructor, but the underlying data structure (ConcurrentHashMap) is + * initialized lazily. All methods are thread safe. + * + * Closures need to be cached so that we cache the corresponding call-targets and if the expression + * is evaluated again, we invoke it through the same call-target, which may be compiled by Truffle. */ -public interface ClosureCache { +public abstract class ClosureCache<K> { - default Closure getOrCreatePromiseClosure(RNode expr) { - return getOrCreateClosure(Closure.PROMISE_CLOSURE_WRAPPER_NAME, expr); + private ConcurrentHashMap<K, Closure> cache; + + public Closure getOrCreatePromiseClosure(K key) { + return getOrCreateClosure(Closure.PROMISE_CLOSURE_WRAPPER_NAME, key); } - default Closure getOrCreateLanguageClosure(RNode expr) { - return getOrCreateClosure(Closure.LANGUAGE_CLOSURE_WRAPPER_NAME, expr); + public Closure getOrCreateLanguageClosure(K key) { + return getOrCreateClosure(Closure.LANGUAGE_CLOSURE_WRAPPER_NAME, key); } - /** - * @param expr - * @return A {@link Closure} representing the given {@link RNode}. If expr is <code>null</code> - * <code>null</code> is returned. - */ + protected abstract RNode keyToNode(K key); + @TruffleBoundary - default Closure getOrCreateClosure(String name, RNode expr) { - if (expr == null) { + private Closure getOrCreateClosure(String name, K key) { + if (key == null) { return null; } + if (cache == null) { + initMap(); + } + return cache.computeIfAbsent(key, k -> Closure.create(name, keyToNode(k))); + } - IdentityHashMap<RNode, Closure> cache = getContent(); - Closure result = cache.get(expr); - if (result == null) { - result = Closure.create(name, expr); - cache.put(expr, result); + private synchronized void initMap() { + if (cache == null) { + cache = new ConcurrentHashMap<>(); } - return result; } - /** - * Access to the raw content. - * - * @return The {@link Map} containing the cached values - */ - IdentityHashMap<RNode, Closure> getContent(); + public static final class RNodeClosureCache extends ClosureCache<RNode> { + @Override + protected RNode keyToNode(RNode key) { + return key; + } + } + + public static final class SymbolClosureCache extends ClosureCache<String> { + @Override + protected RNode keyToNode(String key) { + return RContext.getASTBuilder().lookup(RSyntaxNode.SOURCE_UNAVAILABLE, key, false).asRNode(); + } + } }