/*
 * Copyright (C) 2022 parboiled contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.parboiled.transform;

import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.WeakHashMap;

/**
 * Helper that defines classes using {@code MethodHandles.Lookup#defineClass}.
 */
final class LookupFactory {

	private WeakHashMap<Class<?>, Lookup> lookups = new WeakHashMap<>();
	private Lookup trustedLookup;

	LookupFactory() {
		loadTrustedLookup();
	}

	private void loadTrustedLookup() {
		try {
			Class<?> unsafeType = Class.forName("sun.misc.Unsafe");
			Field theUnsafeField = unsafeType.getDeclaredField("theUnsafe");
			theUnsafeField.setAccessible(true);
			Object unsafeInstance = theUnsafeField.get(null);
			Field trustedLookupField = Lookup.class.getDeclaredField("IMPL_LOOKUP");
			Method baseMethod = unsafeType.getMethod("staticFieldBase", Field.class);
			Object trustedLookupBase = baseMethod.invoke(unsafeInstance, trustedLookupField);
			Method offsetMethod = unsafeType.getMethod("staticFieldOffset", Field.class);
			Object trustedLookupOffset = offsetMethod.invoke(unsafeInstance, trustedLookupField);
			Method getObjectMethod = unsafeType.getMethod("getObject", Object.class, long.class);
			this.trustedLookup =
					(Lookup) getObjectMethod.invoke(unsafeInstance, trustedLookupBase, trustedLookupOffset);
		} catch (Exception e) {
			// Unsafe and trusted lookup is not available
		}
	}

	Lookup lookupFor(Class<?> hostClass) {
		Lookup lookup = lookups.get(hostClass);
		if (lookup == null) {
			try {
				// try to find a lookup() method first
				Method lookupMethod = hostClass.getMethod("lookup");
				lookup = (Lookup) lookupMethod.invoke(null);
			} catch (Exception e) {
				// failed to use lookup() method
			}

			if (lookup == null && trustedLookup != null) {
				// use trusted lookup instance if available
				lookup = trustedLookup.in(hostClass);
			}

			if (lookup != null) {
				lookups.put(hostClass, lookup);
			}
		}
		return lookup;
	}
}
@@ -799,70 +799,78 @@
 hat 
-defines classes using %7B@code MethodHandles.Lookup#defineC
+can be used to create %7B@link Lookup%7D instances for%0A * specific c
 lass
-%7D
+es
 .%0A *
@@ -1048,16 +1048,317 @@
 ();%0A%09%7D%0A%0A
+%09/**%0A%09 * Tries to load a trusted %7B@link Lookup%7D instance.%0A%09 *%0A%09 * %3Cp%3E%0A%09 * Adapted from %3Ca href=%22https://github.com/google/guice/blob/cf759d44c78e8490e3d54df6a27918e0811bbdf9/core/src/com/google/inject/internal/aop/HiddenClassDefiner.java#L40%22%3EHiddenClassDefiner%3C/a%3E%0A%09 * of Google Guice.%0A%09 * %3C/p%3E%0A%09 */%0A
 %09private
@@ -2310,16 +2310,541 @@
 %09%09%7D%0A%09%7D%0A%0A
+%09/**%0A%09 * Determines a %7B@link Lookup%7D instance for the given hostClass.%0A%09 * %3Cp%3E%0A%09 * The method first tries to use a static method of the hostClass with the%0A%09 * following signature:%0A%09 * %3C/p%3E%0A%09 * %3Cp%3E%0A%09 * %3Ccode%3E%0A%09 *     public static %7B@link Lookup%7D lookup();%0A%09 * %3C/code%3E%0A%09 * %3C/p%3E%0A%09 * %3Cp%3E%0A%09 * If this fails then it tries to use a trusted lookup%0A%09 * instance created via sun.misc.Unsafe.%0A%09 * %3C/p%3E%0A%09 *%0A%09 * @param hostClass The target class of the lookup instance%0A%09 * @return a lookup instance or %3Ccode%3Enull%3C/code%3E if not found%0A%09 */%0A
 %09Lookup 

Add some documentation to LookupFactory.