/* * This code is copyrighted work by Daniel Luz . * * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt */ package json.ext; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyEncoding; import org.jruby.RubyModule; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; final class RuntimeInfo { // since the vast majority of cases runs just one runtime, // we optimize for that private static WeakReference runtime1 = new WeakReference(null); private static RuntimeInfo info1; // store remaining runtimes here (does not include runtime1) private static Map runtimes; // these fields are filled by the service loaders // Use WeakReferences so that RuntimeInfo doesn't indirectly hold a hard reference to // the Ruby runtime object, which would cause memory leaks in the runtimes map above. /** JSON */ WeakReference jsonModule; /** JSON::Ext::Generator::GeneratorMethods::String::Extend */ WeakReference stringExtendModule; /** JSON::Ext::Generator::State */ WeakReference generatorStateClass; /** JSON::SAFE_STATE_PROTOTYPE */ WeakReference safeStatePrototype; final WeakReference utf8; final WeakReference ascii8bit; // other encodings private final Map> encodings; private RuntimeInfo(Ruby runtime) { RubyClass encodingClass = runtime.getEncoding(); if (encodingClass == null) { // 1.8 mode utf8 = ascii8bit = null; encodings = null; } else { ThreadContext context = runtime.getCurrentContext(); utf8 = new WeakReference((RubyEncoding)RubyEncoding.find(context, encodingClass, runtime.newString("utf-8"))); ascii8bit = new WeakReference((RubyEncoding)RubyEncoding.find(context, encodingClass, runtime.newString("ascii-8bit"))); encodings = new HashMap>(); } } static RuntimeInfo initRuntime(Ruby runtime) { synchronized (RuntimeInfo.class) { if (runtime1.get() == runtime) { return info1; } else if (runtime1.get() == null) { runtime1 = new WeakReference(runtime); info1 = new RuntimeInfo(runtime); return info1; } else { if (runtimes == null) { runtimes = new WeakHashMap(1); } RuntimeInfo cache = runtimes.get(runtime); if (cache == null) { cache = new RuntimeInfo(runtime); runtimes.put(runtime, cache); } return cache; } } } public static RuntimeInfo forRuntime(Ruby runtime) { synchronized (RuntimeInfo.class) { if (runtime1.get() == runtime) return info1; RuntimeInfo cache = null; if (runtimes != null) cache = runtimes.get(runtime); assert cache != null : "Runtime given has not initialized JSON::Ext"; return cache; } } public RubyEncoding getEncoding(ThreadContext context, String name) { synchronized (encodings) { WeakReference encoding = encodings.get(name); if (encoding == null) { Ruby runtime = context.getRuntime(); encoding = new WeakReference((RubyEncoding)RubyEncoding.find(context, runtime.getEncoding(), runtime.newString(name))); encodings.put(name, encoding); } return encoding.get(); } } public GeneratorState getSafeStatePrototype(ThreadContext context) { if (safeStatePrototype == null) { IRubyObject value = jsonModule.get().getConstant("SAFE_STATE_PROTOTYPE"); if (!(value instanceof GeneratorState)) { throw context.getRuntime().newTypeError(value, generatorStateClass.get()); } safeStatePrototype = new WeakReference((GeneratorState)value); } return safeStatePrototype.get(); } }