--- old/make/hotspot/symbols/symbols-unix 2020-08-05 15:53:46.326085283 -0700 +++ new/make/hotspot/symbols/symbols-unix 2020-08-05 15:53:45.974072033 -0700 @@ -158,6 +158,7 @@ JVM_LoadLibrary JVM_LookupDefineClass JVM_LookupLambdaProxyClassFromArchive +JVM_CDSTraceResolve JVM_MaxMemory JVM_MaxObjectInspectionAge JVM_MonitorNotify --- old/src/hotspot/share/classfile/classListParser.cpp 2020-08-05 15:53:46.918107567 -0700 +++ new/src/hotspot/share/classfile/classListParser.cpp 2020-08-05 15:53:46.570094468 -0700 @@ -94,6 +94,12 @@ _source = NULL; _interfaces_specified = false; + _lambda_format = false; + if (strstr(_line, "LF_RESOLVE") != NULL) { + _lambda_format = true; + return true; + } + { int len = (int)strlen(_line); int i; --- old/src/hotspot/share/classfile/classListParser.hpp 2020-08-05 15:53:47.522130304 -0700 +++ new/src/hotspot/share/classfile/classListParser.hpp 2020-08-05 15:53:47.170117054 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, 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 @@ -67,6 +67,9 @@ bool _interfaces_specified; const char* _source; + // current line is output from TRACE_RESOLVE + bool _lambda_format; + bool parse_int_option(const char* option_name, int* value); InstanceKlass* load_class_from_source(Symbol* class_name, TRAPS); ID2KlassTable *table() { @@ -122,6 +125,14 @@ bool is_loading_from_source(); + bool is_lambda_format() { + return _lambda_format; + } + + // return the current line, duped + char* current_line() { + return os::strdup((const char*)_line, mtInternal); + } // Look up the super or interface of the current class being loaded // (in this->load_current_class()). InstanceKlass* lookup_super_for_current_class(Symbol* super_name); --- old/src/hotspot/share/classfile/classLoaderData.cpp 2020-08-05 15:53:48.130153192 -0700 +++ new/src/hotspot/share/classfile/classLoaderData.cpp 2020-08-05 15:53:47.770139640 -0700 @@ -968,3 +968,12 @@ } return false; } + +InstanceKlass* ClassLoaderData::replace_class(Symbol* class_name, InstanceKlass* k) { + assert_locked_or_safepoint(SystemDictionary_lock); + Dictionary* dict = dictionary(); + unsigned hash = dict->compute_hash(class_name); + int index = dict->hash_to_index(hash); + + return dict->replace_class(index, hash, class_name, k); +} --- old/src/hotspot/share/classfile/classLoaderData.hpp 2020-08-05 15:53:48.734175928 -0700 +++ new/src/hotspot/share/classfile/classLoaderData.hpp 2020-08-05 15:53:48.378162527 -0700 @@ -284,6 +284,9 @@ void classes_do(KlassClosure* klass_closure); Klass* klasses() { return _klasses; } + // replace the existing class with k, return the old class. + InstanceKlass* replace_class(Symbol* class_name, InstanceKlass* k); + JNIMethodBlock* jmethod_ids() const { return _jmethod_ids; } void set_jmethod_ids(JNIMethodBlock* new_block) { _jmethod_ids = new_block; } --- old/src/hotspot/share/classfile/dictionary.cpp 2020-08-05 15:53:49.334198514 -0700 +++ new/src/hotspot/share/classfile/dictionary.cpp 2020-08-05 15:53:48.982185264 -0700 @@ -321,6 +321,16 @@ return (entry != NULL) ? entry->instance_klass() : NULL; } +InstanceKlass* Dictionary::replace_class(int index, unsigned hash, + Symbol* name, InstanceKlass* k) { + DictionaryEntry* entry = get_entry(index, hash, name); + assert(entry != NULL, "InstanceKlass k should exist in dictionary"); + + InstanceKlass** addr = entry->klass_addr(); + InstanceKlass* old = *addr; + *addr = k; + return old; +} void Dictionary::add_protection_domain(int index, unsigned int hash, InstanceKlass* klass, --- old/src/hotspot/share/classfile/dictionary.hpp 2020-08-05 15:53:49.938221250 -0700 +++ new/src/hotspot/share/classfile/dictionary.hpp 2020-08-05 15:53:49.586208000 -0700 @@ -61,6 +61,8 @@ bool resize_if_needed(); void add_klass(unsigned int hash, Symbol* class_name, InstanceKlass* obj); + // replace the existing class with k, return old class. + InstanceKlass* replace_class(int index, unsigned hash, Symbol* class_name, InstanceKlass* k); InstanceKlass* find_class(int index, unsigned int hash, Symbol* name); --- old/src/hotspot/share/classfile/klassFactory.hpp 2020-08-05 15:53:50.542243987 -0700 +++ new/src/hotspot/share/classfile/klassFactory.hpp 2020-08-05 15:53:50.186230586 -0700 @@ -67,6 +67,7 @@ friend class ClassLoader; friend class ClassLoaderExt; friend class SystemDictionary; + friend class MetaspaceShared; private: static InstanceKlass* create_from_stream(ClassFileStream* stream, --- old/src/hotspot/share/classfile/systemDictionaryShared.cpp 2020-08-05 15:53:51.146266723 -0700 +++ new/src/hotspot/share/classfile/systemDictionaryShared.cpp 2020-08-05 15:53:50.790253322 -0700 @@ -1142,6 +1142,15 @@ return created; } +void SystemDictionaryShared::add_replaced_class(InstanceKlass* k, TRAPS) { + Arguments::assert_is_dumping_archive(); + assert(!k->is_loaded(), "Invariant"); + { + MutexLocker mu_r(THREAD, Compile_lock); // add_to_hierarchy asserts this. + 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 +1391,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-05 15:53:51.766290062 -0700 +++ new/src/hotspot/share/classfile/systemDictionaryShared.hpp 2020-08-05 15:53:51.414276812 -0700 @@ -243,6 +243,8 @@ static bool is_sharing_possible(ClassLoaderData* loader_data); static bool add_unregistered_class(InstanceKlass* k, TRAPS); + // Only after replaced the existed one. + static void add_replaced_class(InstanceKlass* k, TRAPS); static InstanceKlass* dump_time_resolve_super_or_fail(Symbol* child_name, Symbol* class_name, Handle class_loader, @@ -305,6 +307,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-05 15:53:52.374312949 -0700 +++ new/src/hotspot/share/classfile/vmSymbols.hpp 2020-08-05 15:53:52.014299397 -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_InvokerBytecodeGeneratorHelper, "java/lang/invoke/InvokerBytecodeGeneratorHelper") \ + 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-05 15:53:53.298347732 -0700 +++ new/src/hotspot/share/include/jvm.h 2020-08-05 15:53:52.938334180 -0700 @@ -206,6 +206,9 @@ JNIEXPORT jlong JNICALL JVM_GetRandomSeedForCDSDump(); +JNIEXPORT void JNICALL +JVM_CDSTraceResolve(JNIEnv* env, jclass ignore, jstring line); + /* * java.lang.Throwable */ --- old/src/hotspot/share/memory/metaspaceShared.cpp 2020-08-05 15:53:54.522393807 -0700 +++ new/src/hotspot/share/memory/metaspaceShared.cpp 2020-08-05 15:53:54.166380406 -0700 @@ -24,10 +24,12 @@ #include "precompiled.hpp" #include "jvm.h" +#include "classfile/classFileStream.hpp" #include "classfile/classLoaderDataGraph.hpp" #include "classfile/classListParser.hpp" #include "classfile/classLoaderExt.hpp" #include "classfile/dictionary.hpp" +#include "classfile/klassFactory.hpp" #include "classfile/loaderConstraints.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/placeholders.hpp" @@ -59,8 +61,10 @@ #include "oops/objArrayOop.hpp" #include "oops/oop.inline.hpp" #include "oops/typeArrayKlass.hpp" +#include "oops/typeArrayOop.inline.hpp" #include "prims/jvmtiRedefineClasses.hpp" #include "runtime/handles.inline.hpp" +#include "runtime/javaCalls.hpp" #include "runtime/os.hpp" #include "runtime/safepointVerifiers.hpp" #include "runtime/signature.hpp" @@ -1935,42 +1939,177 @@ } } +static GrowableArray* lambda_list = NULL; + +void MetaspaceShared::regenerate_holder_classes(TRAPS) { + assert(lambda_list != NULL, "Bad List"); + ResourceMark rm(THREAD); + + Symbol* helper_name = vmSymbols::java_lang_invoke_InvokerBytecodeGeneratorHelper(); + Klass* helper_klass = SystemDictionary::resolve_or_null(helper_name, THREAD); + guarantee(helper_klass != NULL, "java/lang/invoke/InvokerByteCodeGeneratorHelper exist!"); + + int len = lambda_list->length(); + objArrayHandle list_lines = oopFactory::new_objArray_handle(SystemDictionary::String_klass(), len, CHECK); + for (int i = 0; i < len; i++) { + Handle h_line = java_lang_String::create_from_str(lambda_list->at(i), 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(); + + jobject ret_obj; + JavaValue result(T_OBJECT); + JavaCalls::call_static(&result, helper_klass, method, signrs, list_lines, THREAD); + ret_obj = result.get_jobject(); + if (!HAS_PENDING_EXCEPTION) { + if (ret_obj == 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; + } + + objArrayHandle h_array(THREAD, (objArrayOop)ret_obj); + 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)); + Handle h_bytes(THREAD, 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; +} + +// k - the class full name +// v - the class bytes +void MetaspaceShared::reload_class(Handle k, Handle v, TRAPS) { + char* path_name = java_lang_String::as_utf8_string(k()); + 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; + } + + typeArrayOop bytes = (typeArrayOop)v(); + int len = bytes->length(); + u1* buf = (u1*)bytes->byte_at_addr(0); + ClassFileStream st(buf, len, NULL, ClassFileStream::verify); + ClassLoaderData* cld = ClassLoaderData::the_null_class_loader_data(); + Handle protection_domain; + ClassLoadInfo cl_info(protection_domain); + + InstanceKlass* result = KlassFactory::create_from_stream(&st, + sym, + cld, + cl_info, + CHECK); + + 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; + } + + // replace with the new created klass. + { + MutexLocker lock(THREAD, SystemDictionary_lock); + InstanceKlass* old = cld->replace_class(sym, result); + SystemDictionaryShared::set_excluded(old); + log_info(cds)("Replace class %s, old: %p new: %p", class_name, old, result); + } + + // add to hierarchy and set state to loaded. + SystemDictionaryShared::add_replaced_class(result, THREAD); + // new class not linked yet. + try_link_class(result, THREAD); + assert(!HAS_PENDING_EXCEPTION, "Invariant"); +} int MetaspaceShared::preload_classes(const char* class_list_path, TRAPS) { ClassListParser parser(class_list_path); int class_count = 0; while (parser.parse_one_line()) { - Klass* klass = parser.load_current_class(THREAD); - if (HAS_PENDING_EXCEPTION) { - if (klass == NULL && - (PENDING_EXCEPTION->klass()->name() == vmSymbols::java_lang_ClassNotFoundException())) { - // print a warning only when the pending exception is class not found - log_warning(cds)("Preload Warning: Cannot find %s", parser.current_class_name()); - } - CLEAR_PENDING_EXCEPTION; - } - if (klass != NULL) { - if (log_is_enabled(Trace, cds)) { - ResourceMark rm(THREAD); - log_trace(cds)("Shared spaces preloaded: %s", klass->external_name()); + if (!parser.is_lambda_format()) { + Klass* klass = parser.load_current_class(THREAD); + if (HAS_PENDING_EXCEPTION) { + if (klass == NULL && + (PENDING_EXCEPTION->klass()->name() == vmSymbols::java_lang_ClassNotFoundException())) { + // print a warning only when the pending exception is class not found + log_warning(cds)("Preload Warning: Cannot find %s", parser.current_class_name()); + } + CLEAR_PENDING_EXCEPTION; } + if (klass != NULL) { + if (log_is_enabled(Trace, cds)) { + ResourceMark rm(THREAD); + log_trace(cds)("Shared spaces preloaded: %s", klass->external_name()); + } - if (klass->is_instance_klass()) { - InstanceKlass* ik = InstanceKlass::cast(klass); + if (klass->is_instance_klass()) { + InstanceKlass* ik = InstanceKlass::cast(klass); - // Link the class to cause the bytecodes to be rewritten and the - // cpcache to be created. The linking is done as soon as classes - // are loaded in order that the related data structures (klass and - // cpCache) are located together. - try_link_class(ik, THREAD); - guarantee(!HAS_PENDING_EXCEPTION, "exception in link_class"); + // Link the class to cause the bytecodes to be rewritten and the + // cpcache to be created. The linking is done as soon as classes + // are loaded in order that the related data structures (klass and + // cpCache) are located together. + try_link_class(ik, THREAD); + guarantee(!HAS_PENDING_EXCEPTION, "exception in link_class"); + } + class_count++; } - - class_count++; + } else { + if (lambda_list == NULL) { + lambda_list = new GrowableArray(8); + } + lambda_list->append(parser.current_line()); } } + // call java to generate holder classes then replace them in dictionary. + if (lambda_list != NULL) { + regenerate_holder_classes(THREAD); + } return class_count; } @@ -2692,8 +2831,3 @@ } st->cr(); } - - - - - --- old/src/hotspot/share/memory/metaspaceShared.hpp 2020-08-05 15:53:55.458429041 -0700 +++ new/src/hotspot/share/memory/metaspaceShared.hpp 2020-08-05 15:53:55.106415790 -0700 @@ -384,6 +384,8 @@ #if INCLUDE_CDS static void write_region(FileMapInfo* mapinfo, int region_idx, DumpRegion* dump_region, bool read_only, bool allow_exec); + static void regenerate_holder_classes(TRAPS); + static void reload_class(Handle klass/*string*/, Handle bytes/* class bytes*/, TRAPS); #endif static void read_extra_data(const char* filename, TRAPS) NOT_CDS_RETURN; static FileMapInfo* open_static_archive(); --- old/src/hotspot/share/prims/jvm.cpp 2020-08-05 15:53:56.370463372 -0700 +++ new/src/hotspot/share/prims/jvm.cpp 2020-08-05 15:53:56.014449971 -0700 @@ -3847,6 +3847,17 @@ #endif // INCLUDE_CDS JVM_END +JVM_ENTRY(void, JVM_CDSTraceResolve(JNIEnv* env, jclass ignore, 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", 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-05 15:53:57.314498906 -0700 +++ new/src/hotspot/share/runtime/arguments.cpp 2020-08-05 15:53:56.958485506 -0700 @@ -2843,6 +2843,15 @@ if (FLAG_SET_CMDLINE(RequireSharedSpaces, false) != JVMFlag::SUCCESS) { return JNI_EINVAL; } + // -XX:DumpLoadedClassList + } else if (match_option(option, "-XX:DumpLoadedClassList=", &tail)) { + if (tail != NULL) { + add_property("java.lang.invoke.MethodHandle.CDS_TRACE_RESOLVE=true"); + DumpLoadedClassList = os::strdup_check_oom(tail); + } else { + warning("Bad option for -XX:DumpLoadedClassList="); + return JNI_EINVAL; + } // -Xverify } else if (match_option(option, "-Xverify", &tail)) { if (strcmp(tail, ":all") == 0 || strcmp(tail, "") == 0) { --- old/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java 2020-08-05 15:53:57.954522998 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java 2020-08-05 15:53:57.598509597 -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 outLine = "[LF_RESOLVE] " + holder.getName() + " " + name + " " + + shortenSignature(basicTypeSignature(type)) + (resolvedMember != null ? " (success)" : " (fail)"); + if (TRACE_RESOLVE) { + System.out.println(outLine); + } + if (CDS_TRACE_RESOLVE) { + InvokerBytecodeGeneratorHelper.cdsTraceResolve(outLine); + } } return resolvedMember; } --- old/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java 2020-08-05 15:53:58.570546186 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java 2020-08-05 15:53:58.218532936 -0700 @@ -1802,6 +1802,11 @@ } @Override + public Map generateMethodHandleHolderClasses(String... lines) { + return InvokerBytecodeGeneratorHelper.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-05 15:53:59.194569676 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java 2020-08-05 15:53:58.838556275 -0700 @@ -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-05 15:53:59.794592262 -0700 +++ new/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java 2020-08-05 15:53:59.438578860 -0700 @@ -111,6 +111,11 @@ MethodType[] callSiteMethodTypes); /** + * Returns a {@code Map} represent holder class vs class bytes + * @param lines input as TRACE_LF_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-05 15:54:00.398614998 -0700 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java 2020-08-05 15:54:00.042601597 -0700 @@ -32,6 +32,7 @@ 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; @@ -72,33 +73,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 +106,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 +139,47 @@ } }, 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); - } - 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 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); + 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 + File file = null; + if (mainArgument == null || !mainArgument.startsWith("@")) { + file = new File(DEFAULT_TRACE_FILE); } 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); + file = new File(mainArgument.substring(1)); } - } - 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); + if (file.exists()) { + try { + List all = Files.readAllLines(file.toPath()); + // convert to String[] + int size = all.size(); + lines = new String[size]; + lines = all.toArray(lines); + } catch (Exception e) { + throw new PluginException("Couldn't read " + file.getName(), e); + } } } } --- old/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedModuleWithCustomImageTest.java 2020-08-05 15:54:01.006637885 -0700 +++ new/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedModuleWithCustomImageTest.java 2020-08-05 15:54:00.650624484 -0700 @@ -63,6 +63,11 @@ throw new RuntimeException("Compilation failure."); } + System.out.println("Java Home: " + JAVA_HOME); + System.out.println("JDK Home: " + jdkHome.toString()); + System.out.println("jdkModes: " + jdkMods.toString()); + System.out.println("testSrc: " + testSrc.toString()); + // create custom runtime image named 'myimage' Files.createDirectories(jmods); Path image = Paths.get("myimage"); @@ -72,11 +77,14 @@ // test using 'myimage' testArchivedModuleUsingImage(image); - Files.delete(jmods.resolve(TEST_MODULE + ".jmod")); + // Files.delete(jmods.resolve(TEST_MODULE + ".jmod")); } private static void runJlink(Path image, String modName) throws Throwable { Path jlink = Paths.get(JAVA_HOME, "bin", "jlink"); + String cmd = jlink.toString() + " --output " + image.toString() + " --add-modules " + modName + + " --module-path " + jdkMods + File.pathSeparator + jmods; + System.out.println("Cmd: " + cmd); OutputAnalyzer output = ProcessTools.executeProcess(jlink.toString(), "--output", image.toString(), "--add-modules", modName, @@ -86,6 +94,14 @@ private static void runJmod(String cp, String modName) throws Throwable { Path jmod = Paths.get(JAVA_HOME, "bin", "jmod"); + String cmd = jmod.toString() + + " create " + + " --class-path " + cp + + " --module-version " + "1.0" + + " --main-class " + " jdk.test.Test " + + jmods.resolve(modName + ".jmod").toString(); + System.out.println("Cmd: " + cmd); + OutputAnalyzer output = ProcessTools.executeProcess(jmod.toString(), "create", "--class-path", cp, --- /dev/null 2019-11-19 22:05:02.069813242 -0800 +++ new/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGeneratorHelper.java 2020-08-05 15:54:01.258647371 -0700 @@ -0,0 +1,405 @@ +/* + * 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. + * + */ +package java.lang.invoke; + +import jdk.internal.loader.BuiltinClassLoader; +import jdk.internal.misc.VM; +import jdk.internal.access.JavaLangInvokeAccess; +import jdk.internal.access.SharedSecrets; +import java.io.BufferedReader; +import java.io.File; +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.Arrays; +import java.util.EnumSet; +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; +/** + * Helper to generate java.lang.invoke classes. + * + * This class takes stream of strings generated by running any application with + * {@code -Djava.lang.invoke.MethodHandle.TRACE_RESOLVE=true}. This is done + * automatically during build, see make/GenerateLinkOptData.gmk. See + * build/tools/classlist/HelloClasslist.java for the training application. + * + * HelloClasslist tries to reflect common use of java.lang.invoke during early + * startup and warmup in various applications. To ensure a good default + * trade-off between static footprint and startup the application should be + * relatively conservative. + * + * help to improve startup time. + */ + +class InvokerBytecodeGeneratorHelper { + // @overide Can be accessed in children + 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 as 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 create generated lambda-form class + * The input is as TRACE_RESOLVE + * @return @code { Object[] } if holder classes can be generated. + * @param lines the output line string from @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 i = 0; + for (Map.Entry entry : result.entrySet()) { + ret_array[i++] = entry.getKey(); + ret_array[i++] = entry.getValue(); + }; + return ret_array; + } catch (Exception e) { + return null; + } + } + + /* return a map of generateMHHolderClasses(String[] lines) throws InvokerGenerateBytesException { + 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 InvokerGenerateBytesException( + "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 InvokerGenerateBytesException( + "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()]; + 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 InvokerGenerateBytesException( + "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[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 InvokerGenerateBytesException( + "CallSite type parameter must end with Object: " + callSiteType); + } + callSiteMethodTypes[i] = mt.dropParameterTypes(lastParam, lastParam + 1); + i++; + } + Map result = new HashMap(); + + byte[] res = GenerateJLIClassesHelper.generateDirectMethodHandleHolderClassBytes( + DIRECT_HOLDER, directMethodTypes, dmhTypes); + result.put(DIRECT_METHOD_HOLDER_ENTRY, res); + + res = GenerateJLIClassesHelper.generateDelegatingMethodHandleHolderClassBytes( + DELEGATING_HOLDER, directMethodTypes); + result.put(DELEGATING_METHOD_HOLDER_ENTRY, res); + + + res = GenerateJLIClassesHelper.generateInvokersHolderClassBytes(INVOKERS_HOLDER_INTERNAL_NAME, + invokerMethodTypes, callSiteMethodTypes); + result.put(INVOKERS_HOLDER_ENTRY, res); + + res = GenerateJLIClassesHelper.generateBasicFormsClassBytes(BASIC_FORMS_HOLDER); + result.put(BASIC_FORMS_HOLDER_ENTRY, res); + + speciesTypes.forEach(types -> { + Map.Entry entry = GenerateJLIClassesHelper.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 InvokerGenerateBytesException( + "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 InvokerGenerateBytesException( + "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); + } + } +} --- /dev/null 2019-11-19 22:05:02.069813242 -0800 +++ new/src/java.base/share/classes/java/lang/invoke/InvokerGenerateBytesException.java 2020-08-05 15:54:01.886671011 -0700 @@ -0,0 +1,64 @@ +/* + * 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. + */ +package java.lang.invoke; + +/** + * An unchecked exception thrown by InvokerBytecodeGeneratorHelper API for unrecoverable + * conditions. + */ +public final class InvokerGenerateBytesException extends RuntimeException { + private static final long serialVersionUID = 7117982019443100396L; + /** + * default construct an InvokerGenerateBytesException + */ + public InvokerGenerateBytesException() { + + } + + /** + * construct an InvokerGenerateBytesException from a Throwble + * @param ex the exception for this InvokerGenerateBytesException + */ + public InvokerGenerateBytesException(Throwable ex) { + super(ex); + } + + /** + * construct an InvokerGenerateBytesException from a message + * @param msg the message for build this InvokerGenerateBytesException + */ + public InvokerGenerateBytesException(String msg) { + super(msg); + } + + /** + * construct an InvokerGenerateBytesException from a Throwable and a message + * @param thr the Throwable + * @param msg the message + */ + public InvokerGenerateBytesException(String msg, Throwable thr) { + super(msg, thr); + } +} --- /dev/null 2019-11-19 22:05:02.069813242 -0800 +++ new/src/java.base/share/native/libjava/InvokerBytecodeGeneratorHelper.c 2020-08-05 15:54:02.514694651 -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_InvokerBytecodeGeneratorHelper.h" + +JNIEXPORT void JNICALL +Java_java_lang_invoke_InvokerBytecodeGeneratorHelper_cdsTraceResolve(JNIEnv *env, jclass ignore, jstring line) { + JVM_CDSTraceResolve(env, ignore, line); +}