--- old/make/hotspot/lib/JvmFeatures.gmk 2020-08-18 21:51:52.742119816 -0700 +++ new/make/hotspot/lib/JvmFeatures.gmk 2020-08-18 21:51:52.626115449 -0700 @@ -121,6 +121,7 @@ dynamicArchive.cpp \ filemap.cpp \ heapShared.cpp \ + lambdaFormInvokers.cpp \ metaspaceShared.cpp \ metaspaceShared_$(HOTSPOT_TARGET_CPU).cpp \ metaspaceShared_$(HOTSPOT_TARGET_CPU_ARCH).cpp \ --- old/make/hotspot/symbols/symbols-unix 2020-08-18 21:51:53.110133669 -0700 +++ new/make/hotspot/symbols/symbols-unix 2020-08-18 21:51:52.998129453 -0700 @@ -158,6 +158,7 @@ JVM_LoadLibrary JVM_LookupDefineClass JVM_LookupLambdaProxyClassFromArchive +JVM_CDSTraceResolve JVM_MaxMemory JVM_MaxObjectInspectionAge JVM_MonitorNotify --- old/src/hotspot/share/classfile/classFileParser.cpp 2020-08-18 21:51:53.474147371 -0700 +++ new/src/hotspot/share/classfile/classFileParser.cpp 2020-08-18 21:51:53.358143004 -0700 @@ -6097,7 +6097,18 @@ // use an illegal char such as ';' because that causes serialization issues // and issues with hidden classes that create their own hidden classes. char addr_buf[20]; - jio_snprintf(addr_buf, 20, INTPTR_FORMAT, p2i(ik)); + if (DumpSharedSpaces) { + // We want stable names for the archived hidden classes (only for static + // archive for now). Spaces under default_SharedBaseAddress() will be + // occupied by the archive at run time, so we know that no dynamically + // loaded InstanceKlass will be placed under there. + static volatile size_t counter = 0; + Atomic::cmpxchg(&counter, (size_t)0, Arguments::default_SharedBaseAddress()); // initialize it + size_t new_id = Atomic::add(&counter, (size_t)1); + jio_snprintf(addr_buf, 20, INTPTR_FORMAT, new_id); + } else { + jio_snprintf(addr_buf, 20, INTPTR_FORMAT, p2i(ik)); + } size_t new_name_len = _class_name->utf8_length() + 2 + strlen(addr_buf); char* new_name = NEW_RESOURCE_ARRAY(char, new_name_len); jio_snprintf(new_name, new_name_len, "%s+%s", --- old/src/hotspot/share/classfile/classListParser.cpp 2020-08-18 21:51:53.878162579 -0700 +++ new/src/hotspot/share/classfile/classListParser.cpp 2020-08-18 21:51:53.766158363 -0700 @@ -33,6 +33,7 @@ #include "classfile/systemDictionaryShared.hpp" #include "logging/log.hpp" #include "logging/logTag.hpp" +#include "memory/lambdaFormInvokers.hpp" #include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" #include "runtime/handles.inline.hpp" @@ -85,6 +86,12 @@ if (*_line == '#') { // comment continue; } + // The line is output TRACE_RESOLVE + if (strncmp(_line, LambdaFormInvokers::lambda_form_invoker_tag(), + strlen(LambdaFormInvokers::lambda_form_invoker_tag())) == 0) { + LambdaFormInvokers::append(os::strdup((const char*)_line, mtInternal)); + continue; + } break; } --- old/src/hotspot/share/classfile/classLoaderData.cpp 2020-08-18 21:51:54.242176281 -0700 +++ new/src/hotspot/share/classfile/classLoaderData.cpp 2020-08-18 21:51:54.126171914 -0700 @@ -968,3 +968,14 @@ } return false; } + +Klass* ClassLoaderData::find_class(Symbol* name) { + Dictionary* dict = dictionary(); + unsigned hash = dict->compute_hash(name); + int index = dict->hash_to_index(hash); + { + // find_class assert on SystemDictionary_lock or safepoint + MutexLocker lock(SystemDictionary_lock); + return dict->find_class(index, hash, name); + } +} --- old/src/hotspot/share/classfile/classLoaderData.hpp 2020-08-18 21:51:54.606189983 -0700 +++ new/src/hotspot/share/classfile/classLoaderData.hpp 2020-08-18 21:51:54.490185616 -0700 @@ -298,6 +298,7 @@ void init_handle_locked(OopHandle& pd, Handle h); // used for concurrent access to ModuleEntry::_pd field void add_class(Klass* k, bool publicize = true); void remove_class(Klass* k); + Klass* find_class(Symbol *name); bool contains_klass(Klass* k); void record_dependency(const Klass* to); PackageEntryTable* packages() { return _packages; } --- old/src/hotspot/share/classfile/klassFactory.hpp 2020-08-18 21:51:54.962203384 -0700 +++ new/src/hotspot/share/classfile/klassFactory.hpp 2020-08-18 21:51:54.850199168 -0700 @@ -36,6 +36,7 @@ class Klass; class Symbol; class TempNewSymbol; +class LambdaFormInvokers; /* * KlassFactory is an interface to implementations of the following mapping/function: @@ -67,6 +68,7 @@ friend class ClassLoader; friend class ClassLoaderExt; friend class SystemDictionary; + friend class LambdaFormInvokers; private: static InstanceKlass* create_from_stream(ClassFileStream* stream, --- old/src/hotspot/share/classfile/systemDictionaryShared.cpp 2020-08-18 21:51:55.322216936 -0700 +++ new/src/hotspot/share/classfile/systemDictionaryShared.cpp 2020-08-18 21:51:55.206212569 -0700 @@ -1142,6 +1142,13 @@ return created; } +void SystemDictionaryShared::add_shared_to_hierarchy(InstanceKlass* k, TRAPS) { + Arguments::assert_is_dumping_archive(); + assert(!k->is_loaded(), "Invariant"); + assert_locked_or_safepoint(Compile_lock); // add_to_hierarchy assert on it. + SystemDictionary::add_to_hierarchy(k, CHECK); +} + // This function is called to resolve the super/interfaces of shared classes for // non-built-in loaders. E.g., ChildClass in the below example // where "super:" (and optionally "interface:") have been specified. @@ -1382,6 +1389,11 @@ _dumptime_table->update_counts(); } +void SystemDictionaryShared::set_excluded(InstanceKlass* k) { + Arguments::assert_is_dumping_archive(); + find_or_allocate_info_for(k)->set_excluded(); +} + bool SystemDictionaryShared::is_excluded_class(InstanceKlass* k) { assert(_no_class_loading_should_happen, "sanity"); Arguments::assert_is_dumping_archive(); --- old/src/hotspot/share/classfile/systemDictionaryShared.hpp 2020-08-18 21:51:55.706231391 -0700 +++ new/src/hotspot/share/classfile/systemDictionaryShared.hpp 2020-08-18 21:51:55.590227024 -0700 @@ -243,6 +243,7 @@ static bool is_sharing_possible(ClassLoaderData* loader_data); static bool add_unregistered_class(InstanceKlass* k, TRAPS); + static void add_shared_to_hierarchy(InstanceKlass* k, TRAPS); static InstanceKlass* dump_time_resolve_super_or_fail(Symbol* child_name, Symbol* class_name, Handle class_loader, @@ -305,6 +306,7 @@ return (k->shared_classpath_index() != UNREGISTERED_INDEX); } static void check_excluded_classes(); + static void set_excluded(InstanceKlass* k); static void validate_before_archiving(InstanceKlass* k); static bool is_excluded_class(InstanceKlass* k); static void dumptime_classes_do(class MetaspaceClosure* it); --- old/src/hotspot/share/classfile/vmSymbols.hpp 2020-08-18 21:51:56.134247502 -0700 +++ new/src/hotspot/share/classfile/vmSymbols.hpp 2020-08-18 21:51:56.018243135 -0700 @@ -287,7 +287,10 @@ template(jdk_internal_vm_annotation_ForceInline_signature, "Ljdk/internal/vm/annotation/ForceInline;") \ template(jdk_internal_vm_annotation_Hidden_signature, "Ljdk/internal/vm/annotation/Hidden;") \ template(jdk_internal_vm_annotation_Stable_signature, "Ljdk/internal/vm/annotation/Stable;") \ - \ + /* used by JLI and CDS */ \ + template(java_lang_invoke_GenerateJLIClassesHelper, "java/lang/invoke/GenerateJLIClassesHelper") \ + template(generateMethodHandleHolderClasses, "generateMethodHandleHolderClasses") \ + template(generateMethodHandleHolderClasses_signature, "([Ljava/lang/String;)[Ljava/lang/Object;") \ /* Support for JSR 292 & invokedynamic (JDK 1.7 and above) */ \ template(java_lang_invoke_CallSite, "java/lang/invoke/CallSite") \ template(java_lang_invoke_ConstantCallSite, "java/lang/invoke/ConstantCallSite") \ --- old/src/hotspot/share/include/jvm.h 2020-08-18 21:51:56.654267076 -0700 +++ new/src/hotspot/share/include/jvm.h 2020-08-18 21:51:56.538262710 -0700 @@ -206,6 +206,9 @@ JNIEXPORT jlong JNICALL JVM_GetRandomSeedForCDSDump(); +JNIEXPORT void JNICALL +JVM_CDSTraceResolve(JNIEnv* env, jclass ignored, jstring line); + /* * java.lang.Throwable */ --- old/src/hotspot/share/memory/metaspaceShared.cpp 2020-08-18 21:51:57.162286199 -0700 +++ new/src/hotspot/share/memory/metaspaceShared.cpp 2020-08-18 21:51:57.046281832 -0700 @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "jvm.h" +#include "classfile/classFileStream.hpp" #include "classfile/classLoaderDataGraph.hpp" #include "classfile/classListParser.hpp" #include "classfile/classLoaderExt.hpp" @@ -44,6 +45,7 @@ #include "memory/archiveUtils.inline.hpp" #include "memory/dynamicArchive.hpp" #include "memory/filemap.hpp" +#include "memory/lambdaFormInvokers.hpp" #include "memory/heapShared.inline.hpp" #include "memory/metaspace.hpp" #include "memory/metaspaceClosure.hpp" @@ -661,6 +663,15 @@ } } } + public: + void add_extra_classes(GrowableArray* klass_list) { + if (klass_list != NULL && klass_list->length() > 0) { + for (int i = 0; i < klass_list->length(); i++) { + Klass* k = klass_list->at(i); + _global_klass_objects->append_if_missing(k); + } + } + } }; // Global object for holding symbols that created during class loading. See SymbolTable::new_symbol @@ -1643,6 +1654,7 @@ _global_klass_objects = new GrowableArray(1000); CollectClassesClosure collect_classes; ClassLoaderDataGraph::loaded_classes_do(&collect_classes); + collect_classes.add_extra_classes(LambdaFormInvokers::replacement_holder_classes()); _global_klass_objects->sort(global_klass_compare); print_class_stats(); @@ -1913,8 +1925,14 @@ if (SharedArchiveConfigFile) { log_info(cds)("Reading extra data from %s ...", SharedArchiveConfigFile); read_extra_data(SharedArchiveConfigFile, THREAD); + log_info(cds)("Reading extra data: done."); + } + + if (LambdaFormInvokers::lambdaform_lines() != NULL) { + log_info(cds)("Regenerate MethodHandle Holder classes..."); + LambdaFormInvokers::regenerate_holder_classes(THREAD); + log_info(cds)("Regenerate MethodHandle Holder classes done."); } - log_info(cds)("Reading extra data: done."); HeapShared::init_subgraph_entry_fields(THREAD); @@ -1935,7 +1953,6 @@ } } - int MetaspaceShared::preload_classes(const char* class_list_path, TRAPS) { ClassListParser parser(class_list_path); int class_count = 0; @@ -2692,8 +2709,3 @@ } st->cr(); } - - - - - --- old/src/hotspot/share/prims/jvm.cpp 2020-08-18 21:51:57.690306074 -0700 +++ new/src/hotspot/share/prims/jvm.cpp 2020-08-18 21:51:57.574301708 -0700 @@ -44,6 +44,7 @@ #include "logging/log.hpp" #include "memory/dynamicArchive.hpp" #include "memory/heapShared.hpp" +#include "memory/lambdaFormInvokers.hpp" #include "memory/oopFactory.hpp" #include "memory/referenceType.hpp" #include "memory/resourceArea.hpp" @@ -3847,6 +3848,17 @@ #endif // INCLUDE_CDS JVM_END +JVM_ENTRY(void, JVM_CDSTraceResolve(JNIEnv* env, jclass ignored, jstring line)) +#if INCLUDE_CDS + if (line != NULL && DumpLoadedClassList != NULL && classlist_file->is_open()) { + ResourceMark rm(THREAD); + Handle h_line (THREAD, JNIHandles::resolve_non_null(line)); + char* c_line = java_lang_String::as_utf8_string(h_line()); + classlist_file->print_cr("%s %s", LambdaFormInvokers::lambda_form_invoker_tag(), c_line); + } +#endif // INCLUDE_CDS +JVM_END + JVM_ENTRY(jboolean, JVM_IsCDSDumpingEnabled(JNIEnv* env)) JVMWrapper("JVM_IsCDSDumpingEnable"); return DynamicDumpSharedSpaces; --- old/src/hotspot/share/runtime/arguments.cpp 2020-08-18 21:51:58.154323541 -0700 +++ new/src/hotspot/share/runtime/arguments.cpp 2020-08-18 21:51:58.038319175 -0700 @@ -4065,7 +4065,13 @@ // Set object alignment values. set_object_alignment(); -#if !INCLUDE_CDS +#if INCLUDE_CDS + if (DumpLoadedClassList != NULL) { + if (!add_property("java.lang.invoke.MethodHandle.CDS_TRACE_RESOLVE=true")) { + return JNI_ENOMEM; + } + } +# else // !INCLUDE_CDS if (DumpSharedSpaces || RequireSharedSpaces) { jio_fprintf(defaultStream::error_stream(), "Shared spaces are not supported in this VM\n"); --- old/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java 2020-08-18 21:51:58.558338749 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java 2020-08-18 21:51:58.442334382 -0700 @@ -453,6 +453,15 @@ return null; // null means don't report a top species } + private void traceResolve(String line) { + if (TRACE_RESOLVE) { + System.out.println(line); + } + if (CDS_TRACE_RESOLVE) { + GenerateJLIClassesHelper.cdsTraceResolve(line); + } + } + /** * Code generation support for instances. * Subclasses can modify the behavior. @@ -475,15 +484,11 @@ Class salvage = null; try { salvage = BootLoader.loadClassOrNull(className); - if (TRACE_RESOLVE && salvage != null) { - // Used by jlink species pregeneration plugin, see - // jdk.tools.jlink.internal.plugins.GenerateJLIClassesPlugin - System.out.println("[SPECIES_RESOLVE] " + className + " (salvaged)"); - } + // Used by jlink species pregeneration plugin, see + // jdk.tools.jlink.internal.plugins.GenerateJLIClassesPlugin + traceResolve("[SPECIES_RESOLVE] " + className + " (salvaged)"); } catch (Error ex) { - if (TRACE_RESOLVE) { - System.out.println("[SPECIES_FRESOLVE] " + className + " (Error) " + ex.getMessage()); - } + traceResolve("[SPECIES_RESOLVE] " + className + " (Error) " + ex.getMessage()); } final Class speciesCode; if (salvage != null) { @@ -494,19 +499,15 @@ // Not pregenerated, generate the class try { speciesCode = generateConcreteSpeciesCode(className, speciesData); - if (TRACE_RESOLVE) { - // Used by jlink species pregeneration plugin, see - // jdk.tools.jlink.internal.plugins.GenerateJLIClassesPlugin - System.out.println("[SPECIES_RESOLVE] " + className + " (generated)"); - } + // Used by jlink species pregeneration plugin, see + // jdk.tools.jlink.internal.plugins.GenerateJLIClassesPlugin + traceResolve("[SPECIES_RESOLVE] " + className + " (generated)"); // This operation causes a lot of churn: linkSpeciesDataToCode(speciesData, speciesCode); // This operation commits the relation, but causes little churn: linkCodeToSpeciesData(speciesCode, speciesData, false); } catch (Error ex) { - if (TRACE_RESOLVE) { - System.out.println("[SPECIES_RESOLVE] " + className + " (Error #2)" ); - } + traceResolve("[SPECIES_RESOLVE] " + className + " (Error #2)" ); // We can get here if there is a race condition loading a class. // Or maybe we are out of resources. Back out of the CHM.get and retry. throw ex; --- old/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java 2020-08-18 21:51:58.926352602 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java 2020-08-18 21:51:58.814348385 -0700 @@ -29,9 +29,15 @@ import jdk.internal.org.objectweb.asm.Opcodes; import sun.invoke.util.Wrapper; +import java.util.Arrays; import java.util.ArrayList; import java.util.HashSet; import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.stream.Stream; import static java.lang.invoke.MethodTypeForm.LF_INVINTERFACE; import static java.lang.invoke.MethodTypeForm.LF_INVVIRTUAL; @@ -41,6 +47,347 @@ * generate classes ahead of time. */ class GenerateJLIClassesHelper { + private static int DMH_INVOKE_VIRTUAL_TYPE = 0; + private static int DMH_INVOKE_INTERFACE_TYPE = 4; + + private static final String DIRECT_HOLDER = "java/lang/invoke/DirectMethodHandle$Holder"; + private static final String DMH_INVOKE_VIRTUAL = "invokeVirtual"; + private static final String DMH_INVOKE_STATIC = "invokeStatic"; + private static final String DMH_INVOKE_SPECIAL = "invokeSpecial"; + private static final String DMH_NEW_INVOKE_SPECIAL = "newInvokeSpecial"; + private static final String DMH_INVOKE_INTERFACE = "invokeInterface"; + private static final String DMH_INVOKE_STATIC_INIT = "invokeStaticInit"; + private static final String DMH_INVOKE_SPECIAL_IFC = "invokeSpecialIFC"; + + private static final String DELEGATING_HOLDER = "java/lang/invoke/DelegatingMethodHandle$Holder"; + private static final String BASIC_FORMS_HOLDER = "java/lang/invoke/LambdaForm$Holder"; + + private static final String INVOKERS_HOLDER_NAME = "java.lang.invoke.Invokers$Holder"; + private static final String INVOKERS_HOLDER_INTERNAL_NAME = INVOKERS_HOLDER_NAME.replace('.', '/'); + + // Map from DirectMethodHandle method type to internal ID, matching values + // of the corresponding constants in java.lang.invoke.MethodTypeForm + private static final Map DMH_METHOD_TYPE_MAP = + Map.of( + DMH_INVOKE_VIRTUAL, DMH_INVOKE_VIRTUAL_TYPE, + DMH_INVOKE_STATIC, 1, + DMH_INVOKE_SPECIAL, 2, + DMH_NEW_INVOKE_SPECIAL, 3, + DMH_INVOKE_INTERFACE, DMH_INVOKE_INTERFACE_TYPE, + DMH_INVOKE_STATIC_INIT, 5, + DMH_INVOKE_SPECIAL_IFC, 20 + ); + + /** + * Output to DumpLoadedClassList, format is simimar to LF_RESOLVE + * @see InvokerBytecodeGenerator + * @param line the line to output. + */ + static native void cdsTraceResolve(String line); + + private static TreeSet getSpeciesTyes() { return speciesTypes; } + private static TreeSet speciesTypes = new TreeSet<>(); + private static TreeSet invokerTypes = new TreeSet<>(); + private static TreeSet callSiteTypes = new TreeSet<>(); + private static Map> dmhMethods = new TreeMap<>(); + + private static void clear() { + speciesTypes.clear(); + invokerTypes.clear(); + callSiteTypes.clear(); + dmhMethods.clear(); + } + + private static void addSpeciesType(String type) { + speciesTypes.add(expandSignature(type)); + } + + private static void addInvokerType(String methodType) { + validateMethodType(methodType); + invokerTypes.add(methodType); + } + + private static void addCallSiteType(String csType) { + validateMethodType(csType); + callSiteTypes.add(csType); + } + + private static void readTraceConfig(Stream lines) { + lines.map(line -> line.split(" ")) + .forEach(parts -> { + switch (parts[0]) { + case "[SPECIES_RESOLVE]": + // Allow for new types of species data classes being resolved here + if (parts.length == 3 && parts[1].startsWith("java.lang.invoke.BoundMethodHandle$Species_")) { + String species = parts[1].substring("java.lang.invoke.BoundMethodHandle$Species_".length()); + if (!"L".equals(species)) { + addSpeciesType(species); + } + } + break; + case "[LF_RESOLVE]": + String methodType = parts[3]; + if (parts[1].equals(INVOKERS_HOLDER_NAME)) { + if ("linkToTargetMethod".equals(parts[2]) || + "linkToCallSite".equals(parts[2])) { + addCallSiteType(methodType); + } else { + addInvokerType(methodType); + } + } else if (parts[1].contains("DirectMethodHandle")) { + String dmh = parts[2]; + // ignore getObject etc for now (generated + // by default) + if (DMH_METHOD_TYPE_MAP.containsKey(dmh)) { + addDMHMethodType(dmh, methodType); + } + } + break; + default: break; // ignore + } + }); + } + + /** + * called from vm to generate MethodHandle holder classes + * @return @code { Object[] } if holder classes can be generated. + * @param lines the output lines from @code { cdsTraceResolve } + */ + static Object[] generateMethodHandleHolderClasses(String[] lines) { + try { + Map result = generateMHHolderClasses(lines); + clear(); + if (result == null) { + return null; + } + int size = result.size(); + Object[] ret_array = new Object[size * 2]; + int index = 0; + for (Map.Entry entry : result.entrySet()) { + ret_array[index++] = entry.getKey(); + ret_array[index++] = entry.getValue(); + }; + return ret_array; + } catch (Exception e) { + return null; + } + } + + /* return a map of */ + static Map generateMHHolderClasses(String[] lines) { + if (lines == null || lines.length == 0) { + return null; + } + readTraceConfig(Arrays.stream(lines)); + int count = 0; + for (Set entry : dmhMethods.values()) { + count += entry.size(); + } + MethodType[] directMethodTypes = new MethodType[count]; + int[] dmhTypes = new int[count]; + int index = 0; + for (Map.Entry> entry : dmhMethods.entrySet()) { + String dmhType = entry.getKey(); + for (String type : entry.getValue()) { + // The DMH type to actually ask for is retrieved by removing + // the first argument, which needs to be of Object.class + MethodType mt = asMethodType(type); + if (mt.parameterCount() < 1 || + mt.parameterType(0) != Object.class) { + throw new RuntimeException( + "DMH type parameter must start with L: " + dmhType + " " + type); + } + + // Adapt the method type of the LF to retrieve + directMethodTypes[index] = mt.dropParameterTypes(0, 1); + + // invokeVirtual and invokeInterface must have a leading Object + // parameter, i.e., the receiver + dmhTypes[index] = DMH_METHOD_TYPE_MAP.get(dmhType); + if (dmhTypes[index] == DMH_INVOKE_INTERFACE_TYPE || + dmhTypes[index] == DMH_INVOKE_VIRTUAL_TYPE) { + if (mt.parameterCount() < 2 || + mt.parameterType(1) != Object.class) { + throw new RuntimeException( + "DMH type parameter must start with LL: " + dmhType + " " + type); + } + } + index++; + } + } + + // The invoker type to ask for is retrieved by removing the first + // and the last argument, which needs to be of Object.class + MethodType[] invokerMethodTypes = new MethodType[invokerTypes.size()]; + index = 0; + for (String invokerType : invokerTypes) { + MethodType mt = asMethodType(invokerType); + final int lastParam = mt.parameterCount() - 1; + if (mt.parameterCount() < 2 || + mt.parameterType(0) != Object.class || + mt.parameterType(lastParam) != Object.class) { + throw new RuntimeException( + "Invoker type parameter must start and end with Object: " + invokerType); + } + mt = mt.dropParameterTypes(lastParam, lastParam + 1); + invokerMethodTypes[index] = mt.dropParameterTypes(0, 1); + index++; + } + + // The callSite type to ask for is retrieved by removing the last + // argument, which needs to be of Object.class + MethodType[] callSiteMethodTypes = new MethodType[callSiteTypes.size()]; + index = 0; + for (String callSiteType : callSiteTypes) { + MethodType mt = asMethodType(callSiteType); + final int lastParam = mt.parameterCount() - 1; + if (mt.parameterCount() < 1 || + mt.parameterType(lastParam) != Object.class) { + throw new RuntimeException( + "CallSite type parameter must end with Object: " + callSiteType); + } + callSiteMethodTypes[index] = mt.dropParameterTypes(lastParam, lastParam + 1); + index++; + } + Map result = new HashMap(); + + byte[] res = generateDirectMethodHandleHolderClassBytes( + DIRECT_HOLDER, directMethodTypes, dmhTypes); + result.put(DIRECT_METHOD_HOLDER_ENTRY, res); + + res = generateDelegatingMethodHandleHolderClassBytes( + DELEGATING_HOLDER, directMethodTypes); + result.put(DELEGATING_METHOD_HOLDER_ENTRY, res); + + res = generateInvokersHolderClassBytes(INVOKERS_HOLDER_INTERNAL_NAME, + invokerMethodTypes, callSiteMethodTypes); + result.put(INVOKERS_HOLDER_ENTRY, res); + + res = generateBasicFormsClassBytes(BASIC_FORMS_HOLDER); + result.put(BASIC_FORMS_HOLDER_ENTRY, res); + + speciesTypes.forEach(types -> { + Map.Entry entry = generateConcreteBMHClassBytes(types); + String className = entry.getKey(); + String key = "/java.base/" + className + ".class"; + byte[] value = entry.getValue(); + result.put(key, value); + }); + + return result; + } + + private static final String DIRECT_METHOD_HOLDER_ENTRY = + "/java.base/" + DIRECT_HOLDER + ".class"; + private static final String DELEGATING_METHOD_HOLDER_ENTRY = + "/java.base/" + DELEGATING_HOLDER + ".class"; + private static final String BASIC_FORMS_HOLDER_ENTRY = + "/java.base/" + BASIC_FORMS_HOLDER + ".class"; + private static final String INVOKERS_HOLDER_ENTRY = + "/java.base/" + INVOKERS_HOLDER_INTERNAL_NAME + ".class"; + + private static MethodType asMethodType(String basicSignatureString) { + String[] parts = basicSignatureString.split("_"); + assert(parts.length == 2); + assert(parts[1].length() == 1); + String parameters = expandSignature(parts[0]); + Class rtype = simpleType(parts[1].charAt(0)); + if (parameters.isEmpty()) { + return MethodType.methodType(rtype); + } else { + Class[] ptypes = new Class[parameters.length()]; + for (int i = 0; i < ptypes.length; i++) { + ptypes[i] = simpleType(parameters.charAt(i)); + } + return MethodType.methodType(rtype, ptypes); + } + } + + private static void addDMHMethodType(String dmh, String methodType) { + validateMethodType(methodType); + Set methodTypes = dmhMethods.get(dmh); + if (methodTypes == null) { + methodTypes = new TreeSet<>(); + dmhMethods.put(dmh, methodTypes); + } + methodTypes.add(methodType); + } + + private static void validateMethodType(String type) { + String[] typeParts = type.split("_"); + // check return type (second part) + if (typeParts.length != 2 || typeParts[1].length() != 1 + || "LJIFDV".indexOf(typeParts[1].charAt(0)) == -1) { + throw new RuntimeException( + "Method type signature must be of form [LJIFD]*_[LJIFDV]"); + } + // expand and check arguments (first part) + expandSignature(typeParts[0]); + } + + // Convert LL -> LL, L3 -> LLL + private static String expandSignature(String signature) { + StringBuilder sb = new StringBuilder(); + char last = 'X'; + int count = 0; + for (int i = 0; i < signature.length(); i++) { + char c = signature.charAt(i); + if (c >= '0' && c <= '9') { + count *= 10; + count += (c - '0'); + } else { + requireBasicType(c); + for (int j = 1; j < count; j++) { + sb.append(last); + } + sb.append(c); + last = c; + count = 0; + } + } + + // ended with a number, e.g., "L2": append last char count - 1 times + if (count > 1) { + requireBasicType(last); + for (int j = 1; j < count; j++) { + sb.append(last); + } + } + return sb.toString(); + } + + private static void requireBasicType(char c) { + if ("LIJFD".indexOf(c) < 0) { + throw new RuntimeException( + "Character " + c + " must correspond to a basic field type: LIJFD"); + } + } + + private static Class simpleType(char c) { + switch (c) { + case 'F': + return float.class; + case 'D': + return double.class; + case 'I': + return int.class; + case 'L': + return Object.class; + case 'J': + return long.class; + case 'V': + return void.class; + case 'Z': + case 'B': + case 'S': + case 'C': + throw new IllegalArgumentException("Not a valid primitive: " + c + + " (use I instead)"); + default: + throw new IllegalArgumentException("Not a primitive: " + c); + } + } + static byte[] generateBasicFormsClassBytes(String className) { ArrayList forms = new ArrayList<>(); --- old/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java 2020-08-18 21:51:59.290366304 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java 2020-08-18 21:51:59.178362088 -0700 @@ -694,9 +694,15 @@ private static MemberName resolveFrom(String name, MethodType type, Class holder) { MemberName member = new MemberName(holder, name, type, REF_invokeStatic); MemberName resolvedMember = MemberName.getFactory().resolveOrNull(REF_invokeStatic, member, holder); - if (TRACE_RESOLVE) { - System.out.println("[LF_RESOLVE] " + holder.getName() + " " + name + " " + - shortenSignature(basicTypeSignature(type)) + (resolvedMember != null ? " (success)" : " (fail)") ); + if (TRACE_RESOLVE || CDS_TRACE_RESOLVE) { + String line = "[LF_RESOLVE] " + holder.getName() + " " + name + " " + + shortenSignature(basicTypeSignature(type)) + (resolvedMember != null ? " (success)" : " (fail)"); + if (TRACE_RESOLVE) { + System.out.println(line); + } + if (CDS_TRACE_RESOLVE) { + GenerateJLIClassesHelper.cdsTraceResolve(line); + } } return resolvedMember; } --- old/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java 2020-08-18 21:51:59.666380458 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java 2020-08-18 21:51:59.550376091 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2020, 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 @@ -1802,6 +1802,11 @@ } @Override + public Map generateMethodHandleHolderClasses(String... lines) { + return GenerateJLIClassesHelper.generateMHHolderClasses(lines); + } + + @Override public VarHandle memoryAccessVarHandle(Class carrier, long alignmentMask, ByteOrder order, long offset, long[] strides) { return VarHandles.makeMemoryAddressViewHandle(carrier, alignmentMask, order, offset, strides); --- old/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java 2020-08-18 21:52:00.050394912 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java 2020-08-18 21:51:59.934390546 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2020, 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,6 +48,7 @@ static final boolean TRACE_INTERPRETER; static final boolean TRACE_METHOD_LINKAGE; static final boolean TRACE_RESOLVE; + static final boolean CDS_TRACE_RESOLVE; static final int COMPILE_THRESHOLD; static final boolean LOG_LF_COMPILATION_FAILURE; static final int DONT_INLINE_THRESHOLD; @@ -70,6 +71,8 @@ props.getProperty("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE")); TRACE_RESOLVE = Boolean.parseBoolean( props.getProperty("java.lang.invoke.MethodHandle.TRACE_RESOLVE")); + CDS_TRACE_RESOLVE = Boolean.parseBoolean( + props.getProperty("java.lang.invoke.MethodHandle.CDS_TRACE_RESOLVE")); COMPILE_THRESHOLD = Integer.parseInt( props.getProperty("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD", "0")); LOG_LF_COMPILATION_FAILURE = Boolean.parseBoolean( --- old/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java 2020-08-18 21:52:00.414408614 -0700 +++ new/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java 2020-08-18 21:52:00.302404399 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, 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 @@ -111,6 +111,11 @@ MethodType[] callSiteMethodTypes); /** + * Returns a {@code Map} represent holder class vs class bytes + * @param lines input as output from TRACE_RESOLVE + */ + Map generateMethodHandleHolderClasses(String... lines); + /** * Returns a var handle view of a given memory address. * Used by {@code jdk.internal.foreign.LayoutPath} and * {@code jdk.incubator.foreign.MemoryHandles}. --- old/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java 2020-08-18 21:52:00.806423371 -0700 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java 2020-08-18 21:52:00.682418703 -0700 @@ -29,14 +29,11 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.lang.invoke.MethodType; import java.nio.file.Files; import java.util.EnumSet; +import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.stream.Stream; import jdk.internal.access.JavaLangInvokeAccess; import jdk.internal.access.SharedSecrets; @@ -72,33 +69,10 @@ private static final String DEFAULT_TRACE_FILE = "default_jli_trace.txt"; - private static final String DIRECT_HOLDER = "java/lang/invoke/DirectMethodHandle$Holder"; - private static final String DMH_INVOKE_VIRTUAL = "invokeVirtual"; - private static final String DMH_INVOKE_STATIC = "invokeStatic"; - private static final String DMH_INVOKE_SPECIAL = "invokeSpecial"; - private static final String DMH_NEW_INVOKE_SPECIAL = "newInvokeSpecial"; - private static final String DMH_INVOKE_INTERFACE = "invokeInterface"; - private static final String DMH_INVOKE_STATIC_INIT = "invokeStaticInit"; - private static final String DMH_INVOKE_SPECIAL_IFC = "invokeSpecialIFC"; - - private static final String DELEGATING_HOLDER = "java/lang/invoke/DelegatingMethodHandle$Holder"; - private static final String BASIC_FORMS_HOLDER = "java/lang/invoke/LambdaForm$Holder"; - - private static final String INVOKERS_HOLDER_NAME = "java.lang.invoke.Invokers$Holder"; - private static final String INVOKERS_HOLDER_INTERNAL_NAME = INVOKERS_HOLDER_NAME.replace('.', '/'); - - private static final JavaLangInvokeAccess JLIA - = SharedSecrets.getJavaLangInvokeAccess(); - - private final TreeSet speciesTypes = new TreeSet<>(); - - private final TreeSet invokerTypes = new TreeSet<>(); - - private final TreeSet callSiteTypes = new TreeSet<>(); - - private final Map> dmhMethods = new TreeMap<>(); + private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess(); String mainArgument; + String[] lines = null; public GenerateJLIClassesPlugin() { } @@ -128,135 +102,21 @@ return PluginsResourceBundle.getArgument(NAME); } - private static int DMH_INVOKE_VIRTUAL_TYPE = 0; - private static int DMH_INVOKE_INTERFACE_TYPE = 4; - - // Map from DirectMethodHandle method type to internal ID, matching values - // of the corresponding constants in java.lang.invoke.MethodTypeForm - private static final Map DMH_METHOD_TYPE_MAP = - Map.of( - DMH_INVOKE_VIRTUAL, DMH_INVOKE_VIRTUAL_TYPE, - DMH_INVOKE_STATIC, 1, - DMH_INVOKE_SPECIAL, 2, - DMH_NEW_INVOKE_SPECIAL, 3, - DMH_INVOKE_INTERFACE, DMH_INVOKE_INTERFACE_TYPE, - DMH_INVOKE_STATIC_INIT, 5, - DMH_INVOKE_SPECIAL_IFC, 20 - ); - @Override public void configure(Map config) { mainArgument = config.get(NAME); } - private void addSpeciesType(String type) { - speciesTypes.add(expandSignature(type)); - } - - private void addInvokerType(String methodType) { - validateMethodType(methodType); - invokerTypes.add(methodType); - } - - private void addCallSiteType(String csType) { - validateMethodType(csType); - callSiteTypes.add(csType); - } - - public void initialize(ResourcePool in) { - // Load configuration from the contents in the supplied input file - // - if none was supplied we look for the default file - if (mainArgument == null || !mainArgument.startsWith("@")) { - try (InputStream traceFile = - this.getClass().getResourceAsStream(DEFAULT_TRACE_FILE)) { - if (traceFile != null) { - readTraceConfig( - new BufferedReader( - new InputStreamReader(traceFile)).lines()); - } - } catch (Exception e) { - throw new PluginException("Couldn't read " + DEFAULT_TRACE_FILE, e); - } - } else { - File file = new File(mainArgument.substring(1)); - if (file.exists()) { - readTraceConfig(fileLines(file)); - } - } - } - - private void readTraceConfig(Stream lines) { - lines.map(line -> line.split(" ")) - .forEach(parts -> { - switch (parts[0]) { - case "[SPECIES_RESOLVE]": - // Allow for new types of species data classes being resolved here - if (parts.length == 3 && parts[1].startsWith("java.lang.invoke.BoundMethodHandle$Species_")) { - String species = parts[1].substring("java.lang.invoke.BoundMethodHandle$Species_".length()); - if (!"L".equals(species)) { - addSpeciesType(species); - } - } - break; - case "[LF_RESOLVE]": - String methodType = parts[3]; - if (parts[1].equals(INVOKERS_HOLDER_NAME)) { - if ("linkToTargetMethod".equals(parts[2]) || - "linkToCallSite".equals(parts[2])) { - addCallSiteType(methodType); - } else { - addInvokerType(methodType); - } - } else if (parts[1].contains("DirectMethodHandle")) { - String dmh = parts[2]; - // ignore getObject etc for now (generated - // by default) - if (DMH_METHOD_TYPE_MAP.containsKey(dmh)) { - addDMHMethodType(dmh, methodType); - } - } - break; - default: break; // ignore - } - }); - } - - private void addDMHMethodType(String dmh, String methodType) { - validateMethodType(methodType); - Set methodTypes = dmhMethods.get(dmh); - if (methodTypes == null) { - methodTypes = new TreeSet<>(); - dmhMethods.put(dmh, methodTypes); - } - methodTypes.add(methodType); - } - - private Stream fileLines(File file) { - try { - return Files.lines(file.toPath()); - } catch (IOException io) { - throw new PluginException("Couldn't read file"); - } - } - - private void validateMethodType(String type) { - String[] typeParts = type.split("_"); - // check return type (second part) - if (typeParts.length != 2 || typeParts[1].length() != 1 - || "LJIFDV".indexOf(typeParts[1].charAt(0)) == -1) { - throw new PluginException( - "Method type signature must be of form [LJIFD]*_[LJIFDV]"); - } - // expand and check arguments (first part) - expandSignature(typeParts[0]); - } - - private static void requireBasicType(char c) { - if ("LIJFD".indexOf(c) < 0) { - throw new PluginException( - "Character " + c + " must correspond to a basic field type: LIJFD"); - } - } + // Repeat def, we do not have access right to InvokerBytecodeGeneratorHelper + // Must be exact same! + private static final String DIRECT_METHOD_HOLDER_ENTRY = + "/java.base/java/lang/invoke/DirectMethodHandle$Holder.class"; + private static final String DELEGATING_METHOD_HOLDER_ENTRY = + "/java.base/java/lang/invoke/DelegatingMethodHandle$Holder.class"; + private static final String INVOKERS_HOLDER_ENTRY = + "/java.base/java/lang/invoke/Invokers$Holder.class"; + private static final String BASIC_FORMS_HOLDER_ENTRY = + "/java.base/java/lang/invoke/LambdaForm$Holder.class"; @Override public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { @@ -275,213 +135,49 @@ } }, out); - // Generate BMH Species classes - speciesTypes.forEach(types -> generateBMHClass(types, out)); - // Generate LambdaForm Holder classes generateHolderClasses(out); - - // Let it go - speciesTypes.clear(); - invokerTypes.clear(); - callSiteTypes.clear(); - dmhMethods.clear(); - + // clear input. + lines = null; return out.build(); } - private void generateBMHClass(String types, ResourcePoolBuilder out) { - try { - // Generate class - Map.Entry result = - JLIA.generateConcreteBMHClassBytes(types); - String className = result.getKey(); - byte[] bytes = result.getValue(); - - // Add class to pool - ResourcePoolEntry ndata = ResourcePoolEntry.create( - "/java.base/" + className + ".class", - bytes); - out.add(ndata); - } catch (Exception ex) { - throw new PluginException(ex); - } - } - private void generateHolderClasses(ResourcePoolBuilder out) { - int count = 0; - for (Set entry : dmhMethods.values()) { - count += entry.size(); - } - MethodType[] directMethodTypes = new MethodType[count]; - int[] dmhTypes = new int[count]; - int index = 0; - for (Map.Entry> entry : dmhMethods.entrySet()) { - String dmhType = entry.getKey(); - for (String type : entry.getValue()) { - // The DMH type to actually ask for is retrieved by removing - // the first argument, which needs to be of Object.class - MethodType mt = asMethodType(type); - if (mt.parameterCount() < 1 || - mt.parameterType(0) != Object.class) { - throw new PluginException( - "DMH type parameter must start with L: " + dmhType + " " + type); - } - - // Adapt the method type of the LF to retrieve - directMethodTypes[index] = mt.dropParameterTypes(0, 1); - - // invokeVirtual and invokeInterface must have a leading Object - // parameter, i.e., the receiver - dmhTypes[index] = DMH_METHOD_TYPE_MAP.get(dmhType); - if (dmhTypes[index] == DMH_INVOKE_INTERFACE_TYPE || - dmhTypes[index] == DMH_INVOKE_VIRTUAL_TYPE) { - if (mt.parameterCount() < 2 || - mt.parameterType(1) != Object.class) { - throw new PluginException( - "DMH type parameter must start with LL: " + dmhType + " " + type); - } - } - index++; - } - } - - // The invoker type to ask for is retrieved by removing the first - // and the last argument, which needs to be of Object.class - MethodType[] invokerMethodTypes = new MethodType[this.invokerTypes.size()]; - int i = 0; - for (String invokerType : invokerTypes) { - MethodType mt = asMethodType(invokerType); - final int lastParam = mt.parameterCount() - 1; - if (mt.parameterCount() < 2 || - mt.parameterType(0) != Object.class || - mt.parameterType(lastParam) != Object.class) { - throw new PluginException( - "Invoker type parameter must start and end with Object: " + invokerType); - } - mt = mt.dropParameterTypes(lastParam, lastParam + 1); - invokerMethodTypes[i] = mt.dropParameterTypes(0, 1); - i++; - } - - // The callSite type to ask for is retrieved by removing the last - // argument, which needs to be of Object.class - MethodType[] callSiteMethodTypes = new MethodType[this.callSiteTypes.size()]; - i = 0; - for (String callSiteType : callSiteTypes) { - MethodType mt = asMethodType(callSiteType); - final int lastParam = mt.parameterCount() - 1; - if (mt.parameterCount() < 1 || - mt.parameterType(lastParam) != Object.class) { - throw new PluginException( - "CallSite type parameter must end with Object: " + callSiteType); - } - callSiteMethodTypes[i] = mt.dropParameterTypes(lastParam, lastParam + 1); - i++; - } try { - byte[] bytes = JLIA.generateDirectMethodHandleHolderClassBytes( - DIRECT_HOLDER, directMethodTypes, dmhTypes); - ResourcePoolEntry ndata = ResourcePoolEntry - .create(DIRECT_METHOD_HOLDER_ENTRY, bytes); - out.add(ndata); - - bytes = JLIA.generateDelegatingMethodHandleHolderClassBytes( - DELEGATING_HOLDER, directMethodTypes); - ndata = ResourcePoolEntry.create(DELEGATING_METHOD_HOLDER_ENTRY, bytes); - out.add(ndata); - - bytes = JLIA.generateInvokersHolderClassBytes(INVOKERS_HOLDER_INTERNAL_NAME, - invokerMethodTypes, callSiteMethodTypes); - ndata = ResourcePoolEntry.create(INVOKERS_HOLDER_ENTRY, bytes); - out.add(ndata); - - bytes = JLIA.generateBasicFormsClassBytes(BASIC_FORMS_HOLDER); - ndata = ResourcePoolEntry.create(BASIC_FORMS_HOLDER_ENTRY, bytes); - out.add(ndata); + Map result = JLIA.generateMethodHandleHolderClasses(lines); + if (result != null) { + result.forEach ((k,v) -> { + ResourcePoolEntry ndata = ResourcePoolEntry.create(k, v); + out.add(ndata); + }); + } } catch (Exception ex) { throw new PluginException(ex); } } - private static final String DIRECT_METHOD_HOLDER_ENTRY = - "/java.base/" + DIRECT_HOLDER + ".class"; - private static final String DELEGATING_METHOD_HOLDER_ENTRY = - "/java.base/" + DELEGATING_HOLDER + ".class"; - private static final String BASIC_FORMS_HOLDER_ENTRY = - "/java.base/" + BASIC_FORMS_HOLDER + ".class"; - private static final String INVOKERS_HOLDER_ENTRY = - "/java.base/" + INVOKERS_HOLDER_INTERNAL_NAME + ".class"; - // Convert LL -> LL, L3 -> LLL - public static String expandSignature(String signature) { - StringBuilder sb = new StringBuilder(); - char last = 'X'; - int count = 0; - for (int i = 0; i < signature.length(); i++) { - char c = signature.charAt(i); - if (c >= '0' && c <= '9') { - count *= 10; - count += (c - '0'); - } else { - requireBasicType(c); - for (int j = 1; j < count; j++) { - sb.append(last); + public void initialize(ResourcePool in) { + // Load configuration from the contents in the supplied input file + // - if none was supplied we look for the default file + if (mainArgument == null || !mainArgument.startsWith("@")) { + try (InputStream traceFile = + this.getClass().getResourceAsStream(DEFAULT_TRACE_FILE)) { + if (traceFile != null) { + lines = new BufferedReader( + new InputStreamReader(traceFile)).lines().toArray(String[]::new); } - sb.append(c); - last = c; - count = 0; - } - } - - // ended with a number, e.g., "L2": append last char count - 1 times - if (count > 1) { - requireBasicType(last); - for (int j = 1; j < count; j++) { - sb.append(last); + } catch (Exception e) { + throw new PluginException("Couldn't read " + DEFAULT_TRACE_FILE, e); } - } - return sb.toString(); - } - - private static MethodType asMethodType(String basicSignatureString) { - String[] parts = basicSignatureString.split("_"); - assert(parts.length == 2); - assert(parts[1].length() == 1); - String parameters = expandSignature(parts[0]); - Class rtype = simpleType(parts[1].charAt(0)); - if (parameters.isEmpty()) { - return MethodType.methodType(rtype); } else { - Class[] ptypes = new Class[parameters.length()]; - for (int i = 0; i < ptypes.length; i++) { - ptypes[i] = simpleType(parameters.charAt(i)); + File file = new File(mainArgument.substring(1)); + try { + if (file.exists()) { + lines = Files.readAllLines(file.toPath()).toArray(new String[0]); + } + } catch (Exception e) { + throw new PluginException("Couldn't read " + file.getName(), e); } - return MethodType.methodType(rtype, ptypes); - } - } - - private static Class simpleType(char c) { - switch (c) { - case 'F': - return float.class; - case 'D': - return double.class; - case 'I': - return int.class; - case 'L': - return Object.class; - case 'J': - return long.class; - case 'V': - return void.class; - case 'Z': - case 'B': - case 'S': - case 'C': - throw new IllegalArgumentException("Not a valid primitive: " + c + - " (use I instead)"); - default: - throw new IllegalArgumentException("Not a primitive: " + c); } } } --- old/test/hotspot/jtreg/runtime/cds/DumpSymbolAndStringTable.java 2020-08-18 21:52:01.190437826 -0700 +++ new/test/hotspot/jtreg/runtime/cds/DumpSymbolAndStringTable.java 2020-08-18 21:52:01.066433158 -0700 @@ -34,6 +34,7 @@ import jdk.test.lib.JDKToolFinder; public class DumpSymbolAndStringTable { + private static final String my_string = "DumpSymbolAndStringTable"; public static void main(String[] args) throws Exception { // Grab my own PID String pid = Long.toString(ProcessTools.getProcessId()); @@ -50,7 +51,7 @@ pb.command(new String[] {JDKToolFinder.getJDKTool("jcmd"), pid, "VM.stringtable", "-verbose"}); output = CDSTestUtils.executeAndLog(pb, "jcmd-stringtable"); try { - output.shouldContain("16: java.lang.String\n"); + output.shouldContain("24: DumpSymbolAndStringTable"); } catch (RuntimeException e) { output.shouldContain("Unknown diagnostic command"); } --- /dev/null 2019-11-19 22:05:02.069813242 -0800 +++ new/src/hotspot/share/memory/lambdaFormInvokers.cpp 2020-08-18 21:52:01.434447010 -0700 @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2012, 2020, 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. + * + */ + +#include "precompiled.hpp" +#include "classfile/classFileStream.hpp" +#include "classfile/dictionary.hpp" +#include "classfile/javaClasses.inline.hpp" +#include "classfile/klassFactory.hpp" +#include "classfile/systemDictionaryShared.hpp" +#include "classfile/vmSymbols.hpp" +#include "memory/lambdaFormInvokers.hpp" +#include "memory/allocation.hpp" +#include "logging/log.hpp" +#include "memory/oopFactory.hpp" +#include "memory/metaspaceShared.hpp" +#include "memory/resourceArea.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/klass.hpp" +#include "oops/objArrayKlass.hpp" +#include "oops/objArrayOop.hpp" +#include "oops/oop.inline.hpp" +#include "oops/typeArrayKlass.hpp" +#include "oops/typeArrayOop.inline.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/javaCalls.hpp" +#include "runtime/os.hpp" +#include "runtime/signature.hpp" +#include "utilities/exceptions.hpp" + +GrowableArray* LambdaFormInvokers::_lambdaform_lines = NULL; +GrowableArray* LambdaFormInvokers::_replacement_holder_classes = NULL; + +void LambdaFormInvokers::append(char* line) { + if (_lambdaform_lines == NULL) { + _lambdaform_lines = new GrowableArray(100); + _replacement_holder_classes = new (ResourceObj::C_HEAP, mtClassShared) GrowableArray(12, mtClassShared); + } + _lambdaform_lines->append(line); +} + +void LambdaFormInvokers::regenerate_holder_classes(TRAPS) { + assert(_lambdaform_lines != NULL, "Bad List"); + ResourceMark rm(THREAD); + + Symbol* helper_name = vmSymbols::java_lang_invoke_GenerateJLIClassesHelper(); + Klass* helper_klass = SystemDictionary::resolve_or_null(helper_name, THREAD); + guarantee(helper_klass != NULL, "java/lang/invoke/GenerateJLIClassesHelper exist!"); + + int len = _lambdaform_lines->length(); + objArrayHandle list_lines = oopFactory::new_objArray_handle(SystemDictionary::String_klass(), len, CHECK); + for (int i = 0; i < len; i++) { + char* record = _lambdaform_lines->at(i); + record += strlen(lambda_form_invoker_tag()) + 1; // @lambda_form_invoker [...] + Handle h_line = java_lang_String::create_from_str(record, CHECK); + list_lines->obj_at_put(i, h_line()); + } + + // + // Object[] InvokerBytecodeGeneratorHelper.generateMethodHandleHolderClasses(String[] lines) + // the returned Object[] layout: + // name, byte[], name, byte[] .... + Symbol* method = vmSymbols::generateMethodHandleHolderClasses(); + Symbol* signrs = vmSymbols::generateMethodHandleHolderClasses_signature(); + + JavaValue result(T_OBJECT); + JavaCalls::call_static(&result, helper_klass, method, signrs, list_lines, THREAD); + objArrayHandle h_array(THREAD, (objArrayOop)result.get_jobject()); + if (!HAS_PENDING_EXCEPTION) { + if (h_array() == NULL) { + log_info(cds)("Failed call to %s.%s", helper_name->as_C_string(), method->as_C_string()); + return; + } + } else { + log_info(cds)("Exception happened: %s", PENDING_EXCEPTION->klass()->name()->as_C_string()); + CLEAR_PENDING_EXCEPTION; + return; + } + + int sz = h_array->length(); + assert(sz % 2 == 0 && sz >= 2, "Must be even size of length"); + for (int i = 0; i < sz; i+= 2) { + Handle h_name(THREAD, h_array->obj_at(i)); + typeArrayHandle h_bytes(THREAD, (typeArrayOop)h_array->obj_at(i+1)); + assert(h_name != NULL, "Class name is NULL"); + assert(h_bytes != NULL, "Class bytes is NULL"); + reload_class(h_name, h_bytes, THREAD); + } + +} + +// the format maybe of "/java.base/package/class_name.class" +char* get_full_class_name(char* path_name) { + char* end = strstr(path_name, ".class"); + if (end == NULL) { + end = path_name + strlen(path_name); + } + char* start = strstr(path_name, "/java.base/"); + if (start == NULL) { + start = path_name; + } else { + start = path_name + strlen("/java.base/"); + } + assert(start < end, "Sanity check"); + size_t size = end - start + 1; + + char* full_name = (char*)os::malloc(size, mtInternal); + size_t i = 0; + while (i < size) { + full_name[i++] = *start++; + } + full_name[size - 1] = '\0'; + return full_name; +} + +// class_handle - the class full name, bytes_handle - the class bytes +void LambdaFormInvokers::reload_class(Handle name_handle, typeArrayHandle bytes_handle, TRAPS) { + char* path_name = java_lang_String::as_utf8_string(name_handle()); + char* class_name = get_full_class_name(path_name); + Symbol* sym = SymbolTable::probe((const char*)class_name, (int)strlen(class_name)); + assert(sym != NULL, "The class should be loaded already"); + // the class must exist + Klass* klass = SystemDictionary::resolve_or_null(sym, THREAD); + if (klass == NULL) { + log_info(cds)("Class %s not present, skip", class_name); + return; + } + + int len = bytes_handle->length(); + // make a copy of class bytes so GC will not affect us. + char *buf = resource_allocate_bytes(THREAD, len); + memcpy(buf, (char*)bytes_handle->byte_at_addr(0), len); + ClassFileStream st((u1*)buf, len, NULL, ClassFileStream::verify); + ClassLoaderData* cld = ClassLoaderData::the_null_class_loader_data(); + Handle protection_domain; + ClassLoadInfo cl_info(protection_domain); + + Klass* result = KlassFactory::create_from_stream(&st, + sym, + cld, + cl_info, + CHECK); + + // free buf + resource_free_bytes(buf, len); + if (HAS_PENDING_EXCEPTION) { + log_info(cds)("Exception happened: %s", PENDING_EXCEPTION->klass()->name()->as_C_string()); + log_info(cds)("Could not create InstanceKlass for class %s", class_name); + CLEAR_PENDING_EXCEPTION; + return; + } + + // add to hierarchy and set state to loaded. + { + MutexLocker mu_r(THREAD, Compile_lock); // add_shared_to_hierarchy asserts this. + SystemDictionaryShared::add_shared_to_hierarchy(InstanceKlass::cast(result), THREAD); + } + // new class not linked yet. + MetaspaceShared::try_link_class(InstanceKlass::cast(result), THREAD); + assert(!HAS_PENDING_EXCEPTION, "Invariant"); + + assert(_replacement_holder_classes != NULL, "Should be created already"); + _replacement_holder_classes->append(result); + + // exclude the existing class from dump list + Klass* old = cld->find_class(sym); + assert(old->is_instance_klass(), "Invariant"); + SystemDictionaryShared::set_excluded(InstanceKlass::cast(old)); + log_info(cds)("Replace class %s, old: %p new: %p", class_name, old, result); +} --- /dev/null 2019-11-19 22:05:02.069813242 -0800 +++ new/src/hotspot/share/memory/lambdaFormInvokers.hpp 2020-08-18 21:52:01.810461165 -0700 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020, 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. + * + */ + +#ifndef SHARE_MEMORY_LAMBDAFORMINVOKERS_HPP +#define SHARE_MEMORY_LAMBDAFORMINVOKERS_HPP + +class LambdaFormInvokers : public AllStatic { + public: + static GrowableArray* _lambdaform_lines; + static GrowableArray* _replacement_holder_classes; + + static void append(char* line); + static void regenerate_holder_classes(TRAPS); + static void reload_class(Handle name, typeArrayHandle bytes, TRAPS); + + static GrowableArray* replacement_holder_classes() { + return _replacement_holder_classes; + } + static GrowableArray* lambdaform_lines() { + return _lambdaform_lines; + } + + static const char* lambda_form_invoker_tag() { + return "@lambda-form-invoker"; + } +}; +#endif // SHARE_MEMORY_LAMBDAFORMINVOKERS_HPP --- /dev/null 2019-11-19 22:05:02.069813242 -0800 +++ new/src/java.base/share/native/libjava/GenerateJLIClassesHelper.c 2020-08-18 21:52:02.194475620 -0700 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#include "jni.h" +#include "jni_util.h" +#include "jlong.h" +#include "jvm.h" + +#include "java_lang_invoke_GenerateJLIClassesHelper.h" + +JNIEXPORT void JNICALL +Java_java_lang_invoke_GenerateJLIClassesHelper_cdsTraceResolve(JNIEnv *env, jclass ignore, jstring line) { + JVM_CDSTraceResolve(env, ignore, line); +}