--- a/.hgtags Thu Jul 25 08:13:44 2019 -0400
+++ b/.hgtags Thu Jul 25 08:24:01 2019 -0400
@@ -573,3 +573,6 @@
19d0b382f0869f72d4381b54fa129f1c74b6e766 jdk-14+4
3081f39a3d30d63b112098386ac2bb027c2b7223 jdk-13+29
0f1e29c77e50c7da11d83df410026392c4d1a28c jdk-14+5
+2e63fb0a885fa908a97bbb0da8d7c3de11536aca jdk-13+30
+443f7359b34d60e7821216ffc60f88b6ffe0ccdd jdk-14+6
+28ab01c067551ef158abaef08e154e1051ca0893 jdk-14+7
--- a/doc/building.html Thu Jul 25 08:13:44 2019 -0400
+++ b/doc/building.html Thu Jul 25 08:24:01 2019 -0400
@@ -297,10 +297,7 @@
</tr>
</tbody>
</table>
-<p>All compilers are expected to be able to compile to the C99 language standard,
-as some C99 features are used in the source code. Microsoft Visual Studio
-doesn't fully support C99 so in practice shared code is limited to using C99
-features that it does support.</p>
+<p>All compilers are expected to be able to compile to the C99 language standard, as some C99 features are used in the source code. Microsoft Visual Studio doesn't fully support C99 so in practice shared code is limited to using C99 features that it does support.</p>
<h3 id="gcc">gcc</h3>
<p>The minimum accepted version of gcc is 4.8. Older versions will generate a warning by <code>configure</code> and are unlikely to work.</p>
<p>The JDK is currently known to be able to compile with at least version 7.4 of gcc.</p>
--- a/doc/testing.html Thu Jul 25 08:13:44 2019 -0400
+++ b/doc/testing.html Thu Jul 25 08:24:01 2019 -0400
@@ -154,6 +154,9 @@
<p>Use additional problem lists file or files, in addition to the default ProblemList.txt located at the JTReg test roots.</p>
<p>If multiple file names are specified, they should be separated by space (or, to help avoid quoting issues, the special value <code>%20</code>).</p>
<p>The file names should be either absolute, or relative to the JTReg test root of the tests to be run.</p>
+<h4 id="run_problem_lists">RUN_PROBLEM_LISTS</h4>
+<p>Use the problem lists to select tests instead of excluding them.</p>
+<p>Set to <code>true</code> or <code>false</code>. If <code>true</code>, JTReg will use <code>-match:</code> option, otherwise <code>-exclude:</code> will be used. Default is <code>false</code>.</p>
<h4 id="options">OPTIONS</h4>
<p>Additional options to the JTReg test framework.</p>
<p>Use <code>JTREG="OPTIONS=--help all"</code> to see all available JTReg options.</p>
--- a/doc/testing.md Thu Jul 25 08:13:44 2019 -0400
+++ b/doc/testing.md Thu Jul 25 08:24:01 2019 -0400
@@ -306,6 +306,14 @@
The file names should be either absolute, or relative to the JTReg test root of
the tests to be run.
+#### RUN_PROBLEM_LISTS
+
+Use the problem lists to select tests instead of excluding them.
+
+Set to `true` or `false`.
+If `true`, JTReg will use `-match:` option, otherwise `-exclude:` will be used.
+Default is `false`.
+
#### OPTIONS
Additional options to the JTReg test framework.
--- a/make/Images.gmk Thu Jul 25 08:13:44 2019 -0400
+++ b/make/Images.gmk Thu Jul 25 08:24:01 2019 -0400
@@ -102,7 +102,7 @@
WARN := Creating legacy jre image, \
DEPS := $(JMODS) $(BASE_RELEASE_FILE) \
$(call DependOnVariable, JDK_MODULES_LIST), \
- OUTPUT_DIR := $(JDK_IMAGE_DIR), \
+ OUTPUT_DIR := $(JRE_IMAGE_DIR), \
SUPPORT_DIR := $(SUPPORT_OUTPUTDIR)/images/jre, \
PRE_COMMAND := $(RM) -r $(JRE_IMAGE_DIR), \
COMMAND := $(JLINK_TOOL) --add-modules $(JRE_MODULES_LIST) \
--- a/make/InitSupport.gmk Thu Jul 25 08:13:44 2019 -0400
+++ b/make/InitSupport.gmk Thu Jul 25 08:24:01 2019 -0400
@@ -424,8 +424,8 @@
$(if $(filter all, $(LOG_REPORT)), \
$(GREP) -v -e "^Note: including file:" < $(logfile) || true $(NEWLINE) \
, \
- ($(GREP) -v -e "^Note: including file:" < $(logfile) || true) | $(HEAD) -n 12 $(NEWLINE) \
- if test `$(WC) -l < $(logfile)` -gt 12; then \
+ ($(GREP) -v -e "^Note: including file:" < $(logfile) || true) | $(HEAD) -n 15 $(NEWLINE) \
+ if test `$(WC) -l < $(logfile)` -gt 15; then \
$(ECHO) " ... (rest of output omitted)" ; \
fi $(NEWLINE) \
) \
--- a/make/RunTests.gmk Thu Jul 25 08:13:44 2019 -0400
+++ b/make/RunTests.gmk Thu Jul 25 08:24:01 2019 -0400
@@ -278,7 +278,7 @@
$(eval $(call ParseKeywordVariable, JTREG, \
SINGLE_KEYWORDS := JOBS TIMEOUT_FACTOR TEST_MODE ASSERT VERBOSE RETAIN \
- MAX_MEM, \
+ MAX_MEM RUN_PROBLEM_LISTS, \
STRING_KEYWORDS := OPTIONS JAVA_OPTIONS VM_OPTIONS KEYWORDS \
EXTRA_PROBLEM_LISTS AOT_MODULES, \
))
@@ -828,6 +828,7 @@
endif
JTREG_VERBOSE ?= fail,error,summary
JTREG_RETAIN ?= fail,error
+ JTREG_RUN_PROBLEM_LISTS ?= false
ifneq ($$($1_JTREG_MAX_MEM), 0)
$1_JTREG_BASIC_OPTIONS += -vmoption:-Xmx$$($1_JTREG_MAX_MEM)
@@ -865,13 +866,19 @@
$1_JTREG_BASIC_OPTIONS += -nativepath:$$($1_JTREG_NATIVEPATH)
endif
+ ifeq ($$(JTREG_RUN_PROBLEM_LISTS), true)
+ JTREG_PROBLEM_LIST_PREFIX := -match:
+ else
+ JTREG_PROBLEM_LIST_PREFIX := -exclude:
+ endif
+
ifneq ($$($1_JTREG_PROBLEM_LIST), )
- $1_JTREG_BASIC_OPTIONS += $$(addprefix -exclude:, $$($1_JTREG_PROBLEM_LIST))
+ $1_JTREG_BASIC_OPTIONS += $$(addprefix $$(JTREG_PROBLEM_LIST_PREFIX), $$($1_JTREG_PROBLEM_LIST))
endif
ifneq ($$(JTREG_EXTRA_PROBLEM_LISTS), )
# Accept both absolute paths as well as relative to the current test root.
- $1_JTREG_BASIC_OPTIONS += $$(addprefix -exclude:, $$(wildcard \
+ $1_JTREG_BASIC_OPTIONS += $$(addprefix $$(JTREG_PROBLEM_LIST_PREFIX), $$(wildcard \
$$(JTREG_EXTRA_PROBLEM_LISTS) \
$$(addprefix $$($1_TEST_ROOT)/, $$(JTREG_EXTRA_PROBLEM_LISTS)) \
))
--- a/make/autoconf/flags-cflags.m4 Thu Jul 25 08:13:44 2019 -0400
+++ b/make/autoconf/flags-cflags.m4 Thu Jul 25 08:24:01 2019 -0400
@@ -229,7 +229,7 @@
;;
xlc)
- DISABLE_WARNING_PREFIX="-qsuppress="
+ DISABLE_WARNING_PREFIX="-Wno-"
CFLAGS_WARNINGS_ARE_ERRORS="-qhalt=w"
# Possibly a better subset than "all" is "lan:trx:ret:zea:cmp:ret"
--- a/make/autoconf/flags.m4 Thu Jul 25 08:13:44 2019 -0400
+++ b/make/autoconf/flags.m4 Thu Jul 25 08:24:01 2019 -0400
@@ -176,6 +176,10 @@
AC_MSG_WARN([Ignoring LDFLAGS($LDFLAGS) found in environment. Use --with-extra-ldflags])
fi
+ if test "x$ASFLAGS" != "x"; then
+ AC_MSG_WARN([Ignoring ASFLAGS($ASFLAGS) found in environment. Use --with-extra-asflags])
+ fi
+
AC_ARG_WITH(extra-cflags, [AS_HELP_STRING([--with-extra-cflags],
[extra flags to be used when compiling jdk c-files])])
@@ -185,9 +189,13 @@
AC_ARG_WITH(extra-ldflags, [AS_HELP_STRING([--with-extra-ldflags],
[extra flags to be used when linking jdk])])
+ AC_ARG_WITH(extra-asflags, [AS_HELP_STRING([--with-extra-asflags],
+ [extra flags to be passed to the assembler])])
+
USER_CFLAGS="$with_extra_cflags"
USER_CXXFLAGS="$with_extra_cxxflags"
USER_LDFLAGS="$with_extra_ldflags"
+ USER_ASFLAGS="$with_extra_asflags"
])
# Setup the sysroot flags and add them to global CFLAGS and LDFLAGS so
@@ -280,10 +288,12 @@
EXTRA_CFLAGS="$MACHINE_FLAG $USER_CFLAGS"
EXTRA_CXXFLAGS="$MACHINE_FLAG $USER_CXXFLAGS"
EXTRA_LDFLAGS="$MACHINE_FLAG $USER_LDFLAGS"
+ EXTRA_ASFLAGS="$USER_ASFLAGS"
AC_SUBST(EXTRA_CFLAGS)
AC_SUBST(EXTRA_CXXFLAGS)
AC_SUBST(EXTRA_LDFLAGS)
+ AC_SUBST(EXTRA_ASFLAGS)
# For autoconf testing to work, the global flags must also be stored in the
# "unnamed" CFLAGS etc.
--- a/make/autoconf/spec.gmk.in Thu Jul 25 08:13:44 2019 -0400
+++ b/make/autoconf/spec.gmk.in Thu Jul 25 08:24:01 2019 -0400
@@ -489,6 +489,7 @@
EXTRA_CFLAGS = @EXTRA_CFLAGS@
EXTRA_CXXFLAGS = @EXTRA_CXXFLAGS@
EXTRA_LDFLAGS = @EXTRA_LDFLAGS@
+EXTRA_ASFLAGS = @EXTRA_ASFLAGS@
CXX:=@FIXPATH@ @CCACHE@ @ICECC@ @CXX@
--- a/make/hotspot/lib/CompileJvm.gmk Thu Jul 25 08:13:44 2019 -0400
+++ b/make/hotspot/lib/CompileJvm.gmk Thu Jul 25 08:24:01 2019 -0400
@@ -47,6 +47,8 @@
$(EXTRA_LDFLAGS) \
#
+JVM_ASFLAGS += $(EXTRA_ASFLAGS)
+
JVM_LIBS += \
$(JVM_LIBS_FEATURES) \
#
@@ -95,8 +97,7 @@
unknownpragma doubunder w_enumnotused w_toomanyenumnotused \
wvarhidenmem wunreachable wnoretvalue notemsource
-DISABLED_WARNINGS_xlc := 1540-0216 1540-0198 1540-1090 1540-1639 1540-1088 \
- 1500-010
+DISABLED_WARNINGS_xlc := tautological-compare shift-negative-value
DISABLED_WARNINGS_microsoft :=
@@ -260,6 +261,15 @@
#
# Search the output for the operator(s) of interest, to see where they are
# referenced.
+#
+# When a reference to the global 'operator delete' is reported, it might be
+# due to a "deleting destructor". In this case, objdump may show the
+# reference to be associated with but not actually in a destructor. A
+# deleting destructor is automatically generated for a class whose destructor
+# is virtual. A deleting destructor requires an accessible 'operator delete'
+# for the associated class. If the class doesn't provide a more specific
+# declaration (either directly or by inheriting from a class that provides
+# one) then the global definition will be used, triggering this check.
ifneq ($(GENERATE_COMPILE_COMMANDS_ONLY), true)
ifneq ($(filter $(TOOLCHAIN_TYPE), gcc clang solstudio), )
--- a/make/jdk/src/classes/build/tools/jdwpgen/RootNode.java Thu Jul 25 08:13:44 2019 -0400
+++ b/make/jdk/src/classes/build/tools/jdwpgen/RootNode.java Thu Jul 25 08:24:01 2019 -0400
@@ -53,7 +53,7 @@
writer.println("</head>");
writer.println("<body>");
writer.println("<div class=\"centered\" role=\"banner\">");
- writer.println("<h1 id=\"Protocol Details\">Java Debug Wire Protocol Details</h1>");
+ writer.println("<h1 id=\"Protocol_Details\">Java Debug Wire Protocol Details</h1>");
writer.println("</div>");
writer.println("<nav>");
writer.println("<ul>");
--- a/make/lib/Awt2dLibraries.gmk Thu Jul 25 08:13:44 2019 -0400
+++ b/make/lib/Awt2dLibraries.gmk Thu Jul 25 08:24:01 2019 -0400
@@ -471,7 +471,6 @@
$(LIBAWT_HEADLESS_CFLAGS), \
EXTRA_HEADER_DIRS := $(LIBAWT_HEADLESS_EXTRA_HEADER_DIRS), \
DISABLED_WARNINGS_gcc := unused-function, \
- DISABLED_WARNINGS_xlc := 1506-356, \
DISABLED_WARNINGS_solstudio := E_EMPTY_TRANSLATION_UNIT, \
LDFLAGS := $(LDFLAGS_JDKLIB) \
$(call SET_SHARED_LIBRARY_ORIGIN), \
@@ -481,10 +480,6 @@
LIBS_solaris := $(LIBM) $(LIBDL) $(LIBCXX), \
))
- # AIX warning explanation:
- # 1506-356 : (W) Compilation unit is empty.
- # This happens during the headless build
-
$(BUILD_LIBAWT_HEADLESS): $(BUILD_LIBAWT)
TARGETS += $(BUILD_LIBAWT_HEADLESS)
--- a/src/demo/share/jfc/SwingSet2/TreeDemo.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/demo/share/jfc/SwingSet2/TreeDemo.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,6 +1,6 @@
/*
*
- * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -74,7 +74,7 @@
public JScrollPane createTree() {
DefaultMutableTreeNode top = new DefaultMutableTreeNode(getString("TreeDemo.music"));
- DefaultMutableTreeNode catagory = null ;
+ DefaultMutableTreeNode category = null;
DefaultMutableTreeNode artist = null;
DefaultMutableTreeNode record = null;
@@ -94,12 +94,12 @@
char linetype = line.charAt(0);
switch(linetype) {
case 'C':
- catagory = new DefaultMutableTreeNode(line.substring(2));
- top.add(catagory);
+ category = new DefaultMutableTreeNode(line.substring(2));
+ top.add(category);
break;
case 'A':
- if(catagory != null) {
- catagory.add(artist = new DefaultMutableTreeNode(line.substring(2)));
+ if(category != null) {
+ category.add(artist = new DefaultMutableTreeNode(line.substring(2)));
}
break;
case 'R':
--- a/src/demo/share/jfc/SwingSet2/resources/tree.txt Thu Jul 25 08:13:44 2019 -0400
+++ b/src/demo/share/jfc/SwingSet2/resources/tree.txt Thu Jul 25 08:24:01 2019 -0400
@@ -8,7 +8,7 @@
# A = Artist / Composer #
# R = Record / Style #
# S = Song Name / Composition #
-# C = Catagory #
+# C = Category #
# #
################################################################################
C Classical
@@ -195,49 +195,49 @@
A The Beatles
R A Hard Day's Night
S A Hard Day's Night
-S I Should Have Known Better
-S If I Fell
-S I'm Happy Just To Dance With You
-S And I Love Her
-S Tell Me Why
-S Can't Buy Me Love
-S Any Time At All
-S I'll Cry Instead
-S Things We Said Today
-S When I Get Home
-S You Can't Do That
+S I Should Have Known Better
+S If I Fell
+S I'm Happy Just To Dance With You
+S And I Love Her
+S Tell Me Why
+S Can't Buy Me Love
+S Any Time At All
+S I'll Cry Instead
+S Things We Said Today
+S When I Get Home
+S You Can't Do That
R Beatles For Sale
-S No Reply
-S I'm a Loser
-S Baby's In Black
-S Rock And Roll Music
-S I'll Follow the Sun
-S Mr. Moonlight
-S Kansas City/Hey Hey Hey Hey
-S Eight Days a Week
-S Words Of Love
-S Honey Don't
-S Every Little Thing
-S I Don't Want To Spoil the Party
-S What You're Doing
-S Everybody's Trying To Be My Baby
+S No Reply
+S I'm a Loser
+S Baby's In Black
+S Rock And Roll Music
+S I'll Follow the Sun
+S Mr. Moonlight
+S Kansas City/Hey Hey Hey Hey
+S Eight Days a Week
+S Words Of Love
+S Honey Don't
+S Every Little Thing
+S I Don't Want To Spoil the Party
+S What You're Doing
+S Everybody's Trying To Be My Baby
R Help!
-S Help!
-S The Night Before
-S You've Got To Hide Your Love Away
-S I Need You
-S Another Girl
-S You're Going To Lose That Girl
-S Ticket To Ride
-S Act Naturally
-S It's Only Love
-S You Like Me Too Much
-S Tell Me What You See
-S I've Just Seen a Face
-S Yesterday
-S Dizzy Miss Lizzie
+S Help!
+S The Night Before
+S You've Got To Hide Your Love Away
+S I Need You
+S Another Girl
+S You're Going To Lose That Girl
+S Ticket To Ride
+S Act Naturally
+S It's Only Love
+S You Like Me Too Much
+S Tell Me What You See
+S I've Just Seen a Face
+S Yesterday
+S Dizzy Miss Lizzie
R Rubber Soul
-S Drive My Car
+S Drive My Car
S Norwegian Wood
S You Won't See Me
S Nowhere Man
@@ -245,27 +245,27 @@
S The Word
S Michelle
S What Goes On?
-S Girl
-S I'm Looking Through You
-S In My Life
-S Wait
-S If I Needed Someone
-S Run For Your Life
+S Girl
+S I'm Looking Through You
+S In My Life
+S Wait
+S If I Needed Someone
+S Run For Your Life
R Revolver
-S Taxman
-S Rigby
-S I'm Only Sleeping
-S For You To
-S Here There And Everywhere
+S Taxman
+S Rigby
+S I'm Only Sleeping
+S For You To
+S Here There And Everywhere
S Yellow Submarine
-S She Said She Said
-S Good Day Sunshine
-S And Your Bird Can Sing
-S For No One
-S Doctor Robert
-S I Want To Tell You
-S Got To Get You Into My Life
-S Tomorrow Never Knows
+S She Said She Said
+S Good Day Sunshine
+S And Your Bird Can Sing
+S For No One
+S Doctor Robert
+S I Want To Tell You
+S Got To Get You Into My Life
+S Tomorrow Never Knows
R Sgt. Pepper's Lonely Hearts Club Band
S Sgt. Pepper's Lonely Hearts Club Band
S With a Little Help From My Friends
@@ -554,35 +554,35 @@
S What A Life
A Komeda
R Plan 714 Till
-S Fuego De La Vida
-S Herbamore
-S Som I Fjol
+S Fuego De La Vida
+S Herbamore
+S Som I Fjol
S En Spricka I Taket
R Genius Of
-S More Is More
-S Fire
-S Rocket Plane (Music On The Moon)
-S Boogie Woogie/Rock 'N' Roll
-S Disko
-S Top Star
-S Light O' My Life
-S If
-S Frolic
-S In Orbit
-S Arbogast
+S More Is More
+S Fire
+S Rocket Plane (Music On The Moon)
+S Boogie Woogie/Rock 'N' Roll
+S Disko
+S Top Star
+S Light O' My Life
+S If
+S Frolic
+S In Orbit
+S Arbogast
S New New No
R What Makes It Go
-S Binario
-S It's Alright, Baby
-S Curious
-S Cul de Sac
-S Living Things
-S Flabbergast
-S Campfire
-S Happyment
-S Our Hospitality
-S Focus
-S A Simple Formality
+S Binario
+S It's Alright, Baby
+S Curious
+S Cul de Sac
+S Living Things
+S Flabbergast
+S Campfire
+S Happyment
+S Our Hospitality
+S Focus
+S A Simple Formality
A Steve Miller Band
R Circle Of Love
S Heart Like A Wheel
--- a/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -99,7 +99,7 @@
__ xchg(access.resolved_addr(), value_opr, result, tmp);
if (access.is_oop()) {
- result = load_reference_barrier(access.gen(), result, access.access_emit_info(), true);
+ result = load_reference_barrier(access.gen(), result);
if (ShenandoahSATBBarrier) {
pre_barrier(access.gen(), access.access_emit_info(), access.decorators(), LIR_OprFact::illegalOpr,
result /* pre_val */);
--- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -24,7 +24,7 @@
#include "precompiled.hpp"
#include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp"
#include "gc/shenandoah/shenandoahForwarding.hpp"
-#include "gc/shenandoah/shenandoahHeap.hpp"
+#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "gc/shenandoah/shenandoahHeapRegion.hpp"
#include "gc/shenandoah/shenandoahHeuristics.hpp"
#include "gc/shenandoah/shenandoahRuntime.hpp"
@@ -282,6 +282,40 @@
__ leave();
}
+void ShenandoahBarrierSetAssembler::load_reference_barrier_native(MacroAssembler* masm, Register dst, Register tmp) {
+ if (!ShenandoahLoadRefBarrier) {
+ return;
+ }
+
+ assert(dst != rscratch2, "need rscratch2");
+
+ Label is_null;
+ Label done;
+
+ __ cbz(dst, is_null);
+
+ __ enter();
+
+ Address gc_state(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
+ __ ldrb(rscratch2, gc_state);
+
+ // Check for heap in evacuation phase
+ __ tbz(rscratch2, ShenandoahHeap::EVACUATION_BITPOS, done);
+
+ __ mov(rscratch2, dst);
+ __ push_call_clobbered_registers();
+ __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_native));
+ __ mov(r0, rscratch2);
+ __ blrt(lr, 1, 0, MacroAssembler::ret_type_integral);
+ __ mov(rscratch2, r0);
+ __ pop_call_clobbered_registers();
+ __ mov(dst, rscratch2);
+
+ __ bind(done);
+ __ leave();
+ __ bind(is_null);
+}
+
void ShenandoahBarrierSetAssembler::storeval_barrier(MacroAssembler* masm, Register dst, Register tmp) {
if (ShenandoahStoreValEnqueueBarrier) {
// Save possibly live regs.
@@ -309,15 +343,25 @@
void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
Register dst, Address src, Register tmp1, Register tmp_thread) {
bool on_oop = type == T_OBJECT || type == T_ARRAY;
+ bool not_in_heap = (decorators & IN_NATIVE) != 0;
bool on_weak = (decorators & ON_WEAK_OOP_REF) != 0;
bool on_phantom = (decorators & ON_PHANTOM_OOP_REF) != 0;
bool on_reference = on_weak || on_phantom;
+ bool keep_alive = (decorators & AS_NO_KEEPALIVE) == 0;
BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp_thread);
if (on_oop) {
- load_reference_barrier(masm, dst, tmp1);
-
- if (ShenandoahKeepAliveBarrier && on_reference) {
+ if (not_in_heap) {
+ if (ShenandoahHeap::heap()->is_traversal_mode()) {
+ load_reference_barrier(masm, dst, tmp1);
+ keep_alive = true;
+ } else {
+ load_reference_barrier_native(masm, dst, tmp1);
+ }
+ } else {
+ load_reference_barrier(masm, dst, tmp1);
+ }
+ if (ShenandoahKeepAliveBarrier && on_reference && keep_alive) {
__ enter();
satb_write_barrier_pre(masm /* masm */,
noreg /* obj */,
@@ -469,9 +513,7 @@
__ mov(res, obj);
}
// Check for null.
- if (stub->needs_null_check()) {
- __ cbz(res, done);
- }
+ __ cbz(res, done);
load_reference_barrier_not_null(ce->masm(), res, rscratch1);
--- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Red Hat, Inc. All rights reserved.
+ * Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved.
*
* 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
@@ -58,6 +58,7 @@
void resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp = noreg);
void load_reference_barrier(MacroAssembler* masm, Register dst, Register tmp);
void load_reference_barrier_not_null(MacroAssembler* masm, Register dst, Register tmp);
+ void load_reference_barrier_native(MacroAssembler* masm, Register dst, Register tmp);
address generate_shenandoah_lrb(StubCodeGenerator* cgen);
--- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -800,6 +800,7 @@
#endif
// Class initialization barrier for static methods
+ address c2i_no_clinit_check_entry = NULL;
if (VM_Version::supports_fast_class_init_checks()) {
Label L_skip_barrier;
@@ -812,13 +813,15 @@
__ load_method_holder(rscratch2, rmethod);
__ clinit_barrier(rscratch2, rscratch1, &L_skip_barrier);
__ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub()));
+
__ bind(L_skip_barrier);
+ c2i_no_clinit_check_entry = __ pc();
}
gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup);
__ flush();
- return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+ return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
}
int SharedRuntime::c_calling_convention(const BasicType *sig_bt,
--- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -2040,17 +2040,6 @@
bind(L_fallthrough);
}
-void MacroAssembler::check_method_handle_type(Register mtype_reg, Register mh_reg,
- Register temp_reg,
- Label& wrong_method_type) {
- assert_different_registers(mtype_reg, mh_reg, temp_reg);
- // Compare method type against that of the receiver.
- load_heap_oop(temp_reg, delayed_value(java_lang_invoke_MethodHandle::type_offset_in_bytes, temp_reg), mh_reg,
- noreg, noreg, false, IS_NOT_NULL);
- cmpd(CCR0, temp_reg, mtype_reg);
- bne(CCR0, wrong_method_type);
-}
-
RegisterOrConstant MacroAssembler::argument_offset(RegisterOrConstant arg_slot,
Register temp_reg,
int extra_slot_offset) {
--- a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -565,8 +565,6 @@
Label* L_slow_path = NULL);
// Method handle support (JSR 292).
- void check_method_handle_type(Register mtype_reg, Register mh_reg, Register temp_reg, Label& wrong_method_type);
-
RegisterOrConstant argument_offset(RegisterOrConstant arg_slot, Register temp_reg, int extra_slot_offset = 0);
// Biased locking support
--- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1277,6 +1277,7 @@
c2i_entry = __ pc();
// Class initialization barrier for static methods
+ address c2i_no_clinit_check_entry = NULL;
if (VM_Version::supports_fast_class_init_checks()) {
Label L_skip_barrier;
@@ -1295,11 +1296,12 @@
__ bctr();
__ bind(L_skip_barrier);
+ c2i_no_clinit_check_entry = __ pc();
}
gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, call_interpreter, ientry);
- return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+ return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
}
#ifdef COMPILER2
--- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -2713,6 +2713,7 @@
address c2i_entry = __ pc();
// Class initialization barrier for static methods
+ address c2i_no_clinit_check_entry = NULL;
if (VM_Version::supports_fast_class_init_checks()) {
Label L_skip_barrier;
@@ -2729,11 +2730,12 @@
__ z_br(klass);
__ bind(L_skip_barrier);
+ c2i_no_clinit_check_entry = __ pc();
}
gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup);
- return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+ return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
}
// This function returns the adjust size (in number of words) to a c2i adapter
--- a/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -110,7 +110,7 @@
__ xchg(access.resolved_addr(), result, result, LIR_OprFact::illegalOpr);
if (access.is_oop()) {
- result = load_reference_barrier(access.gen(), result, access.access_emit_info(), true);
+ result = load_reference_barrier(access.gen(), result);
if (ShenandoahSATBBarrier) {
pre_barrier(access.gen(), access.access_emit_info(), access.decorators(), LIR_OprFact::illegalOpr,
result /* pre_val */);
--- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -24,7 +24,7 @@
#include "precompiled.hpp"
#include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp"
#include "gc/shenandoah/shenandoahForwarding.hpp"
-#include "gc/shenandoah/shenandoahHeap.hpp"
+#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "gc/shenandoah/shenandoahHeapRegion.hpp"
#include "gc/shenandoah/shenandoahHeuristics.hpp"
#include "gc/shenandoah/shenandoahRuntime.hpp"
@@ -401,6 +401,86 @@
#endif
}
+void ShenandoahBarrierSetAssembler::load_reference_barrier_native(MacroAssembler* masm, Register dst) {
+ if (!ShenandoahLoadRefBarrier) {
+ return;
+ }
+
+ Label done;
+ Label not_null;
+ Label slow_path;
+
+ // null check
+ __ testptr(dst, dst);
+ __ jcc(Assembler::notZero, not_null);
+ __ jmp(done);
+ __ bind(not_null);
+
+
+#ifdef _LP64
+ Register thread = r15_thread;
+#else
+ Register thread = rcx;
+ if (thread == dst) {
+ thread = rbx;
+ }
+ __ push(thread);
+ __ get_thread(thread);
+#endif
+ assert_different_registers(dst, thread);
+
+ Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
+ __ testb(gc_state, ShenandoahHeap::EVACUATION);
+#ifndef _LP64
+ __ pop(thread);
+#endif
+ __ jccb(Assembler::notZero, slow_path);
+ __ jmp(done);
+ __ bind(slow_path);
+
+ if (dst != rax) {
+ __ xchgptr(dst, rax); // Move obj into rax and save rax into obj.
+ }
+ __ push(rcx);
+ __ push(rdx);
+ __ push(rdi);
+ __ push(rsi);
+#ifdef _LP64
+ __ push(r8);
+ __ push(r9);
+ __ push(r10);
+ __ push(r11);
+ __ push(r12);
+ __ push(r13);
+ __ push(r14);
+ __ push(r15);
+#endif
+
+ __ movptr(rdi, rax);
+ __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_native), rdi);
+
+#ifdef _LP64
+ __ pop(r15);
+ __ pop(r14);
+ __ pop(r13);
+ __ pop(r12);
+ __ pop(r11);
+ __ pop(r10);
+ __ pop(r9);
+ __ pop(r8);
+#endif
+ __ pop(rsi);
+ __ pop(rdi);
+ __ pop(rdx);
+ __ pop(rcx);
+
+ if (dst != rax) {
+ __ xchgptr(rax, dst); // Swap back obj with rax.
+ }
+
+ __ bind(done);
+}
+
void ShenandoahBarrierSetAssembler::storeval_barrier(MacroAssembler* masm, Register dst, Register tmp) {
if (ShenandoahStoreValEnqueueBarrier) {
storeval_barrier_impl(masm, dst, tmp);
@@ -457,12 +537,24 @@
bool on_oop = type == T_OBJECT || type == T_ARRAY;
bool on_weak = (decorators & ON_WEAK_OOP_REF) != 0;
bool on_phantom = (decorators & ON_PHANTOM_OOP_REF) != 0;
+ bool not_in_heap = (decorators & IN_NATIVE) != 0;
bool on_reference = on_weak || on_phantom;
- BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp_thread);
+ bool keep_alive = (decorators & AS_NO_KEEPALIVE) == 0;
+
+ BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp_thread);
if (on_oop) {
- load_reference_barrier(masm, dst);
+ if (not_in_heap) {
+ if (ShenandoahHeap::heap()->is_traversal_mode()) {
+ load_reference_barrier(masm, dst);
+ keep_alive = true;
+ } else {
+ load_reference_barrier_native(masm, dst);
+ }
+ } else {
+ load_reference_barrier(masm, dst);
+ }
- if (ShenandoahKeepAliveBarrier && on_reference) {
+ if (ShenandoahKeepAliveBarrier && on_reference && keep_alive) {
const Register thread = NOT_LP64(tmp_thread) LP64_ONLY(r15_thread);
assert_different_registers(dst, tmp1, tmp_thread);
NOT_LP64(__ get_thread(thread));
@@ -788,10 +880,8 @@
}
// Check for null.
- if (stub->needs_null_check()) {
- __ testptr(res, res);
- __ jcc(Assembler::zero, done);
- }
+ __ testptr(res, res);
+ __ jcc(Assembler::zero, done);
load_reference_barrier_not_null(ce->masm(), res);
--- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Red Hat, Inc. All rights reserved.
+ * Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved.
*
* 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
@@ -78,6 +78,7 @@
#endif
void load_reference_barrier(MacroAssembler* masm, Register dst);
+ void load_reference_barrier_native(MacroAssembler* masm, Register dst);
void cmpxchg_oop(MacroAssembler* masm,
Register res, Address addr, Register oldval, Register newval,
--- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -971,10 +971,8 @@
address c2i_entry = __ pc();
- BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
- bs->c2i_entry_barrier(masm);
-
// Class initialization barrier for static methods
+ address c2i_no_clinit_check_entry = NULL;
if (VM_Version::supports_fast_class_init_checks()) {
Label L_skip_barrier;
Register method = rbx;
@@ -993,12 +991,16 @@
__ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); // slow path
__ bind(L_skip_barrier);
+ c2i_no_clinit_check_entry = __ pc();
}
+ BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
+ bs->c2i_entry_barrier(masm);
+
gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup);
__ flush();
- return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+ return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
}
int SharedRuntime::c_calling_convention(const BasicType *sig_bt,
--- a/src/hotspot/os/aix/attachListener_aix.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/os/aix/attachListener_aix.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2018 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -71,17 +71,7 @@
// the file descriptor for the listening socket
static int _listener;
- static void set_path(char* path) {
- if (path == NULL) {
- _has_path = false;
- } else {
- strncpy(_path, path, UNIX_PATH_MAX);
- _path[UNIX_PATH_MAX-1] = '\0';
- _has_path = true;
- }
- }
-
- static void set_listener(int s) { _listener = s; }
+ static bool _atexit_registered;
// reads a request from the given connected socket
static AixAttachOperation* read_request(int s);
@@ -94,6 +84,19 @@
ATTACH_ERROR_BADVERSION = 101 // error codes
};
+ static void set_path(char* path) {
+ if (path == NULL) {
+ _path[0] = '\0';
+ _has_path = false;
+ } else {
+ strncpy(_path, path, UNIX_PATH_MAX);
+ _path[UNIX_PATH_MAX-1] = '\0';
+ _has_path = true;
+ }
+ }
+
+ static void set_listener(int s) { _listener = s; }
+
// initialize the listener, returns 0 if okay
static int init();
@@ -130,6 +133,7 @@
char AixAttachListener::_path[UNIX_PATH_MAX];
bool AixAttachListener::_has_path;
int AixAttachListener::_listener = -1;
+bool AixAttachListener::_atexit_registered = false;
// Shutdown marker to prevent accept blocking during clean-up
bool AixAttachListener::_shutdown = false;
@@ -177,17 +181,15 @@
// should be sufficient for cleanup.
extern "C" {
static void listener_cleanup() {
- static int cleanup_done;
- if (!cleanup_done) {
- cleanup_done = 1;
- AixAttachListener::set_shutdown(true);
- int s = AixAttachListener::listener();
- if (s != -1) {
- ::shutdown(s, 2);
- }
- if (AixAttachListener::has_path()) {
- ::unlink(AixAttachListener::path());
- }
+ AixAttachListener::set_shutdown(true);
+ int s = AixAttachListener::listener();
+ if (s != -1) {
+ AixAttachListener::set_listener(-1);
+ ::shutdown(s, 2);
+ }
+ if (AixAttachListener::has_path()) {
+ ::unlink(AixAttachListener::path());
+ AixAttachListener::set_path(NULL);
}
}
}
@@ -200,7 +202,10 @@
int listener; // listener socket (file descriptor)
// register function to cleanup
- ::atexit(listener_cleanup);
+ if (!_atexit_registered) {
+ _atexit_registered = true;
+ ::atexit(listener_cleanup);
+ }
int n = snprintf(path, UNIX_PATH_MAX, "%s/.java_pid%d",
os::get_temp_directory(), os::current_process_id());
@@ -515,6 +520,26 @@
return ret_code;
}
+bool AttachListener::check_socket_file() {
+ int ret;
+ struct stat64 st;
+ ret = stat64(AixAttachListener::path(), &st);
+ if (ret == -1) { // need to restart attach listener.
+ log_debug(attach)("Socket file %s does not exist - Restart Attach Listener",
+ AixAttachListener::path());
+
+ listener_cleanup();
+
+ // wait to terminate current attach listener instance...
+ while (AttachListener::transit_state(AL_INITIALIZING,
+ AL_NOT_INITIALIZED) != AL_NOT_INITIALIZED) {
+ os::naked_yield();
+ }
+ return is_init_trigger();
+ }
+ return false;
+}
+
// Attach Listener is started lazily except in the case when
// +ReduseSignalUsage is used
bool AttachListener::init_at_startup() {
--- a/src/hotspot/os/aix/loadlib_aix.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/os/aix/loadlib_aix.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2015 SAP SE. All rights reserved.
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019 SAP SE. 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
@@ -207,7 +207,7 @@
}
}
- trcVerbose("loadquery buffer size is %llu.", buflen);
+ trcVerbose("loadquery buffer size is " SIZE_FORMAT ".", buflen);
// Iterate over the loadquery result. For details see sys/ldr.h on AIX.
ldi = (struct ld_info*) buffer;
@@ -264,7 +264,7 @@
e->info.is_in_vm = true;
}
- trcVerbose("entry: %p %llu, %p %llu, %s %s %s, %d",
+ trcVerbose("entry: %p " SIZE_FORMAT ", %p " SIZE_FORMAT ", %s %s %s, %d",
e->info.text, e->info.text_len,
e->info.data, e->info.data_len,
e->info.path, e->info.shortname,
--- a/src/hotspot/os/aix/os_aix.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/os/aix/os_aix.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -487,8 +487,7 @@
if (::shmctl(shmid, SHM_PAGESIZE, &shm_buf) != 0) {
const int en = errno;
::shmctl(shmid, IPC_RMID, NULL); // As early as possible!
- trcVerbose("shmctl(SHM_PAGESIZE) failed with errno=%n",
- errno);
+ trcVerbose("shmctl(SHM_PAGESIZE) failed with errno=%d", errno);
} else {
// Attach and double check pageisze.
void* p = ::shmat(shmid, NULL, 0);
@@ -496,7 +495,7 @@
guarantee0(p != (void*) -1); // Should always work.
const size_t real_pagesize = os::Aix::query_pagesize(p);
if (real_pagesize != pagesize) {
- trcVerbose("real page size (0x%llX) differs.", real_pagesize);
+ trcVerbose("real page size (" SIZE_FORMAT_HEX ") differs.", real_pagesize);
} else {
can_use = true;
}
@@ -1888,12 +1887,12 @@
if (!contains_range(p, s)) {
trcVerbose("[" PTR_FORMAT " - " PTR_FORMAT "] is not a sub "
"range of [" PTR_FORMAT " - " PTR_FORMAT "].",
- p, p + s, addr, addr + size);
+ p2i(p), p2i(p + s), p2i(addr), p2i(addr + size));
guarantee0(false);
}
if (!is_aligned_to(p, pagesize) || !is_aligned_to(p + s, pagesize)) {
trcVerbose("range [" PTR_FORMAT " - " PTR_FORMAT "] is not"
- " aligned to pagesize (%lu)", p, p + s, (unsigned long) pagesize);
+ " aligned to pagesize (%lu)", p2i(p), p2i(p + s), (unsigned long) pagesize);
guarantee0(false);
}
}
@@ -1964,7 +1963,7 @@
trcVerbose("reserve_shmated_memory " UINTX_FORMAT " bytes, wishaddress "
PTR_FORMAT ", alignment_hint " UINTX_FORMAT "...",
- bytes, requested_addr, alignment_hint);
+ bytes, p2i(requested_addr), alignment_hint);
// Either give me wish address or wish alignment but not both.
assert0(!(requested_addr != NULL && alignment_hint != 0));
@@ -1973,7 +1972,7 @@
// BRK because that may cause malloc OOM.
if (requested_addr != NULL && is_close_to_brk((address)requested_addr)) {
trcVerbose("Wish address " PTR_FORMAT " is too close to the BRK segment. "
- "Will attach anywhere.", requested_addr);
+ "Will attach anywhere.", p2i(requested_addr));
// Act like the OS refused to attach there.
requested_addr = NULL;
}
@@ -2025,7 +2024,7 @@
// Handle shmat error. If we failed to attach, just return.
if (addr == (char*)-1) {
- trcVerbose("Failed to attach segment at " PTR_FORMAT " (%d).", requested_addr, errno_shmat);
+ trcVerbose("Failed to attach segment at " PTR_FORMAT " (%d).", p2i(requested_addr), errno_shmat);
return NULL;
}
@@ -2033,15 +2032,15 @@
// work (see above), the system may have given us something other then 4K (LDR_CNTRL).
const size_t real_pagesize = os::Aix::query_pagesize(addr);
if (real_pagesize != shmbuf.shm_pagesize) {
- trcVerbose("pagesize is, surprisingly, %h.", real_pagesize);
+ trcVerbose("pagesize is, surprisingly, " SIZE_FORMAT, real_pagesize);
}
if (addr) {
trcVerbose("shm-allocated " PTR_FORMAT " .. " PTR_FORMAT " (" UINTX_FORMAT " bytes, " UINTX_FORMAT " %s pages)",
- addr, addr + size - 1, size, size/real_pagesize, describe_pagesize(real_pagesize));
+ p2i(addr), p2i(addr + size - 1), size, size/real_pagesize, describe_pagesize(real_pagesize));
} else {
if (requested_addr != NULL) {
- trcVerbose("failed to shm-allocate " UINTX_FORMAT " bytes at with address " PTR_FORMAT ".", size, requested_addr);
+ trcVerbose("failed to shm-allocate " UINTX_FORMAT " bytes at with address " PTR_FORMAT ".", size, p2i(requested_addr));
} else {
trcVerbose("failed to shm-allocate " UINTX_FORMAT " bytes at any address.", size);
}
@@ -2057,7 +2056,7 @@
static bool release_shmated_memory(char* addr, size_t size) {
trcVerbose("release_shmated_memory [" PTR_FORMAT " - " PTR_FORMAT "].",
- addr, addr + size - 1);
+ p2i(addr), p2i(addr + size - 1));
bool rc = false;
@@ -2073,12 +2072,12 @@
static bool uncommit_shmated_memory(char* addr, size_t size) {
trcVerbose("uncommit_shmated_memory [" PTR_FORMAT " - " PTR_FORMAT "].",
- addr, addr + size - 1);
+ p2i(addr), p2i(addr + size - 1));
const bool rc = my_disclaim64(addr, size);
if (!rc) {
- trcVerbose("my_disclaim64(" PTR_FORMAT ", " UINTX_FORMAT ") failed.\n", addr, size);
+ trcVerbose("my_disclaim64(" PTR_FORMAT ", " UINTX_FORMAT ") failed.\n", p2i(addr), size);
return false;
}
return true;
@@ -2095,11 +2094,11 @@
static char* reserve_mmaped_memory(size_t bytes, char* requested_addr, size_t alignment_hint) {
trcVerbose("reserve_mmaped_memory " UINTX_FORMAT " bytes, wishaddress " PTR_FORMAT ", "
"alignment_hint " UINTX_FORMAT "...",
- bytes, requested_addr, alignment_hint);
+ bytes, p2i(requested_addr), alignment_hint);
// If a wish address is given, but not aligned to 4K page boundary, mmap will fail.
if (requested_addr && !is_aligned_to(requested_addr, os::vm_page_size()) != 0) {
- trcVerbose("Wish address " PTR_FORMAT " not aligned to page boundary.", requested_addr);
+ trcVerbose("Wish address " PTR_FORMAT " not aligned to page boundary.", p2i(requested_addr));
return NULL;
}
@@ -2107,7 +2106,7 @@
// BRK because that may cause malloc OOM.
if (requested_addr != NULL && is_close_to_brk((address)requested_addr)) {
trcVerbose("Wish address " PTR_FORMAT " is too close to the BRK segment. "
- "Will attach anywhere.", requested_addr);
+ "Will attach anywhere.", p2i(requested_addr));
// Act like the OS refused to attach there.
requested_addr = NULL;
}
@@ -2154,7 +2153,7 @@
PROT_READ|PROT_WRITE|PROT_EXEC, flags, -1, 0);
if (addr == MAP_FAILED) {
- trcVerbose("mmap(" PTR_FORMAT ", " UINTX_FORMAT ", ..) failed (%d)", requested_addr, size, errno);
+ trcVerbose("mmap(" PTR_FORMAT ", " UINTX_FORMAT ", ..) failed (%d)", p2i(requested_addr), size, errno);
return NULL;
}
@@ -2173,10 +2172,10 @@
if (addr) {
trcVerbose("mmap-allocated " PTR_FORMAT " .. " PTR_FORMAT " (" UINTX_FORMAT " bytes)",
- addr, addr + bytes, bytes);
+ p2i(addr), p2i(addr + bytes), bytes);
} else {
if (requested_addr != NULL) {
- trcVerbose("failed to mmap-allocate " UINTX_FORMAT " bytes at wish address " PTR_FORMAT ".", bytes, requested_addr);
+ trcVerbose("failed to mmap-allocate " UINTX_FORMAT " bytes at wish address " PTR_FORMAT ".", bytes, p2i(requested_addr));
} else {
trcVerbose("failed to mmap-allocate " UINTX_FORMAT " bytes at any address.", bytes);
}
@@ -2196,7 +2195,7 @@
assert0(is_aligned_to(size, os::vm_page_size()));
trcVerbose("release_mmaped_memory [" PTR_FORMAT " - " PTR_FORMAT "].",
- addr, addr + size - 1);
+ p2i(addr), p2i(addr + size - 1));
bool rc = false;
if (::munmap(addr, size) != 0) {
@@ -2216,7 +2215,7 @@
assert0(is_aligned_to(size, os::vm_page_size()));
trcVerbose("uncommit_mmaped_memory [" PTR_FORMAT " - " PTR_FORMAT "].",
- addr, addr + size - 1);
+ p2i(addr), p2i(addr + size - 1));
bool rc = false;
// Uncommit mmap memory with msync MS_INVALIDATE.
@@ -2247,7 +2246,7 @@
static void warn_fail_commit_memory(char* addr, size_t size, bool exec,
int err) {
warning("INFO: os::commit_memory(" PTR_FORMAT ", " SIZE_FORMAT
- ", %d) failed; error='%s' (errno=%d)", addr, size, exec,
+ ", %d) failed; error='%s' (errno=%d)", p2i(addr), size, exec,
os::errno_name(err), err);
}
#endif
@@ -2275,7 +2274,7 @@
guarantee0(vmi);
vmi->assert_is_valid_subrange(addr, size);
- trcVerbose("commit_memory [" PTR_FORMAT " - " PTR_FORMAT "].", addr, addr + size - 1);
+ trcVerbose("commit_memory [" PTR_FORMAT " - " PTR_FORMAT "].", p2i(addr), p2i(addr + size - 1));
if (UseExplicitCommit) {
// AIX commits memory on touch. So, touch all pages to be committed.
@@ -4075,7 +4074,7 @@
assert(minor > 0, "invalid OS release");
_os_version = (major << 24) | (minor << 16);
char ver_str[20] = {0};
- char *name_str = "unknown OS";
+ const char* name_str = "unknown OS";
if (strcmp(uts.sysname, "OS400") == 0) {
// We run on AS/400 PASE. We do not support versions older than V5R4M0.
_on_pase = 1;
@@ -4086,19 +4085,19 @@
name_str = "OS/400 (pase)";
jio_snprintf(ver_str, sizeof(ver_str), "%u.%u", major, minor);
} else if (strcmp(uts.sysname, "AIX") == 0) {
- // We run on AIX. We do not support versions older than AIX 5.3.
+ // We run on AIX. We do not support versions older than AIX 7.1.
_on_pase = 0;
// Determine detailed AIX version: Version, Release, Modification, Fix Level.
odmWrapper::determine_os_kernel_version(&_os_version);
- if (os_version_short() < 0x0503) {
- trcVerbose("AIX release older than AIX 5.3 not supported.");
+ if (os_version_short() < 0x0701) {
+ trcVerbose("AIX releases older than AIX 7.1 are not supported.");
assert(false, "AIX release too old.");
}
name_str = "AIX";
jio_snprintf(ver_str, sizeof(ver_str), "%u.%u.%u.%u",
major, minor, (_os_version >> 8) & 0xFF, _os_version & 0xFF);
} else {
- assert(false, name_str);
+ assert(false, "%s", name_str);
}
trcVerbose("We run on %s %s", name_str, ver_str);
}
--- a/src/hotspot/os/aix/perfMemory_aix.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/os/aix/perfMemory_aix.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1111,7 +1111,7 @@
if ((statbuf.st_size == 0) ||
((size_t)statbuf.st_size % os::vm_page_size() != 0)) {
- THROW_MSG_0(vmSymbols::java_lang_Exception(),
+ THROW_MSG_0(vmSymbols::java_io_IOException(),
"Invalid PerfMemory size");
}
--- a/src/hotspot/os/bsd/attachListener_bsd.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/os/bsd/attachListener_bsd.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -68,17 +68,7 @@
// the file descriptor for the listening socket
static int _listener;
- static void set_path(char* path) {
- if (path == NULL) {
- _has_path = false;
- } else {
- strncpy(_path, path, UNIX_PATH_MAX);
- _path[UNIX_PATH_MAX-1] = '\0';
- _has_path = true;
- }
- }
-
- static void set_listener(int s) { _listener = s; }
+ static bool _atexit_registered;
// reads a request from the given connected socket
static BsdAttachOperation* read_request(int s);
@@ -91,6 +81,19 @@
ATTACH_ERROR_BADVERSION = 101 // error codes
};
+ static void set_path(char* path) {
+ if (path == NULL) {
+ _path[0] = '\0';
+ _has_path = false;
+ } else {
+ strncpy(_path, path, UNIX_PATH_MAX);
+ _path[UNIX_PATH_MAX-1] = '\0';
+ _has_path = true;
+ }
+ }
+
+ static void set_listener(int s) { _listener = s; }
+
// initialize the listener, returns 0 if okay
static int init();
@@ -124,6 +127,7 @@
char BsdAttachListener::_path[UNIX_PATH_MAX];
bool BsdAttachListener::_has_path;
int BsdAttachListener::_listener = -1;
+bool BsdAttachListener::_atexit_registered = false;
// Supporting class to help split a buffer into individual components
class ArgumentIterator : public StackObj {
@@ -158,16 +162,15 @@
// bound too.
extern "C" {
static void listener_cleanup() {
- static int cleanup_done;
- if (!cleanup_done) {
- cleanup_done = 1;
- int s = BsdAttachListener::listener();
- if (s != -1) {
- ::close(s);
- }
- if (BsdAttachListener::has_path()) {
- ::unlink(BsdAttachListener::path());
- }
+ int s = BsdAttachListener::listener();
+ if (s != -1) {
+ BsdAttachListener::set_listener(-1);
+ ::shutdown(s, SHUT_RDWR);
+ ::close(s);
+ }
+ if (BsdAttachListener::has_path()) {
+ ::unlink(BsdAttachListener::path());
+ BsdAttachListener::set_path(NULL);
}
}
}
@@ -180,7 +183,10 @@
int listener; // listener socket (file descriptor)
// register function to cleanup
- ::atexit(listener_cleanup);
+ if (!_atexit_registered) {
+ _atexit_registered = true;
+ ::atexit(listener_cleanup);
+ }
int n = snprintf(path, UNIX_PATH_MAX, "%s/.java_pid%d",
os::get_temp_directory(), os::current_process_id());
@@ -485,6 +491,28 @@
return ret_code;
}
+bool AttachListener::check_socket_file() {
+ int ret;
+ struct stat st;
+ ret = stat(BsdAttachListener::path(), &st);
+ if (ret == -1) { // need to restart attach listener.
+ log_debug(attach)("Socket file %s does not exist - Restart Attach Listener",
+ BsdAttachListener::path());
+
+ listener_cleanup();
+
+ // wait to terminate current attach listener instance...
+
+ while (AttachListener::transit_state(AL_INITIALIZING,
+
+ AL_NOT_INITIALIZED) != AL_NOT_INITIALIZED) {
+ os::naked_yield();
+ }
+ return is_init_trigger();
+ }
+ return false;
+}
+
// Attach Listener is started lazily except in the case when
// +ReduseSignalUsage is used
bool AttachListener::init_at_startup() {
--- a/src/hotspot/os/bsd/perfMemory_bsd.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/os/bsd/perfMemory_bsd.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1028,7 +1028,7 @@
if ((statbuf.st_size == 0) ||
((size_t)statbuf.st_size % os::vm_page_size() != 0)) {
- THROW_MSG_0(vmSymbols::java_lang_Exception(),
+ THROW_MSG_0(vmSymbols::java_io_IOException(),
"Invalid PerfMemory size");
}
--- a/src/hotspot/os/linux/attachListener_linux.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/os/linux/attachListener_linux.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -69,17 +69,7 @@
// the file descriptor for the listening socket
static int _listener;
- static void set_path(char* path) {
- if (path == NULL) {
- _has_path = false;
- } else {
- strncpy(_path, path, UNIX_PATH_MAX);
- _path[UNIX_PATH_MAX-1] = '\0';
- _has_path = true;
- }
- }
-
- static void set_listener(int s) { _listener = s; }
+ static bool _atexit_registered;
// reads a request from the given connected socket
static LinuxAttachOperation* read_request(int s);
@@ -92,6 +82,19 @@
ATTACH_ERROR_BADVERSION = 101 // error codes
};
+ static void set_path(char* path) {
+ if (path == NULL) {
+ _path[0] = '\0';
+ _has_path = false;
+ } else {
+ strncpy(_path, path, UNIX_PATH_MAX);
+ _path[UNIX_PATH_MAX-1] = '\0';
+ _has_path = true;
+ }
+ }
+
+ static void set_listener(int s) { _listener = s; }
+
// initialize the listener, returns 0 if okay
static int init();
@@ -125,6 +128,7 @@
char LinuxAttachListener::_path[UNIX_PATH_MAX];
bool LinuxAttachListener::_has_path;
int LinuxAttachListener::_listener = -1;
+bool LinuxAttachListener::_atexit_registered = false;
// Supporting class to help split a buffer into individual components
class ArgumentIterator : public StackObj {
@@ -159,16 +163,15 @@
// bound too.
extern "C" {
static void listener_cleanup() {
- static int cleanup_done;
- if (!cleanup_done) {
- cleanup_done = 1;
- int s = LinuxAttachListener::listener();
- if (s != -1) {
- ::close(s);
- }
- if (LinuxAttachListener::has_path()) {
- ::unlink(LinuxAttachListener::path());
- }
+ int s = LinuxAttachListener::listener();
+ if (s != -1) {
+ LinuxAttachListener::set_listener(-1);
+ ::shutdown(s, SHUT_RDWR);
+ ::close(s);
+ }
+ if (LinuxAttachListener::has_path()) {
+ ::unlink(LinuxAttachListener::path());
+ LinuxAttachListener::set_path(NULL);
}
}
}
@@ -181,7 +184,10 @@
int listener; // listener socket (file descriptor)
// register function to cleanup
- ::atexit(listener_cleanup);
+ if (!_atexit_registered) {
+ _atexit_registered = true;
+ ::atexit(listener_cleanup);
+ }
int n = snprintf(path, UNIX_PATH_MAX, "%s/.java_pid%d",
os::get_temp_directory(), os::current_process_id());
@@ -485,6 +491,26 @@
return ret_code;
}
+bool AttachListener::check_socket_file() {
+ int ret;
+ struct stat64 st;
+ ret = stat64(LinuxAttachListener::path(), &st);
+ if (ret == -1) { // need to restart attach listener.
+ log_debug(attach)("Socket file %s does not exist - Restart Attach Listener",
+ LinuxAttachListener::path());
+
+ listener_cleanup();
+
+ // wait to terminate current attach listener instance...
+ while (AttachListener::transit_state(AL_INITIALIZING,
+ AL_NOT_INITIALIZED) != AL_NOT_INITIALIZED) {
+ os::naked_yield();
+ }
+ return is_init_trigger();
+ }
+ return false;
+}
+
// Attach Listener is started lazily except in the case when
// +ReduseSignalUsage is used
bool AttachListener::init_at_startup() {
--- a/src/hotspot/os/linux/perfMemory_linux.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/os/linux/perfMemory_linux.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1107,7 +1107,7 @@
if ((statbuf.st_size == 0) ||
((size_t)statbuf.st_size % os::vm_page_size() != 0)) {
- THROW_MSG_0(vmSymbols::java_lang_Exception(),
+ THROW_MSG_0(vmSymbols::java_io_IOException(),
"Invalid PerfMemory size");
}
--- a/src/hotspot/os/solaris/attachListener_solaris.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/os/solaris/attachListener_solaris.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -75,17 +75,7 @@
// door descriptor returned by door_create
static int _door_descriptor;
- static void set_door_path(char* path) {
- if (path == NULL) {
- _has_door_path = false;
- } else {
- strncpy(_door_path, path, PATH_MAX);
- _door_path[PATH_MAX] = '\0'; // ensure it's nul terminated
- _has_door_path = true;
- }
- }
-
- static void set_door_descriptor(int dd) { _door_descriptor = dd; }
+ static bool _atexit_registered;
// mutex to protect operation list
static mutex_t _mutex;
@@ -121,6 +111,19 @@
ATTACH_ERROR_DENIED = 104
};
+ static void set_door_path(char* path) {
+ if (path == NULL) {
+ _door_path[0] = '\0';
+ _has_door_path = false;
+ } else {
+ strncpy(_door_path, path, PATH_MAX);
+ _door_path[PATH_MAX] = '\0'; // ensure it's nul terminated
+ _has_door_path = true;
+ }
+ }
+
+ static void set_door_descriptor(int dd) { _door_descriptor = dd; }
+
// initialize the listener
static int init();
@@ -169,6 +172,7 @@
char SolarisAttachListener::_door_path[PATH_MAX+1];
volatile bool SolarisAttachListener::_has_door_path;
int SolarisAttachListener::_door_descriptor = -1;
+bool SolarisAttachListener::_atexit_registered = false;
mutex_t SolarisAttachListener::_mutex;
sema_t SolarisAttachListener::_wakeup;
SolarisAttachOperation* SolarisAttachListener::_head = NULL;
@@ -364,18 +368,16 @@
// atexit hook to detach the door and remove the file
extern "C" {
static void listener_cleanup() {
- static int cleanup_done;
- if (!cleanup_done) {
- cleanup_done = 1;
- int dd = SolarisAttachListener::door_descriptor();
- if (dd >= 0) {
- ::close(dd);
- }
- if (SolarisAttachListener::has_door_path()) {
- char* path = SolarisAttachListener::door_path();
- ::fdetach(path);
- ::unlink(path);
- }
+ int dd = SolarisAttachListener::door_descriptor();
+ if (dd >= 0) {
+ SolarisAttachListener::set_door_descriptor(-1);
+ ::close(dd);
+ }
+ if (SolarisAttachListener::has_door_path()) {
+ char* path = SolarisAttachListener::door_path();
+ ::fdetach(path);
+ ::unlink(path);
+ SolarisAttachListener::set_door_path(NULL);
}
}
}
@@ -387,7 +389,10 @@
int fd, res;
// register exit function
- ::atexit(listener_cleanup);
+ if (!_atexit_registered) {
+ _atexit_registered = true;
+ ::atexit(listener_cleanup);
+ }
// create the door descriptor
int dd = ::door_create(enqueue_proc, NULL, 0);
@@ -643,6 +648,26 @@
}
}
+bool AttachListener::check_socket_file() {
+ int ret;
+ struct stat64 st;
+ ret = stat64(SolarisAttachListener::door_path(), &st);
+ if (ret == -1) { // need to restart attach listener.
+ log_debug(attach)("Door file %s does not exist - Restart Attach Listener",
+ SolarisAttachListener::door_path());
+
+ listener_cleanup();
+
+ // wait to terminate current attach listener instance...
+ while (AttachListener::transit_state(AL_INITIALIZING,
+ AL_NOT_INITIALIZED) != AL_NOT_INITIALIZED) {
+ os::naked_yield();
+ }
+ return is_init_trigger();
+ }
+ return false;
+}
+
// If the file .attach_pid<pid> exists in the working directory
// or /tmp then this is the trigger to start the attach mechanism
bool AttachListener::is_init_trigger() {
--- a/src/hotspot/os/solaris/perfMemory_solaris.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/os/solaris/perfMemory_solaris.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1055,7 +1055,7 @@
if ((statbuf.st_size == 0) ||
((size_t)statbuf.st_size % os::vm_page_size() != 0)) {
- THROW_MSG_0(vmSymbols::java_lang_Exception(),
+ THROW_MSG_0(vmSymbols::java_io_IOException(),
"Invalid PerfMemory size");
}
--- a/src/hotspot/os/windows/attachListener_windows.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/os/windows/attachListener_windows.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -383,6 +383,12 @@
return Win32AttachListener::init();
}
+// This function is used for Un*x OSes only.
+// We need not to implement it for Windows.
+bool AttachListener::check_socket_file() {
+ return false;
+}
+
bool AttachListener::init_at_startup() {
return true;
}
--- a/src/hotspot/os/windows/perfMemory_windows.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/os/windows/perfMemory_windows.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1561,7 +1561,7 @@
warning("unexpected file size: size = " SIZE_FORMAT "\n",
statbuf.st_size);
}
- THROW_MSG_0(vmSymbols::java_lang_Exception(),
+ THROW_MSG_0(vmSymbols::java_io_IOException(),
"Invalid PerfMemory size");
}
--- a/src/hotspot/share/adlc/formssel.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/adlc/formssel.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, 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
@@ -1513,7 +1513,7 @@
MatchNode *mnode =
strcmp(_matrule->_opType, "Set") ? _matrule : _matrule->_rChild;
- mnode->count_instr_names(names);
+ if (mnode != NULL) mnode->count_instr_names(names);
uint first = 1;
// Start with the predicate supplied in the .ad file.
@@ -1726,26 +1726,25 @@
const char *description = NULL;
const char *value = NULL;
// Check if user provided any opcode definitions
- if( this != NULL ) {
- // Update 'value' if user provided a definition in the instruction
- switch (desired_opcode) {
- case PRIMARY:
- description = "primary()";
- if( _primary != NULL) { value = _primary; }
- break;
- case SECONDARY:
- description = "secondary()";
- if( _secondary != NULL ) { value = _secondary; }
- break;
- case TERTIARY:
- description = "tertiary()";
- if( _tertiary != NULL ) { value = _tertiary; }
- break;
- default:
- assert( false, "ShouldNotReachHere();");
- break;
- }
+ // Update 'value' if user provided a definition in the instruction
+ switch (desired_opcode) {
+ case PRIMARY:
+ description = "primary()";
+ if( _primary != NULL) { value = _primary; }
+ break;
+ case SECONDARY:
+ description = "secondary()";
+ if( _secondary != NULL ) { value = _secondary; }
+ break;
+ case TERTIARY:
+ description = "tertiary()";
+ if( _tertiary != NULL ) { value = _tertiary; }
+ break;
+ default:
+ assert( false, "ShouldNotReachHere();");
+ break;
}
+
if (value != NULL) {
fprintf(fp, "(%s /*%s*/)", value, description);
}
@@ -3413,7 +3412,6 @@
// Count occurrences of operands names in the leaves of the instruction
// match rule.
void MatchNode::count_instr_names( Dict &names ) {
- if( this == NULL ) return;
if( _lChild ) _lChild->count_instr_names(names);
if( _rChild ) _rChild->count_instr_names(names);
if( !_lChild && !_rChild ) {
--- a/src/hotspot/share/adlc/output_c.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/adlc/output_c.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, 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
@@ -2377,7 +2377,7 @@
_processing_noninput = false;
// A replacement variable, originally '$'
if ( Opcode::as_opcode_type(rep_var) != Opcode::NOT_AN_OPCODE ) {
- if (!_inst._opcode->print_opcode(_fp, Opcode::as_opcode_type(rep_var) )) {
+ if ((_inst._opcode == NULL) || !_inst._opcode->print_opcode(_fp, Opcode::as_opcode_type(rep_var) )) {
// Missing opcode
_AD.syntax_err( _inst._linenum,
"Missing $%s opcode definition in %s, used by encoding %s\n",
@@ -2433,7 +2433,7 @@
else if( Opcode::as_opcode_type(inst_rep_var) != Opcode::NOT_AN_OPCODE ) {
// else check if "primary", "secondary", "tertiary"
assert( _constant_status == LITERAL_ACCESSED, "Must be processing a literal constant parameter");
- if (!_inst._opcode->print_opcode(_fp, Opcode::as_opcode_type(inst_rep_var) )) {
+ if ((_inst._opcode == NULL) || !_inst._opcode->print_opcode(_fp, Opcode::as_opcode_type(inst_rep_var) )) {
// Missing opcode
_AD.syntax_err( _inst._linenum,
"Missing $%s opcode definition in %s\n",
--- a/src/hotspot/share/aot/aotCompiledMethod.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/aot/aotCompiledMethod.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -168,7 +168,7 @@
int state() const { return *_state_adr; }
// Non-virtual for speed
- bool _is_alive() const { return state() < zombie; }
+ bool _is_alive() const { return state() < unloaded; }
virtual bool is_zombie() const { return state() == zombie; }
virtual bool is_unloaded() const { return state() == unloaded; }
--- a/src/hotspot/share/asm/assembler.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/asm/assembler.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -218,85 +218,6 @@
}
}
-struct DelayedConstant {
- typedef void (*value_fn_t)();
- BasicType type;
- intptr_t value;
- value_fn_t value_fn;
- // This limit of 20 is generous for initial uses.
- // The limit needs to be large enough to store the field offsets
- // into classes which do not have statically fixed layouts.
- // (Initial use is for method handle object offsets.)
- // Look for uses of "delayed_value" in the source code
- // and make sure this number is generous enough to handle all of them.
- enum { DC_LIMIT = 20 };
- static DelayedConstant delayed_constants[DC_LIMIT];
- static DelayedConstant* add(BasicType type, value_fn_t value_fn);
- bool match(BasicType t, value_fn_t cfn) {
- return type == t && value_fn == cfn;
- }
- static void update_all();
-};
-
-DelayedConstant DelayedConstant::delayed_constants[DC_LIMIT];
-// Default C structure initialization rules have the following effect here:
-// = { { (BasicType)0, (intptr_t)NULL }, ... };
-
-DelayedConstant* DelayedConstant::add(BasicType type,
- DelayedConstant::value_fn_t cfn) {
- for (int i = 0; i < DC_LIMIT; i++) {
- DelayedConstant* dcon = &delayed_constants[i];
- if (dcon->match(type, cfn))
- return dcon;
- if (dcon->value_fn == NULL) {
- dcon->value_fn = cfn;
- dcon->type = type;
- return dcon;
- }
- }
- // If this assert is hit (in pre-integration testing!) then re-evaluate
- // the comment on the definition of DC_LIMIT.
- guarantee(false, "too many delayed constants");
- return NULL;
-}
-
-void DelayedConstant::update_all() {
- for (int i = 0; i < DC_LIMIT; i++) {
- DelayedConstant* dcon = &delayed_constants[i];
- if (dcon->value_fn != NULL && dcon->value == 0) {
- typedef int (*int_fn_t)();
- typedef address (*address_fn_t)();
- switch (dcon->type) {
- case T_INT: dcon->value = (intptr_t) ((int_fn_t) dcon->value_fn)(); break;
- case T_ADDRESS: dcon->value = (intptr_t) ((address_fn_t)dcon->value_fn)(); break;
- default: break;
- }
- }
- }
-}
-
-RegisterOrConstant AbstractAssembler::delayed_value(int(*value_fn)(), Register tmp, int offset) {
- intptr_t val = (intptr_t) (*value_fn)();
- if (val != 0) return val + offset;
- return delayed_value_impl(delayed_value_addr(value_fn), tmp, offset);
-}
-RegisterOrConstant AbstractAssembler::delayed_value(address(*value_fn)(), Register tmp, int offset) {
- intptr_t val = (intptr_t) (*value_fn)();
- if (val != 0) return val + offset;
- return delayed_value_impl(delayed_value_addr(value_fn), tmp, offset);
-}
-intptr_t* AbstractAssembler::delayed_value_addr(int(*value_fn)()) {
- DelayedConstant* dcon = DelayedConstant::add(T_INT, (DelayedConstant::value_fn_t) value_fn);
- return &dcon->value;
-}
-intptr_t* AbstractAssembler::delayed_value_addr(address(*value_fn)()) {
- DelayedConstant* dcon = DelayedConstant::add(T_ADDRESS, (DelayedConstant::value_fn_t) value_fn);
- return &dcon->value;
-}
-void AbstractAssembler::update_delayed_values() {
- DelayedConstant::update_all();
-}
-
void AbstractAssembler::block_comment(const char* comment) {
if (sect() == CodeBuffer::SECT_INSTS) {
code_section()->outer()->block_comment(offset(), comment);
--- a/src/hotspot/share/classfile/javaClasses.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/classfile/javaClasses.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -4547,9 +4547,6 @@
// BASIC_JAVA_CLASSES_DO_PART1 classes (java_lang_String and java_lang_Class)
// earlier inside SystemDictionary::resolve_well_known_classes()
BASIC_JAVA_CLASSES_DO_PART2(DO_COMPUTE_OFFSETS);
-
- // generated interpreter code wants to know about the offsets we just computed:
- AbstractAssembler::update_delayed_values();
}
#if INCLUDE_CDS
--- a/src/hotspot/share/classfile/stringTable.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/classfile/stringTable.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -342,7 +342,7 @@
if (found_string != NULL) {
return found_string;
}
- return do_intern(string_or_null_h, name, len, hash, CHECK_NULL);
+ return do_intern(string_or_null_h, name, len, hash, THREAD);
}
oop StringTable::do_intern(Handle string_or_null_h, const jchar* name,
--- a/src/hotspot/share/classfile/systemDictionary.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/classfile/systemDictionary.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -114,6 +114,7 @@
const int defaultProtectionDomainCacheSize = 1009;
+OopStorage* SystemDictionary::_vm_global_oop_storage = NULL;
OopStorage* SystemDictionary::_vm_weak_oop_storage = NULL;
@@ -1844,7 +1845,7 @@
return unloading_occurred;
}
-void SystemDictionary::oops_do(OopClosure* f) {
+void SystemDictionary::oops_do(OopClosure* f, bool include_handles) {
f->do_oop(&_java_system_loader);
f->do_oop(&_java_platform_loader);
f->do_oop(&_system_loader_lock_obj);
@@ -1852,6 +1853,10 @@
// Visit extra methods
invoke_method_table()->oops_do(f);
+
+ if (include_handles) {
+ vm_global_oop_storage()->oops_do(f);
+ }
}
// CDS: scan and relocate all classes referenced by _well_known_klasses[].
@@ -2893,12 +2898,22 @@
}
void SystemDictionary::initialize_oop_storage() {
+ _vm_global_oop_storage =
+ new OopStorage("VM Global Oop Handles",
+ VMGlobalAlloc_lock,
+ VMGlobalActive_lock);
+
_vm_weak_oop_storage =
new OopStorage("VM Weak Oop Handles",
VMWeakAlloc_lock,
VMWeakActive_lock);
}
+OopStorage* SystemDictionary::vm_global_oop_storage() {
+ assert(_vm_global_oop_storage != NULL, "Uninitialized");
+ return _vm_global_oop_storage;
+}
+
OopStorage* SystemDictionary::vm_weak_oop_storage() {
assert(_vm_weak_oop_storage != NULL, "Uninitialized");
return _vm_weak_oop_storage;
--- a/src/hotspot/share/classfile/systemDictionary.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/classfile/systemDictionary.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -348,7 +348,9 @@
static bool do_unloading(GCTimer* gc_timer);
// Applies "f->do_oop" to all root oops in the system dictionary.
- static void oops_do(OopClosure* f);
+ // If include_handles is true (the default), then the handles in the
+ // storage object returned by vm_global_oop_storage() are included.
+ static void oops_do(OopClosure* f, bool include_handles = true);
// System loader lock
static oop system_loader_lock() { return _system_loader_lock_obj; }
@@ -563,7 +565,8 @@
// ProtectionDomain cache
static ProtectionDomainCacheTable* _pd_cache_table;
- // VM weak OopStorage object.
+ // VM OopStorage objects.
+ static OopStorage* _vm_global_oop_storage;
static OopStorage* _vm_weak_oop_storage;
protected:
@@ -621,6 +624,7 @@
}
static void initialize_oop_storage();
+ static OopStorage* vm_global_oop_storage();
static OopStorage* vm_weak_oop_storage();
protected:
--- a/src/hotspot/share/classfile/vmSymbols.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/classfile/vmSymbols.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -247,9 +247,6 @@
template(clazz_name, "clazz") \
template(exceptionTypes_name, "exceptionTypes") \
template(modifiers_name, "modifiers") \
- template(newConstructor_name, "newConstructor") \
- template(newField_name, "newField") \
- template(newMethod_name, "newMethod") \
template(invokeBasic_name, "invokeBasic") \
template(linkToVirtual_name, "linkToVirtual") \
template(linkToStatic_name, "linkToStatic") \
--- a/src/hotspot/share/code/codeCache.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/code/codeCache.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -771,9 +771,10 @@
uint8_t CodeCache::_unloading_cycle = 1;
void CodeCache::increment_unloading_cycle() {
- if (_unloading_cycle == 1) {
- _unloading_cycle = 2;
- } else {
+ // 2-bit value (see IsUnloadingState in nmethod.cpp for details)
+ // 0 is reserved for new methods.
+ _unloading_cycle = (_unloading_cycle + 1) % 4;
+ if (_unloading_cycle == 0) {
_unloading_cycle = 1;
}
}
--- a/src/hotspot/share/code/compiledMethod.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/code/compiledMethod.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -208,9 +208,9 @@
not_used = 1, // not entrant, but revivable
not_entrant = 2, // marked for deoptimization but activations may still exist,
// will be transformed to zombie when all activations are gone
- zombie = 3, // no activations exist, nmethod is ready for purge
- unloaded = 4 // there should be no activations, should not be called,
- // will be transformed to zombie immediately
+ unloaded = 3, // there should be no activations, should not be called, will be
+ // transformed to zombie by the sweeper, when not "locked in vm".
+ zombie = 4 // no activations exist, nmethod is ready for purge
};
virtual bool is_in_use() const = 0;
--- a/src/hotspot/share/code/nmethod.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/code/nmethod.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1136,6 +1136,20 @@
mdo->inc_decompile_count();
}
+bool nmethod::try_transition(int new_state_int) {
+ signed char new_state = new_state_int;
+ for (;;) {
+ signed char old_state = Atomic::load(&_state);
+ if (old_state >= new_state) {
+ // Ensure monotonicity of transitions.
+ return false;
+ }
+ if (Atomic::cmpxchg(new_state, &_state, old_state) == old_state) {
+ return true;
+ }
+ }
+}
+
void nmethod::make_unloaded() {
post_compiled_method_unload();
@@ -1159,7 +1173,9 @@
}
// Unlink the osr method, so we do not look this up again
if (is_osr_method()) {
- // Invalidate the osr nmethod only once
+ // Invalidate the osr nmethod only once. Note that with concurrent
+ // code cache unloading, OSR nmethods are invalidated before they
+ // are made unloaded. Therefore, this becomes a no-op then.
if (is_in_use()) {
invalidate_osr_method();
}
@@ -1213,12 +1229,14 @@
set_osr_link(NULL);
NMethodSweeper::report_state_change(this);
- // The release is only needed for compile-time ordering, as accesses
- // into the nmethod after the store are not safe due to the sweeper
- // being allowed to free it when the store is observed, during
- // concurrent nmethod unloading. Therefore, there is no need for
- // acquire on the loader side.
- OrderAccess::release_store(&_state, (signed char)unloaded);
+ bool transition_success = try_transition(unloaded);
+
+ // It is an important invariant that there exists no race between
+ // the sweeper and GC thread competing for making the same nmethod
+ // zombie and unloaded respectively. This is ensured by
+ // can_convert_to_zombie() returning false for any is_unloading()
+ // nmethod, informing the sweeper not to step on any GC toes.
+ assert(transition_success, "Invalid nmethod transition to unloaded");
#if INCLUDE_JVMCI
// Clear the link between this nmethod and a HotSpotNmethod mirror
@@ -1283,7 +1301,7 @@
assert(state == zombie || state == not_entrant, "must be zombie or not_entrant");
assert(!is_zombie(), "should not already be a zombie");
- if (_state == state) {
+ if (Atomic::load(&_state) >= state) {
// Avoid taking the lock if already in required state.
// This is safe from races because the state is an end-state,
// which the nmethod cannot back out of once entered.
@@ -1318,7 +1336,7 @@
// Enter critical section. Does not block for safepoint.
MutexLocker pl(Patching_lock, Mutex::_no_safepoint_check_flag);
- if (_state == state) {
+ if (Atomic::load(&_state) >= state) {
// another thread already performed this transition so nothing
// to do, but return false to indicate this.
return false;
@@ -1354,7 +1372,18 @@
}
// Change state
- _state = state;
+ if (!try_transition(state)) {
+ // If the transition fails, it is due to another thread making the nmethod more
+ // dead. In particular, one thread might be making the nmethod unloaded concurrently.
+ // If so, having patched in the jump in the verified entry unnecessarily is fine.
+ // The nmethod is no longer possible to call by Java threads.
+ // Incrementing the decompile count is also fine as the caller of make_not_entrant()
+ // had a valid reason to deoptimize the nmethod.
+ // Marking the nmethod as seen on stack also has no effect, as the nmethod is now
+ // !is_alive(), and the seen on stack value is only used to convert not_entrant
+ // nmethods to zombie in can_convert_to_zombie().
+ return false;
+ }
// Log the transition once
log_state_change();
--- a/src/hotspot/share/code/nmethod.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/code/nmethod.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -212,6 +212,9 @@
void* operator new(size_t size, int nmethod_size, int comp_level) throw();
const char* reloc_string_for(u_char* begin, u_char* end);
+
+ bool try_transition(int new_state);
+
// Returns true if this thread changed the state of the nmethod or
// false if another thread performed the transition.
bool make_not_entrant_or_zombie(int state);
@@ -339,7 +342,7 @@
// flag accessing and manipulation
bool is_not_installed() const { return _state == not_installed; }
bool is_in_use() const { return _state <= in_use; }
- bool is_alive() const { return _state < zombie; }
+ bool is_alive() const { return _state < unloaded; }
bool is_not_entrant() const { return _state == not_entrant; }
bool is_zombie() const { return _state == zombie; }
bool is_unloaded() const { return _state == unloaded; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/g1/g1CardTableEntryClosure.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019, 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_GC_G1_G1CARDTABLEENTRYCLOSURE_HPP
+#define SHARE_GC_G1_G1CARDTABLEENTRYCLOSURE_HPP
+
+#include "gc/shared/cardTable.hpp"
+#include "memory/allocation.hpp"
+
+// A closure class for processing card table entries. Note that we don't
+// require these closure objects to be stack-allocated.
+class G1CardTableEntryClosure: public CHeapObj<mtGC> {
+public:
+ typedef CardTable::CardValue CardValue;
+
+ // Process the card whose card table entry is "card_ptr". If returns
+ // "false", terminate the iteration early.
+ virtual bool do_card_ptr(CardValue* card_ptr, uint worker_id) = 0;
+};
+
+#endif // SHARE_GC_G1_G1CARDTABLEENTRYCLOSURE_HPP
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -31,6 +31,7 @@
#include "gc/g1/g1Allocator.inline.hpp"
#include "gc/g1/g1Arguments.hpp"
#include "gc/g1/g1BarrierSet.hpp"
+#include "gc/g1/g1CardTableEntryClosure.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1CollectionSet.hpp"
#include "gc/g1/g1CollectorState.hpp"
@@ -47,8 +48,10 @@
#include "gc/g1/g1HotCardCache.hpp"
#include "gc/g1/g1MemoryPool.hpp"
#include "gc/g1/g1OopClosures.inline.hpp"
+#include "gc/g1/g1ParallelCleaning.hpp"
#include "gc/g1/g1ParScanThreadState.inline.hpp"
#include "gc/g1/g1Policy.hpp"
+#include "gc/g1/g1RedirtyCardsQueue.hpp"
#include "gc/g1/g1RegionToSpaceMapper.hpp"
#include "gc/g1/g1RemSet.hpp"
#include "gc/g1/g1RootClosures.hpp"
@@ -72,7 +75,6 @@
#include "gc/shared/generationSpec.hpp"
#include "gc/shared/isGCActiveMark.hpp"
#include "gc/shared/oopStorageParState.hpp"
-#include "gc/shared/parallelCleaning.hpp"
#include "gc/shared/preservedMarks.inline.hpp"
#include "gc/shared/suspendibleThreadSet.hpp"
#include "gc/shared/referenceProcessor.inline.hpp"
@@ -1078,7 +1080,9 @@
// Discard all remembered set updates.
G1BarrierSet::dirty_card_queue_set().abandon_logs();
- assert(dirty_card_queue_set().completed_buffers_num() == 0, "DCQS should be empty");
+ assert(G1BarrierSet::dirty_card_queue_set().num_completed_buffers() == 0,
+ "DCQS should be empty");
+ redirty_cards_queue_set().verify_empty();
}
void G1CollectedHeap::verify_after_full_collection() {
@@ -1517,7 +1521,7 @@
_collection_set(this, _policy),
_hot_card_cache(NULL),
_rem_set(NULL),
- _dirty_card_queue_set(false),
+ _redirty_cards_queue_set(),
_cm(NULL),
_cm_thread(NULL),
_cr(NULL),
@@ -1687,8 +1691,8 @@
&bs->dirty_card_queue_buffer_allocator(),
true); // init_free_ids
- dirty_card_queue_set().initialize(DirtyCardQ_CBL_mon,
- &bs->dirty_card_queue_buffer_allocator());
+ // Use same buffer allocator as dirty card qset, to allow merging.
+ _redirty_cards_queue_set.initialize(&bs->dirty_card_queue_buffer_allocator());
// Create the hot card cache.
_hot_card_cache = new G1HotCardCache(this);
@@ -1953,7 +1957,7 @@
while (dcqs.apply_closure_during_gc(cl, worker_i)) {
n_completed_buffers++;
}
- assert(dcqs.completed_buffers_num() == 0, "Completed buffers exist!");
+ assert(dcqs.num_completed_buffers() == 0, "Completed buffers exist!");
phase_times()->record_thread_work_item(G1GCPhaseTimes::MergeLB, worker_i, n_completed_buffers, G1GCPhaseTimes::MergeLBProcessedBuffers);
}
@@ -2609,10 +2613,9 @@
Threads::threads_do(&count_from_threads);
G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set();
- size_t buffer_size = dcqs.buffer_size();
- size_t buffer_num = dcqs.completed_buffers_num();
-
- return buffer_size * buffer_num + count_from_threads._cards;
+ dcqs.verify_num_entries_in_completed_buffers();
+
+ return dcqs.num_entries_in_completed_buffers() + count_from_threads._cards;
}
bool G1CollectedHeap::is_potential_eager_reclaim_candidate(HeapRegion* r) const {
@@ -3162,7 +3165,7 @@
void G1CollectedHeap::complete_cleaning(BoolObjectClosure* is_alive,
bool class_unloading_occurred) {
uint num_workers = workers()->active_workers();
- ParallelCleaningTask unlink_task(is_alive, num_workers, class_unloading_occurred, false);
+ G1ParallelCleaningTask unlink_task(is_alive, num_workers, class_unloading_occurred, false);
workers()->run_task(&unlink_task);
}
@@ -3213,18 +3216,43 @@
class G1RedirtyLoggedCardsTask : public AbstractGangTask {
private:
- G1DirtyCardQueueSet* _queue;
+ G1RedirtyCardsQueueSet* _qset;
G1CollectedHeap* _g1h;
+ BufferNode* volatile _nodes;
+
+ void apply(G1CardTableEntryClosure* cl, BufferNode* node, uint worker_id) {
+ void** buf = BufferNode::make_buffer_from_node(node);
+ size_t limit = _qset->buffer_size();
+ for (size_t i = node->index(); i < limit; ++i) {
+ CardTable::CardValue* card_ptr = static_cast<CardTable::CardValue*>(buf[i]);
+ bool result = cl->do_card_ptr(card_ptr, worker_id);
+ assert(result, "Closure should always return true");
+ }
+ }
+
+ void par_apply(G1CardTableEntryClosure* cl, uint worker_id) {
+ BufferNode* next = Atomic::load(&_nodes);
+ while (next != NULL) {
+ BufferNode* node = next;
+ next = Atomic::cmpxchg(node->next(), &_nodes, node);
+ if (next == node) {
+ apply(cl, node, worker_id);
+ next = node->next();
+ }
+ }
+ }
+
public:
- G1RedirtyLoggedCardsTask(G1DirtyCardQueueSet* queue, G1CollectedHeap* g1h) : AbstractGangTask("Redirty Cards"),
- _queue(queue), _g1h(g1h) { }
+ G1RedirtyLoggedCardsTask(G1RedirtyCardsQueueSet* qset, G1CollectedHeap* g1h) :
+ AbstractGangTask("Redirty Cards"),
+ _qset(qset), _g1h(g1h), _nodes(qset->all_completed_buffers()) { }
virtual void work(uint worker_id) {
G1GCPhaseTimes* p = _g1h->phase_times();
G1GCParPhaseTimesTracker x(p, G1GCPhaseTimes::RedirtyCards, worker_id);
RedirtyLoggedCardTableEntryClosure cl(_g1h);
- _queue->par_apply_closure_to_all_completed_buffers(&cl);
+ par_apply(&cl, worker_id);
p->record_thread_work_item(G1GCPhaseTimes::RedirtyCards, worker_id, cl.num_dirtied());
}
@@ -3233,13 +3261,12 @@
void G1CollectedHeap::redirty_logged_cards() {
double redirty_logged_cards_start = os::elapsedTime();
- G1RedirtyLoggedCardsTask redirty_task(&dirty_card_queue_set(), this);
- dirty_card_queue_set().reset_for_par_iteration();
+ G1RedirtyLoggedCardsTask redirty_task(&redirty_cards_queue_set(), this);
workers()->run_task(&redirty_task);
G1DirtyCardQueueSet& dcq = G1BarrierSet::dirty_card_queue_set();
- dcq.merge_bufferlists(&dirty_card_queue_set());
- assert(dirty_card_queue_set().completed_buffers_num() == 0, "All should be consumed");
+ dcq.merge_bufferlists(&redirty_cards_queue_set());
+ redirty_cards_queue_set().verify_empty();
phase_times()->record_redirty_logged_cards_time_ms((os::elapsedTime() - redirty_logged_cards_start) * 1000.0);
}
@@ -3571,7 +3598,7 @@
// Should G1EvacuationFailureALot be in effect for this GC?
NOT_PRODUCT(set_evacuation_failure_alot_for_current_gc();)
- assert(dirty_card_queue_set().completed_buffers_num() == 0, "Should be empty");
+ redirty_cards_queue_set().verify_empty();
}
class G1EvacuateRegionsBaseTask : public AbstractGangTask {
@@ -3683,7 +3710,7 @@
{
Ticks start = Ticks::now();
- rem_set()->merge_heap_roots(false /* remset_only */, G1GCPhaseTimes::MergeRS);
+ rem_set()->merge_heap_roots(true /* initial_evacuation */);
p->record_merge_heap_roots_time((Ticks::now() - start).seconds() * 1000.0);
}
@@ -3759,7 +3786,7 @@
{
Ticks start = Ticks::now();
- rem_set()->merge_heap_roots(true /* remset_only */, G1GCPhaseTimes::OptMergeRS);
+ rem_set()->merge_heap_roots(false /* initial_evacuation */);
phase_times()->record_or_add_optional_merge_heap_roots_time((Ticks::now() - start).seconds() * 1000.0);
}
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -31,7 +31,6 @@
#include "gc/g1/g1CollectionSet.hpp"
#include "gc/g1/g1CollectorState.hpp"
#include "gc/g1/g1ConcurrentMark.hpp"
-#include "gc/g1/g1DirtyCardQueue.hpp"
#include "gc/g1/g1EdenRegions.hpp"
#include "gc/g1/g1EvacFailure.hpp"
#include "gc/g1/g1EvacStats.hpp"
@@ -42,6 +41,7 @@
#include "gc/g1/g1HRPrinter.hpp"
#include "gc/g1/g1HeapRegionAttr.hpp"
#include "gc/g1/g1MonitoringSupport.hpp"
+#include "gc/g1/g1RedirtyCardsQueue.hpp"
#include "gc/g1/g1SurvivorRegions.hpp"
#include "gc/g1/g1YCTypes.hpp"
#include "gc/g1/heapRegionManager.hpp"
@@ -73,6 +73,7 @@
class SpaceClosure;
class CompactibleSpaceClosure;
class Space;
+class G1CardTableEntryClosure;
class G1CollectionSet;
class G1Policy;
class G1HotCardCache;
@@ -353,6 +354,7 @@
assert(Thread::current()->is_VM_thread(), "current thread is not VM thread"); \
} while (0)
+#ifdef ASSERT
#define assert_used_and_recalculate_used_equal(g1h) \
do { \
size_t cur_used_bytes = g1h->used(); \
@@ -361,6 +363,9 @@
" same as recalculated used(" SIZE_FORMAT ").", \
cur_used_bytes, recal_used_bytes); \
} while (0)
+#else
+#define assert_used_and_recalculate_used_equal(g1h) do {} while(0)
+#endif
const char* young_gc_name() const;
@@ -771,7 +776,7 @@
// A set of cards that cover the objects for which the Rsets should be updated
// concurrently after the collection.
- G1DirtyCardQueueSet _dirty_card_queue_set;
+ G1RedirtyCardsQueueSet _redirty_cards_queue_set;
// After a collection pause, convert the regions in the collection set into free
// regions.
@@ -931,7 +936,9 @@
uint num_task_queues() const;
// A set of cards where updates happened during the GC
- G1DirtyCardQueueSet& dirty_card_queue_set() { return _dirty_card_queue_set; }
+ G1RedirtyCardsQueueSet& redirty_cards_queue_set() {
+ return _redirty_cards_queue_set;
+ }
// Create a G1CollectedHeap.
// Must call the initialize method afterwards.
--- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -397,7 +397,7 @@
dcqs.set_max_completed_buffers(red_zone());
}
- size_t curr_queue_size = dcqs.completed_buffers_num();
+ size_t curr_queue_size = dcqs.num_completed_buffers();
if ((dcqs.max_completed_buffers() > 0) &&
(curr_queue_size >= yellow_zone())) {
dcqs.set_completed_buffers_padding(curr_queue_size);
@@ -430,7 +430,7 @@
bool G1ConcurrentRefine::do_refinement_step(uint worker_id) {
G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set();
- size_t curr_buffer_num = dcqs.completed_buffers_num();
+ size_t curr_buffer_num = dcqs.num_completed_buffers();
// If the number of the buffers falls down into the yellow zone,
// that means that the transition period after the evacuation pause has ended.
// Since the value written to the DCQS is the same for all threads, there is no
--- a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -104,7 +104,7 @@
size_t buffers_processed = 0;
log_debug(gc, refine)("Activated worker %d, on threshold: " SIZE_FORMAT ", current: " SIZE_FORMAT,
_worker_id, _cr->activation_threshold(_worker_id),
- G1BarrierSet::dirty_card_queue_set().completed_buffers_num());
+ G1BarrierSet::dirty_card_queue_set().num_completed_buffers());
{
SuspendibleThreadSetJoiner sts_join;
@@ -126,7 +126,7 @@
log_debug(gc, refine)("Deactivated worker %d, off threshold: " SIZE_FORMAT
", current: " SIZE_FORMAT ", processed: " SIZE_FORMAT,
_worker_id, _cr->deactivation_threshold(_worker_id),
- G1BarrierSet::dirty_card_queue_set().completed_buffers_num(),
+ G1BarrierSet::dirty_card_queue_set().num_completed_buffers(),
buffers_processed);
if (os::supports_vtime()) {
--- a/src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -23,9 +23,11 @@
*/
#include "precompiled.hpp"
+#include "gc/g1/g1CardTableEntryClosure.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1DirtyCardQueue.hpp"
#include "gc/g1/g1FreeIdSet.hpp"
+#include "gc/g1/g1RedirtyCardsQueue.hpp"
#include "gc/g1/g1RemSet.hpp"
#include "gc/g1/g1ThreadLocalData.hpp"
#include "gc/g1/heapRegionRemSet.hpp"
@@ -82,7 +84,7 @@
_cbl_mon(NULL),
_completed_buffers_head(NULL),
_completed_buffers_tail(NULL),
- _n_completed_buffers(0),
+ _num_entries_in_completed_buffers(0),
_process_completed_buffers_threshold(ProcessCompletedBuffersThresholdNever),
_process_completed_buffers(false),
_notify_when_complete(notify_when_complete),
@@ -90,8 +92,7 @@
_completed_buffers_padding(0),
_free_ids(NULL),
_processed_buffers_mut(0),
- _processed_buffers_rs_thread(0),
- _cur_par_buffer_node(NULL)
+ _processed_buffers_rs_thread(0)
{
_all_active = true;
}
@@ -132,42 +133,56 @@
_completed_buffers_tail->set_next(cbn);
_completed_buffers_tail = cbn;
}
- _n_completed_buffers++;
+ _num_entries_in_completed_buffers += buffer_size() - cbn->index();
if (!process_completed_buffers() &&
- (_n_completed_buffers > process_completed_buffers_threshold())) {
+ (num_completed_buffers() > process_completed_buffers_threshold())) {
set_process_completed_buffers(true);
if (_notify_when_complete) {
_cbl_mon->notify_all();
}
}
- assert_completed_buffers_list_len_correct_locked();
+ verify_num_entries_in_completed_buffers();
}
BufferNode* G1DirtyCardQueueSet::get_completed_buffer(size_t stop_at) {
MutexLocker x(_cbl_mon, Mutex::_no_safepoint_check_flag);
- if (_n_completed_buffers <= stop_at) {
+ if (num_completed_buffers() <= stop_at) {
return NULL;
}
- assert(_n_completed_buffers > 0, "invariant");
+ assert(num_completed_buffers() > 0, "invariant");
assert(_completed_buffers_head != NULL, "invariant");
assert(_completed_buffers_tail != NULL, "invariant");
BufferNode* bn = _completed_buffers_head;
- _n_completed_buffers--;
+ _num_entries_in_completed_buffers -= buffer_size() - bn->index();
_completed_buffers_head = bn->next();
if (_completed_buffers_head == NULL) {
- assert(_n_completed_buffers == 0, "invariant");
+ assert(num_completed_buffers() == 0, "invariant");
_completed_buffers_tail = NULL;
set_process_completed_buffers(false);
}
- assert_completed_buffers_list_len_correct_locked();
+ verify_num_entries_in_completed_buffers();
bn->set_next(NULL);
return bn;
}
+#ifdef ASSERT
+void G1DirtyCardQueueSet::verify_num_entries_in_completed_buffers() const {
+ size_t actual = 0;
+ BufferNode* cur = _completed_buffers_head;
+ while (cur != NULL) {
+ actual += buffer_size() - cur->index();
+ cur = cur->next();
+ }
+ assert(actual == _num_entries_in_completed_buffers,
+ "Num entries in completed buffers should be " SIZE_FORMAT " but are " SIZE_FORMAT,
+ _num_entries_in_completed_buffers, actual);
+}
+#endif
+
void G1DirtyCardQueueSet::abandon_completed_buffers() {
BufferNode* buffers_to_delete = NULL;
{
@@ -175,7 +190,7 @@
buffers_to_delete = _completed_buffers_head;
_completed_buffers_head = NULL;
_completed_buffers_tail = NULL;
- _n_completed_buffers = 0;
+ _num_entries_in_completed_buffers = 0;
set_process_completed_buffers(false);
}
while (buffers_to_delete != NULL) {
@@ -188,59 +203,41 @@
void G1DirtyCardQueueSet::notify_if_necessary() {
MutexLocker x(_cbl_mon, Mutex::_no_safepoint_check_flag);
- if (_n_completed_buffers > process_completed_buffers_threshold()) {
+ if (num_completed_buffers() > process_completed_buffers_threshold()) {
set_process_completed_buffers(true);
if (_notify_when_complete)
_cbl_mon->notify();
}
}
-#ifdef ASSERT
-void G1DirtyCardQueueSet::assert_completed_buffers_list_len_correct_locked() {
- assert_lock_strong(_cbl_mon);
- size_t n = 0;
- for (BufferNode* bn = _completed_buffers_head; bn != NULL; bn = bn->next()) {
- ++n;
- }
- assert(n == _n_completed_buffers,
- "Completed buffer length is wrong: counted: " SIZE_FORMAT
- ", expected: " SIZE_FORMAT, n, _n_completed_buffers);
-}
-#endif // ASSERT
-
// Merge lists of buffers. Notify the processing threads.
// The source queue is emptied as a result. The queues
// must share the monitor.
-void G1DirtyCardQueueSet::merge_bufferlists(G1DirtyCardQueueSet *src) {
- assert(_cbl_mon == src->_cbl_mon, "Should share the same lock");
+void G1DirtyCardQueueSet::merge_bufferlists(G1RedirtyCardsQueueSet* src) {
+ assert(allocator() == src->allocator(), "precondition");
+ const G1RedirtyCardsBufferList from = src->take_all_completed_buffers();
+ if (from._head == NULL) return;
+
MutexLocker x(_cbl_mon, Mutex::_no_safepoint_check_flag);
if (_completed_buffers_tail == NULL) {
assert(_completed_buffers_head == NULL, "Well-formedness");
- _completed_buffers_head = src->_completed_buffers_head;
- _completed_buffers_tail = src->_completed_buffers_tail;
+ _completed_buffers_head = from._head;
+ _completed_buffers_tail = from._tail;
} else {
assert(_completed_buffers_head != NULL, "Well formedness");
- if (src->_completed_buffers_head != NULL) {
- _completed_buffers_tail->set_next(src->_completed_buffers_head);
- _completed_buffers_tail = src->_completed_buffers_tail;
- }
+ _completed_buffers_tail->set_next(from._head);
+ _completed_buffers_tail = from._tail;
}
- _n_completed_buffers += src->_n_completed_buffers;
-
- src->_n_completed_buffers = 0;
- src->_completed_buffers_head = NULL;
- src->_completed_buffers_tail = NULL;
- src->set_process_completed_buffers(false);
+ _num_entries_in_completed_buffers += from._entry_count;
assert(_completed_buffers_head == NULL && _completed_buffers_tail == NULL ||
_completed_buffers_head != NULL && _completed_buffers_tail != NULL,
"Sanity");
- assert_completed_buffers_list_len_correct_locked();
+ verify_num_entries_in_completed_buffers();
}
bool G1DirtyCardQueueSet::apply_closure_to_buffer(G1CardTableEntryClosure* cl,
BufferNode* node,
- bool consume,
uint worker_i) {
if (cl == NULL) return true;
bool result = true;
@@ -255,10 +252,8 @@
break;
}
}
- if (consume) {
- assert(i <= buffer_size(), "invariant");
- node->set_index(i);
- }
+ assert(i <= buffer_size(), "invariant");
+ node->set_index(i);
return result;
}
@@ -284,7 +279,7 @@
// add of padding could overflow, which is treated as unlimited.
size_t max_buffers = max_completed_buffers();
size_t limit = max_buffers + completed_buffers_padding();
- if ((completed_buffers_num() > limit) && (limit >= max_buffers)) {
+ if ((num_completed_buffers() > limit) && (limit >= max_buffers)) {
if (mut_process_buffer(node)) {
return true;
}
@@ -299,7 +294,7 @@
uint worker_i = _free_ids->claim_par_id(); // temporarily claim an id
G1RefineCardConcurrentlyClosure cl;
- bool result = apply_closure_to_buffer(&cl, node, true, worker_i);
+ bool result = apply_closure_to_buffer(&cl, node, worker_i);
_free_ids->release_par_id(worker_i); // release the id
if (result) {
@@ -328,7 +323,7 @@
if (nd == NULL) {
return false;
} else {
- if (apply_closure_to_buffer(cl, nd, true, worker_i)) {
+ if (apply_closure_to_buffer(cl, nd, worker_i)) {
assert_fully_consumed(nd, buffer_size());
// Done with fully processed buffer.
deallocate_buffer(nd);
@@ -342,21 +337,6 @@
}
}
-void G1DirtyCardQueueSet::par_apply_closure_to_all_completed_buffers(G1CardTableEntryClosure* cl) {
- BufferNode* nd = _cur_par_buffer_node;
- while (nd != NULL) {
- BufferNode* next = nd->next();
- BufferNode* actual = Atomic::cmpxchg(next, &_cur_par_buffer_node, nd);
- if (actual == nd) {
- bool b = apply_closure_to_buffer(cl, nd, false);
- guarantee(b, "Should not stop early.");
- nd = next;
- } else {
- nd = actual;
- }
- }
-}
-
void G1DirtyCardQueueSet::abandon_logs() {
assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint.");
abandon_completed_buffers();
--- a/src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -25,26 +25,16 @@
#ifndef SHARE_GC_G1_G1DIRTYCARDQUEUE_HPP
#define SHARE_GC_G1_G1DIRTYCARDQUEUE_HPP
-#include "gc/shared/cardTable.hpp"
#include "gc/shared/ptrQueue.hpp"
#include "memory/allocation.hpp"
+class G1CardTableEntryClosure;
class G1DirtyCardQueueSet;
class G1FreeIdSet;
+class G1RedirtyCardsQueueSet;
class Thread;
class Monitor;
-// A closure class for processing card table entries. Note that we don't
-// require these closure objects to be stack-allocated.
-class G1CardTableEntryClosure: public CHeapObj<mtGC> {
-public:
- typedef CardTable::CardValue CardValue;
-
- // Process the card whose card table entry is "card_ptr". If returns
- // "false", terminate the iteration early.
- virtual bool do_card_ptr(CardValue* card_ptr, uint worker_i) = 0;
-};
-
// A ptrQueue whose elements are "oops", pointers to object heads.
class G1DirtyCardQueue: public PtrQueue {
protected:
@@ -79,7 +69,9 @@
Monitor* _cbl_mon; // Protects the fields below.
BufferNode* _completed_buffers_head;
BufferNode* _completed_buffers_tail;
- volatile size_t _n_completed_buffers;
+
+ // Number of actual entries in the list of completed buffers.
+ volatile size_t _num_entries_in_completed_buffers;
size_t _process_completed_buffers_threshold;
volatile bool _process_completed_buffers;
@@ -87,20 +79,18 @@
// If true, notify_all on _cbl_mon when the threshold is reached.
bool _notify_when_complete;
- void assert_completed_buffers_list_len_correct_locked() NOT_DEBUG_RETURN;
-
void abandon_completed_buffers();
// Apply the closure to the elements of "node" from it's index to
// buffer_size. If all closure applications return true, then
// returns true. Stops processing after the first closure
// application that returns false, and returns false from this
- // function. If "consume" is true, the node's index is updated to
- // exclude the processed elements, e.g. up to the element for which
- // the closure returned false.
+ // function. The node's index is updated to exclude the processed
+ // elements, e.g. up to the element for which the closure returned
+ // false, or one past the last element if the closure always
+ // returned true.
bool apply_closure_to_buffer(G1CardTableEntryClosure* cl,
BufferNode* node,
- bool consume,
uint worker_i = 0);
// If there are more than stop_at completed buffers, pop one, apply
@@ -135,9 +125,6 @@
jint _processed_buffers_mut;
jint _processed_buffers_rs_thread;
- // Current buffer node used for parallel iteration.
- BufferNode* volatile _cur_par_buffer_node;
-
public:
G1DirtyCardQueueSet(bool notify_when_complete = true);
~G1DirtyCardQueueSet();
@@ -163,8 +150,17 @@
// return a completed buffer from the list. Otherwise, return NULL.
BufferNode* get_completed_buffer(size_t stop_at = 0);
- // The number of buffers in the list. Racy...
- size_t completed_buffers_num() const { return _n_completed_buffers; }
+ // The number of buffers in the list. Derived as an approximation from the number
+ // of entries in the buffers. Racy.
+ size_t num_completed_buffers() const {
+ return (num_entries_in_completed_buffers() + buffer_size() - 1) / buffer_size();
+ }
+ // The number of entries in completed buffers. Read without synchronization.
+ size_t num_entries_in_completed_buffers() const { return _num_entries_in_completed_buffers; }
+
+ // Verify that _num_entries_in_completed_buffers is equal to the sum of actual entries
+ // in the completed buffers.
+ void verify_num_entries_in_completed_buffers() const NOT_DEBUG_RETURN;
bool process_completed_buffers() { return _process_completed_buffers; }
void set_process_completed_buffers(bool x) { _process_completed_buffers = x; }
@@ -183,7 +179,7 @@
// Notify the consumer if the number of buffers crossed the threshold
void notify_if_necessary();
- void merge_bufferlists(G1DirtyCardQueueSet* src);
+ void merge_bufferlists(G1RedirtyCardsQueueSet* src);
// Apply G1RefineCardConcurrentlyClosure to completed buffers until there are stop_at
// completed buffers remaining.
@@ -193,12 +189,6 @@
// must never return false. Must only be called during GC.
bool apply_closure_during_gc(G1CardTableEntryClosure* cl, uint worker_i);
- void reset_for_par_iteration() { _cur_par_buffer_node = _completed_buffers_head; }
- // Applies the current closure to all completed buffers, non-consumptively.
- // Can be used in parallel, all callers using the iteration state initialized
- // by reset_for_par_iteration.
- void par_apply_closure_to_all_completed_buffers(G1CardTableEntryClosure* cl);
-
// If a full collection is happening, reset partial logs, and release
// completed ones: the full collection will make them all irrelevant.
void abandon_logs();
--- a/src/hotspot/share/gc/g1/g1EvacFailure.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1EvacFailure.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -26,10 +26,10 @@
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1CollectorState.hpp"
#include "gc/g1/g1ConcurrentMark.inline.hpp"
-#include "gc/g1/g1DirtyCardQueue.hpp"
#include "gc/g1/g1EvacFailure.hpp"
#include "gc/g1/g1HeapVerifier.hpp"
#include "gc/g1/g1OopClosures.inline.hpp"
+#include "gc/g1/g1RedirtyCardsQueue.hpp"
#include "gc/g1/heapRegion.hpp"
#include "gc/g1/heapRegionRemSet.hpp"
#include "gc/shared/preservedMarks.inline.hpp"
@@ -40,7 +40,7 @@
class UpdateLogBuffersDeferred : public BasicOopIterateClosure {
private:
G1CollectedHeap* _g1h;
- G1DirtyCardQueue* _dcq;
+ G1RedirtyCardsQueue* _rdcq;
G1CardTable* _ct;
// Remember the last enqueued card to avoid enqueuing the same card over and over;
@@ -48,8 +48,8 @@
size_t _last_enqueued_card;
public:
- UpdateLogBuffersDeferred(G1DirtyCardQueue* dcq) :
- _g1h(G1CollectedHeap::heap()), _dcq(dcq), _ct(_g1h->card_table()), _last_enqueued_card(SIZE_MAX) {}
+ UpdateLogBuffersDeferred(G1RedirtyCardsQueue* rdcq) :
+ _g1h(G1CollectedHeap::heap()), _rdcq(rdcq), _ct(_g1h->card_table()), _last_enqueued_card(SIZE_MAX) {}
virtual void do_oop(narrowOop* p) { do_oop_work(p); }
virtual void do_oop( oop* p) { do_oop_work(p); }
@@ -67,7 +67,7 @@
}
size_t card_index = _ct->index_for(p);
if (card_index != _last_enqueued_card) {
- _dcq->enqueue(_ct->byte_for_index(card_index));
+ _rdcq->enqueue(_ct->byte_for_index(card_index));
_last_enqueued_card = card_index;
}
}
@@ -199,15 +199,15 @@
G1CollectedHeap* _g1h;
uint _worker_id;
- G1DirtyCardQueue _dcq;
+ G1RedirtyCardsQueue _rdcq;
UpdateLogBuffersDeferred _log_buffer_cl;
public:
RemoveSelfForwardPtrHRClosure(uint worker_id) :
_g1h(G1CollectedHeap::heap()),
_worker_id(worker_id),
- _dcq(&_g1h->dirty_card_queue_set()),
- _log_buffer_cl(&_dcq) {
+ _rdcq(&_g1h->redirty_cards_queue_set()),
+ _log_buffer_cl(&_rdcq) {
}
size_t remove_self_forward_ptr_by_walking_hr(HeapRegion* hr,
--- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -61,11 +61,12 @@
_gc_par_phases[CLDGRoots] = new WorkerDataArray<double>(max_gc_threads, "CLDG Roots (ms):");
_gc_par_phases[JVMTIRoots] = new WorkerDataArray<double>(max_gc_threads, "JVMTI Roots (ms):");
AOT_ONLY(_gc_par_phases[AOTCodeRoots] = new WorkerDataArray<double>(max_gc_threads, "AOT Root Scan (ms):");)
- JVMCI_ONLY(_gc_par_phases[JVMCIRoots] = new WorkerDataArray<double>(max_gc_threads, "JVMCI Root Scan (ms):");)
_gc_par_phases[CMRefRoots] = new WorkerDataArray<double>(max_gc_threads, "CM RefProcessor Roots (ms):");
_gc_par_phases[WaitForStrongCLD] = new WorkerDataArray<double>(max_gc_threads, "Wait For Strong CLD (ms):");
_gc_par_phases[WeakCLDRoots] = new WorkerDataArray<double>(max_gc_threads, "Weak CLD Roots (ms):");
+ _gc_par_phases[MergeER] = new WorkerDataArray<double>(max_gc_threads, "Eager Reclaim (ms):");
+
_gc_par_phases[MergeRS] = new WorkerDataArray<double>(max_gc_threads, "Remembered Sets (ms):");
_merge_rs_merged_sparse = new WorkerDataArray<size_t>(max_gc_threads, "Merged Sparse:");
_gc_par_phases[MergeRS]->link_thread_work_items(_merge_rs_merged_sparse, MergeRSMergedSparse);
@@ -85,8 +86,14 @@
_gc_par_phases[MergeLB] = new WorkerDataArray<double>(max_gc_threads, "Log Buffers (ms):");
if (G1HotCardCache::default_use_cache()) {
_gc_par_phases[MergeHCC] = new WorkerDataArray<double>(max_gc_threads, "Hot Card Cache (ms):");
+ _merge_hcc_dirty_cards = new WorkerDataArray<size_t>(max_gc_threads, "Dirty Cards:");
+ _gc_par_phases[MergeHCC]->link_thread_work_items(_merge_hcc_dirty_cards, MergeHCCDirtyCards);
+ _merge_hcc_skipped_cards = new WorkerDataArray<size_t>(max_gc_threads, "Skipped Cards:");
+ _gc_par_phases[MergeHCC]->link_thread_work_items(_merge_hcc_skipped_cards, MergeHCCSkippedCards);
} else {
_gc_par_phases[MergeHCC] = NULL;
+ _merge_hcc_dirty_cards = NULL;
+ _merge_hcc_skipped_cards = NULL;
}
_gc_par_phases[ScanHR] = new WorkerDataArray<double>(max_gc_threads, "Scan Heap Roots (ms):");
_gc_par_phases[OptScanHR] = new WorkerDataArray<double>(max_gc_threads, "Optional Scan Heap Roots (ms):");
@@ -166,6 +173,8 @@
_cur_strong_code_root_purge_time_ms = 0.0;
_cur_merge_heap_roots_time_ms = 0.0;
_cur_optional_merge_heap_roots_time_ms = 0.0;
+ _cur_prepare_merge_heap_roots_time_ms = 0.0;
+ _cur_optional_prepare_merge_heap_roots_time_ms = 0.0;
_cur_evac_fail_recalc_used = 0.0;
_cur_evac_fail_remove_self_forwards = 0.0;
_cur_string_deduplication_time_ms = 0.0;
@@ -248,9 +257,10 @@
// Make sure all slots are uninitialized since this thread did not seem to have been started
ASSERT_PHASE_UNINITIALIZED(GCWorkerEnd);
ASSERT_PHASE_UNINITIALIZED(ExtRootScan);
- ASSERT_PHASE_UNINITIALIZED(MergeHCC);
+ ASSERT_PHASE_UNINITIALIZED(MergeER);
ASSERT_PHASE_UNINITIALIZED(MergeRS);
ASSERT_PHASE_UNINITIALIZED(OptMergeRS);
+ ASSERT_PHASE_UNINITIALIZED(MergeHCC);
ASSERT_PHASE_UNINITIALIZED(MergeLB);
ASSERT_PHASE_UNINITIALIZED(ScanHR);
ASSERT_PHASE_UNINITIALIZED(CodeRoots);
@@ -413,6 +423,8 @@
const double sum_ms = _cur_optional_evac_ms + _cur_optional_merge_heap_roots_time_ms;
if (sum_ms > 0) {
info_time("Merge Optional Heap Roots", _cur_optional_merge_heap_roots_time_ms);
+
+ debug_time("Prepare Optional Merge Heap Roots", _cur_optional_prepare_merge_heap_roots_time_ms);
debug_phase(_gc_par_phases[OptMergeRS]);
info_time("Evacuate Optional Collection Set", _cur_optional_evac_ms);
@@ -427,6 +439,8 @@
double G1GCPhaseTimes::print_evacuate_initial_collection_set() const {
info_time("Merge Heap Roots", _cur_merge_heap_roots_time_ms);
+ debug_time("Prepare Merge Heap Roots", _cur_prepare_merge_heap_roots_time_ms);
+ debug_phase(_gc_par_phases[MergeER]);
debug_phase(_gc_par_phases[MergeRS]);
if (G1HotCardCache::default_use_cache()) {
debug_phase(_gc_par_phases[MergeHCC]);
@@ -554,10 +568,10 @@
"CLDGRoots",
"JVMTIRoots",
AOT_ONLY("AOTCodeRoots" COMMA)
- JVMCI_ONLY("JVMCIRoots" COMMA)
"CMRefRoots",
"WaitForStrongCLD",
"WeakCLDRoots",
+ "MergeER",
"MergeRS",
"OptMergeRS",
"MergeLB",
--- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -56,10 +56,10 @@
CLDGRoots,
JVMTIRoots,
AOT_ONLY(AOTCodeRoots COMMA)
- JVMCI_ONLY(JVMCIRoots COMMA)
CMRefRoots,
WaitForStrongCLD,
WeakCLDRoots,
+ MergeER,
MergeRS,
OptMergeRS,
MergeLB,
@@ -100,6 +100,11 @@
ScanHRUsedMemory
};
+ enum GCMergeHCCWorkItems {
+ MergeHCCDirtyCards,
+ MergeHCCSkippedCards
+ };
+
enum GCMergeLBWorkItems {
MergeLBProcessedBuffers,
MergeLBDirtyCards,
@@ -121,6 +126,9 @@
WorkerDataArray<size_t>* _merge_rs_merged_fine;
WorkerDataArray<size_t>* _merge_rs_merged_coarse;
+ WorkerDataArray<size_t>* _merge_hcc_dirty_cards;
+ WorkerDataArray<size_t>* _merge_hcc_skipped_cards;
+
WorkerDataArray<size_t>* _merge_lb_processed_buffers;
WorkerDataArray<size_t>* _merge_lb_dirty_cards;
WorkerDataArray<size_t>* _merge_lb_skipped_cards;
@@ -164,6 +172,9 @@
double _cur_merge_heap_roots_time_ms;
double _cur_optional_merge_heap_roots_time_ms;
+ double _cur_prepare_merge_heap_roots_time_ms;
+ double _cur_optional_prepare_merge_heap_roots_time_ms;
+
double _cur_prepare_tlab_time_ms;
double _cur_resize_tlab_time_ms;
@@ -308,6 +319,14 @@
_cur_optional_merge_heap_roots_time_ms += ms;
}
+ void record_prepare_merge_heap_roots_time(double ms) {
+ _cur_prepare_merge_heap_roots_time_ms += ms;
+ }
+
+ void record_or_add_optional_prepare_merge_heap_roots_time(double ms) {
+ _cur_optional_prepare_merge_heap_roots_time_ms += ms;
+ }
+
void record_evac_fail_recalc_used_time(double ms) {
_cur_evac_fail_recalc_used = ms;
}
--- a/src/hotspot/share/gc/g1/g1HotCardCache.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1HotCardCache.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -23,6 +23,7 @@
*/
#include "precompiled.hpp"
+#include "gc/g1/g1CardTableEntryClosure.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1DirtyCardQueue.hpp"
#include "gc/g1/g1HotCardCache.hpp"
--- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -43,7 +43,7 @@
size_t optional_cset_length)
: _g1h(g1h),
_refs(g1h->task_queue(worker_id)),
- _dcq(&g1h->dirty_card_queue_set()),
+ _rdcq(&g1h->redirty_cards_queue_set()),
_ct(g1h->card_table()),
_closures(NULL),
_plab_allocator(NULL),
@@ -88,7 +88,7 @@
// Pass locally gathered statistics to global state.
void G1ParScanThreadState::flush(size_t* surviving_young_words) {
- _dcq.flush();
+ _rdcq.flush();
// Update allocation statistics.
_plab_allocator->flush_and_retire_stats();
_g1h->policy()->record_age_table(&_age_table);
--- a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -27,7 +27,7 @@
#include "gc/g1/g1CardTable.hpp"
#include "gc/g1/g1CollectedHeap.hpp"
-#include "gc/g1/g1DirtyCardQueue.hpp"
+#include "gc/g1/g1RedirtyCardsQueue.hpp"
#include "gc/g1/g1OopClosures.hpp"
#include "gc/g1/g1Policy.hpp"
#include "gc/g1/g1RemSet.hpp"
@@ -46,7 +46,7 @@
class G1ParScanThreadState : public CHeapObj<mtGC> {
G1CollectedHeap* _g1h;
RefToScanQueue* _refs;
- G1DirtyCardQueue _dcq;
+ G1RedirtyCardsQueue _rdcq;
G1CardTable* _ct;
G1EvacuationRootClosures* _closures;
@@ -81,7 +81,7 @@
#define PADDING_ELEM_NUM (DEFAULT_CACHE_LINE_SIZE / sizeof(size_t))
- G1DirtyCardQueue& dirty_card_queue() { return _dcq; }
+ G1RedirtyCardsQueue& redirty_cards_queue() { return _rdcq; }
G1CardTable* ct() { return _ct; }
G1HeapRegionAttr dest(G1HeapRegionAttr original) const {
@@ -133,7 +133,7 @@
size_t card_index = ct()->index_for(p);
// If the card hasn't been added to the buffer, do it.
if (_last_enqueued_card != card_index) {
- dirty_card_queue().enqueue(ct()->byte_for_index(card_index));
+ redirty_cards_queue().enqueue(ct()->byte_for_index(card_index));
_last_enqueued_card = card_index;
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/g1/g1ParallelCleaning.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2019, 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 "gc/g1/g1ParallelCleaning.hpp"
+#if INCLUDE_JVMCI
+#include "jvmci/jvmci.hpp"
+#endif
+
+#if INCLUDE_JVMCI
+JVMCICleaningTask::JVMCICleaningTask() :
+ _cleaning_claimed(0) {
+}
+
+bool JVMCICleaningTask::claim_cleaning_task() {
+ if (_cleaning_claimed) {
+ return false;
+ }
+
+ return Atomic::cmpxchg(1, &_cleaning_claimed, 0) == 0;
+}
+
+void JVMCICleaningTask::work(bool unloading_occurred) {
+ // One worker will clean JVMCI metadata handles.
+ if (unloading_occurred && EnableJVMCI && claim_cleaning_task()) {
+ JVMCI::do_unloading(unloading_occurred);
+ }
+}
+#endif // INCLUDE_JVMCI
+
+G1ParallelCleaningTask::G1ParallelCleaningTask(BoolObjectClosure* is_alive,
+ uint num_workers,
+ bool unloading_occurred,
+ bool resize_dedup_table) :
+ AbstractGangTask("G1 Parallel Cleaning"),
+ _unloading_occurred(unloading_occurred),
+ _string_dedup_task(is_alive, NULL, resize_dedup_table),
+ _code_cache_task(num_workers, is_alive, unloading_occurred),
+ JVMCI_ONLY(_jvmci_cleaning_task() COMMA)
+ _klass_cleaning_task() {
+}
+
+// The parallel work done by all worker threads.
+void G1ParallelCleaningTask::work(uint worker_id) {
+ // Clean JVMCI metadata handles.
+ // Execute this task first because it is serial task.
+ JVMCI_ONLY(_jvmci_cleaning_task.work(_unloading_occurred);)
+
+ // Do first pass of code cache cleaning.
+ _code_cache_task.work(worker_id);
+
+ // Clean the string dedup data structures.
+ _string_dedup_task.work(worker_id);
+
+ // Clean all klasses that were not unloaded.
+ // The weak metadata in klass doesn't need to be
+ // processed if there was no unloading.
+ if (_unloading_occurred) {
+ _klass_cleaning_task.work();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/g1/g1ParallelCleaning.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019, 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_GC_G1_G1PARALLELCLEANING_HPP
+#define SHARE_GC_G1_G1PARALLELCLEANING_HPP
+
+#include "gc/shared/parallelCleaning.hpp"
+
+#if INCLUDE_JVMCI
+class JVMCICleaningTask : public StackObj {
+ volatile int _cleaning_claimed;
+
+public:
+ JVMCICleaningTask();
+ // Clean JVMCI metadata handles.
+ void work(bool unloading_occurred);
+
+private:
+ bool claim_cleaning_task();
+};
+#endif
+
+// Do cleanup of some weakly held data in the same parallel task.
+// Assumes a non-moving context.
+class G1ParallelCleaningTask : public AbstractGangTask {
+private:
+ bool _unloading_occurred;
+ StringDedupCleaningTask _string_dedup_task;
+ CodeCacheUnloadingTask _code_cache_task;
+#if INCLUDE_JVMCI
+ JVMCICleaningTask _jvmci_cleaning_task;
+#endif
+ KlassCleaningTask _klass_cleaning_task;
+
+public:
+ // The constructor is run in the VMThread.
+ G1ParallelCleaningTask(BoolObjectClosure* is_alive,
+ uint num_workers,
+ bool unloading_occurred,
+ bool resize_dedup_table);
+
+ void work(uint worker_id);
+};
+
+#endif // SHARE_GC_G1_G1PARALLELCLEANING_HPP
--- a/src/hotspot/share/gc/g1/g1Policy.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1Policy.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -684,7 +684,9 @@
if (remset_cards_scanned > 10) {
double avg_time_remset_scan = ((average_time_ms(G1GCPhaseTimes::ScanHR) + average_time_ms(G1GCPhaseTimes::OptScanHR)) *
remset_cards_scanned / total_cards_scanned) +
- average_time_ms(G1GCPhaseTimes::MergeRS);
+ average_time_ms(G1GCPhaseTimes::MergeER) +
+ average_time_ms(G1GCPhaseTimes::MergeRS) +
+ average_time_ms(G1GCPhaseTimes::OptMergeRS);
cost_per_remset_card_ms = avg_time_remset_scan / remset_cards_scanned;
_analytics->report_cost_per_remset_card_ms(cost_per_remset_card_ms, this_pause_was_young_only);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/g1/g1RedirtyCardsQueue.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2019, 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 "gc/g1/g1RedirtyCardsQueue.hpp"
+#include "runtime/atomic.hpp"
+#include "utilities/debug.hpp"
+#include "utilities/macros.hpp"
+
+// G1RedirtyCardsBufferList
+
+G1RedirtyCardsBufferList::G1RedirtyCardsBufferList() :
+ _head(NULL), _tail(NULL), _entry_count(0) {}
+
+G1RedirtyCardsBufferList::G1RedirtyCardsBufferList(BufferNode* head,
+ BufferNode* tail,
+ size_t entry_count) :
+ _head(head), _tail(tail), _entry_count(entry_count)
+{
+ assert((_head == NULL) == (_tail == NULL), "invariant");
+ assert((_head == NULL) == (_entry_count == 0), "invariant");
+}
+
+// G1RedirtyCardsQueueBase::LocalQSet
+
+G1RedirtyCardsQueueBase::LocalQSet::LocalQSet(G1RedirtyCardsQueueSet* shared_qset) :
+ PtrQueueSet(),
+ _shared_qset(shared_qset),
+ _buffers()
+{
+ PtrQueueSet::initialize(_shared_qset->allocator());
+}
+
+G1RedirtyCardsQueueBase::LocalQSet::~LocalQSet() {
+ assert(_buffers._head == NULL, "unflushed qset");
+ assert(_buffers._tail == NULL, "invariant");
+ assert(_buffers._entry_count == 0, "invariant");
+}
+
+void G1RedirtyCardsQueueBase::LocalQSet::enqueue_completed_buffer(BufferNode* node) {
+ _buffers._entry_count += buffer_size() - node->index();
+ node->set_next(_buffers._head);
+ _buffers._head = node;
+ if (_buffers._tail == NULL) {
+ _buffers._tail = node;
+ }
+}
+
+G1RedirtyCardsBufferList G1RedirtyCardsQueueBase::LocalQSet::take_all_completed_buffers() {
+ G1RedirtyCardsBufferList result = _buffers;
+ _buffers = G1RedirtyCardsBufferList();
+ return result;
+}
+
+void G1RedirtyCardsQueueBase::LocalQSet::flush() {
+ _shared_qset->merge_bufferlist(this);
+}
+
+// G1RedirtyCardsQueue
+
+G1RedirtyCardsQueue::G1RedirtyCardsQueue(G1RedirtyCardsQueueSet* qset) :
+ G1RedirtyCardsQueueBase(qset), // Init _local_qset before passing to PtrQueue.
+ PtrQueue(&_local_qset, true /* active (always) */)
+{}
+
+G1RedirtyCardsQueue::~G1RedirtyCardsQueue() {
+ flush();
+}
+
+void G1RedirtyCardsQueue::handle_completed_buffer() {
+ enqueue_completed_buffer();
+}
+
+void G1RedirtyCardsQueue::flush() {
+ flush_impl();
+ _local_qset.flush();
+}
+
+// G1RedirtyCardsQueueSet
+
+G1RedirtyCardsQueueSet::G1RedirtyCardsQueueSet() :
+ PtrQueueSet(),
+ _list(),
+ _entry_count(0),
+ _tail(NULL)
+ DEBUG_ONLY(COMMA _collecting(true))
+{}
+
+G1RedirtyCardsQueueSet::~G1RedirtyCardsQueueSet() {
+ verify_empty();
+}
+
+#ifdef ASSERT
+void G1RedirtyCardsQueueSet::verify_empty() const {
+ assert(_list.empty(), "precondition");
+ assert(_tail == NULL, "invariant");
+ assert(_entry_count == 0, "invariant");
+}
+#endif // ASSERT
+
+BufferNode* G1RedirtyCardsQueueSet::all_completed_buffers() const {
+ DEBUG_ONLY(_collecting = false;)
+ return _list.top();
+}
+
+G1RedirtyCardsBufferList G1RedirtyCardsQueueSet::take_all_completed_buffers() {
+ DEBUG_ONLY(_collecting = false;)
+ G1RedirtyCardsBufferList result(_list.pop_all(), _tail, _entry_count);
+ _tail = NULL;
+ _entry_count = 0;
+ DEBUG_ONLY(_collecting = true;)
+ return result;
+}
+
+void G1RedirtyCardsQueueSet::update_tail(BufferNode* node) {
+ // Node is the tail of a (possibly single element) list just prepended to
+ // _list. If, after that prepend, node's follower is NULL, then node is
+ // also the tail of _list, so record it as such.
+ if (node->next() == NULL) {
+ assert(_tail == NULL, "invariant");
+ _tail = node;
+ }
+}
+
+void G1RedirtyCardsQueueSet::enqueue_completed_buffer(BufferNode* node) {
+ assert(_collecting, "precondition");
+ Atomic::add(buffer_size() - node->index(), &_entry_count);
+ _list.push(*node);
+ update_tail(node);
+}
+
+void G1RedirtyCardsQueueSet::merge_bufferlist(LocalQSet* src) {
+ assert(_collecting, "precondition");
+ const G1RedirtyCardsBufferList from = src->take_all_completed_buffers();
+ if (from._head != NULL) {
+ assert(from._tail != NULL, "invariant");
+ Atomic::add(from._entry_count, &_entry_count);
+ _list.prepend(*from._head, *from._tail);
+ update_tail(from._tail);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/g1/g1RedirtyCardsQueue.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2019, 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_GC_G1_G1REDIRTYCARDSQUEUE_HPP
+#define SHARE_GC_G1_G1REDIRTYCARDSQUEUE_HPP
+
+#include "gc/shared/ptrQueue.hpp"
+#include "memory/allocation.hpp"
+#include "memory/padded.hpp"
+
+class G1CardTableEntryClosure;
+class G1RedirtyCardsQueue;
+class G1RedirtyCardsQueueSet;
+
+struct G1RedirtyCardsBufferList {
+ BufferNode* _head;
+ BufferNode* _tail;
+ size_t _entry_count;
+
+ G1RedirtyCardsBufferList();
+ G1RedirtyCardsBufferList(BufferNode* head, BufferNode* tail, size_t entry_count);
+};
+
+// Provide G1RedirtyCardsQueue with a thread-local qset. It provides an
+// uncontended staging area for completed buffers, to be flushed to the
+// shared qset en masse. Using the "base from member" idiom so the local
+// qset is constructed before being passed to the PtrQueue constructor.
+class G1RedirtyCardsQueueBase {
+ friend class G1RedirtyCardsQueue;
+ friend class G1RedirtyCardsQueueSet;
+
+ class LocalQSet : public PtrQueueSet {
+ G1RedirtyCardsQueueSet* _shared_qset;
+ G1RedirtyCardsBufferList _buffers;
+
+ public:
+ LocalQSet(G1RedirtyCardsQueueSet* shared_qset);
+ ~LocalQSet();
+
+ // Add the buffer to the local list.
+ virtual void enqueue_completed_buffer(BufferNode* node);
+
+ // Transfer all completed buffers to the shared qset.
+ void flush();
+
+ G1RedirtyCardsBufferList take_all_completed_buffers();
+ };
+
+ G1RedirtyCardsQueueBase(G1RedirtyCardsQueueSet* shared_qset) :
+ _local_qset(shared_qset) {}
+
+ ~G1RedirtyCardsQueueBase() {}
+
+ LocalQSet _local_qset;
+};
+
+// Worker-local queues of card table entries.
+class G1RedirtyCardsQueue : private G1RedirtyCardsQueueBase, public PtrQueue {
+protected:
+ virtual void handle_completed_buffer();
+
+public:
+ G1RedirtyCardsQueue(G1RedirtyCardsQueueSet* qset);
+
+ // Flushes the queue.
+ ~G1RedirtyCardsQueue();
+
+ // Flushes all enqueued cards to qset.
+ void flush();
+};
+
+// Card table entries to be redirtied and the cards reprocessed later.
+// Has two phases, collecting and processing. During the collecting
+// phase buffers are added to the set. Once collecting is complete and
+// processing starts, buffers can no longer be added. Taking all the
+// collected (and processed) buffers reverts back to collecting, allowing
+// the set to be reused for another round of redirtying.
+class G1RedirtyCardsQueueSet : public PtrQueueSet {
+ DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, 0);
+ BufferNode::Stack _list;
+ DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, sizeof(size_t));
+ volatile size_t _entry_count;
+ DEFINE_PAD_MINUS_SIZE(3, DEFAULT_CACHE_LINE_SIZE, sizeof(BufferNode*));
+ BufferNode* _tail;
+ DEBUG_ONLY(mutable bool _collecting;)
+
+ typedef G1RedirtyCardsQueueBase::LocalQSet LocalQSet;
+
+ void update_tail(BufferNode* node);
+
+public:
+ G1RedirtyCardsQueueSet();
+ ~G1RedirtyCardsQueueSet();
+
+ using PtrQueueSet::initialize;
+
+ void verify_empty() const NOT_DEBUG_RETURN;
+
+ // Collect buffers. These functions are thread-safe.
+ // precondition: Must not be concurrent with buffer processing.
+ virtual void enqueue_completed_buffer(BufferNode* node);
+ void merge_bufferlist(LocalQSet* src);
+
+ // Processing phase operations.
+ // precondition: Must not be concurrent with buffer collection.
+ BufferNode* all_completed_buffers() const;
+ G1RedirtyCardsBufferList take_all_completed_buffers();
+};
+
+#endif // SHARE_GC_G1_G1REDIRTYCARDSQUEUE_HPP
--- a/src/hotspot/share/gc/g1/g1RemSet.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1RemSet.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -26,6 +26,7 @@
#include "gc/g1/g1BarrierSet.hpp"
#include "gc/g1/g1BlockOffsetTable.inline.hpp"
#include "gc/g1/g1CardTable.inline.hpp"
+#include "gc/g1/g1CardTableEntryClosure.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1ConcurrentRefine.hpp"
#include "gc/g1/g1DirtyCardQueue.hpp"
@@ -93,24 +94,35 @@
// to (>=) HeapRegion::CardsPerRegion (completely scanned).
uint volatile* _card_table_scan_state;
- // Random power of two number of cards we want to claim per thread. This corresponds
- // to a 64k of memory work chunk area for every thread.
- // We use the same claim size as Parallel GC. No particular measurements have been
- // performed to determine an optimal number.
- static const uint CardsPerChunk = 128;
+ // Return "optimal" number of chunks per region we want to use for claiming areas
+ // within a region to claim. Dependent on the region size as proxy for the heap
+ // size, we limit the total number of chunks to limit memory usage and maintenance
+ // effort of that table vs. granularity of distributing scanning work.
+ // Testing showed that 8 for 1M/2M region, 16 for 4M/8M regions, 32 for 16/32M regions
+ // seems to be such a good trade-off.
+ static uint get_chunks_per_region(uint log_region_size) {
+ // Limit the expected input values to current known possible values of the
+ // (log) region size. Adjust as necessary after testing if changing the permissible
+ // values for region size.
+ assert(log_region_size >= 20 && log_region_size <= 25,
+ "expected value in [20,25], but got %u", log_region_size);
+ return 1u << (log_region_size / 2 - 7);
+ }
- uint _scan_chunks_per_region;
+ uint _scan_chunks_per_region; // Number of chunks per region.
+ uint8_t _log_scan_chunks_per_region; // Log of number of chunks per region.
bool* _region_scan_chunks;
- uint8_t _scan_chunks_shift;
+ size_t _num_total_scan_chunks; // Total number of elements in _region_scan_chunks.
+ uint8_t _scan_chunks_shift; // For conversion between card index and chunk index.
public:
uint scan_chunk_size() const { return (uint)1 << _scan_chunks_shift; }
// Returns whether the chunk corresponding to the given region/card in region contain a
// dirty card, i.e. actually needs scanning.
bool chunk_needs_scan(uint const region_idx, uint const card_in_region) const {
- size_t const idx = (size_t)region_idx * _scan_chunks_per_region + (card_in_region >> _scan_chunks_shift);
- assert(idx < (_max_regions * _scan_chunks_per_region), "Index " SIZE_FORMAT " out of bounds " SIZE_FORMAT,
- idx, _max_regions * _scan_chunks_per_region);
+ size_t const idx = ((size_t)region_idx << _log_scan_chunks_per_region) + (card_in_region >> _scan_chunks_shift);
+ assert(idx < _num_total_scan_chunks, "Index " SIZE_FORMAT " out of bounds " SIZE_FORMAT,
+ idx, _num_total_scan_chunks);
return _region_scan_chunks[idx];
}
@@ -183,259 +195,6 @@
}
};
- // Returns whether the given region contains cards we need to scan. The remembered
- // set and other sources may contain cards that
- // - are in uncommitted regions
- // - are located in the collection set
- // - are located in free regions
- // as we do not clean up remembered sets before merging heap roots.
- bool contains_cards_to_process(uint const region_idx) const {
- HeapRegion* hr = G1CollectedHeap::heap()->region_at_or_null(region_idx);
- return (hr != NULL && !hr->in_collection_set() && hr->is_old_or_humongous_or_archive());
- }
-
- class G1MergeCardSetClosure : public HeapRegionClosure {
- G1RemSetScanState* _scan_state;
- G1CardTable* _ct;
-
- uint _merged_sparse;
- uint _merged_fine;
- uint _merged_coarse;
-
- // Returns if the region contains cards we need to scan. If so, remember that
- // region in the current set of dirty regions.
- bool remember_if_interesting(uint const region_idx) {
- if (!_scan_state->contains_cards_to_process(region_idx)) {
- return false;
- }
- _scan_state->add_dirty_region(region_idx);
- return true;
- }
- public:
- G1MergeCardSetClosure(G1RemSetScanState* scan_state) :
- _scan_state(scan_state),
- _ct(G1CollectedHeap::heap()->card_table()),
- _merged_sparse(0),
- _merged_fine(0),
- _merged_coarse(0) { }
-
- void next_coarse_prt(uint const region_idx) {
- if (!remember_if_interesting(region_idx)) {
- return;
- }
-
- _merged_coarse++;
-
- size_t region_base_idx = (size_t)region_idx << HeapRegion::LogCardsPerRegion;
- _ct->mark_region_dirty(region_base_idx, HeapRegion::CardsPerRegion);
- _scan_state->set_chunk_region_dirty(region_base_idx);
- }
-
- void next_fine_prt(uint const region_idx, BitMap* bm) {
- if (!remember_if_interesting(region_idx)) {
- return;
- }
-
- _merged_fine++;
-
- size_t const region_base_idx = (size_t)region_idx << HeapRegion::LogCardsPerRegion;
- BitMap::idx_t cur = bm->get_next_one_offset(0);
- while (cur != bm->size()) {
- _ct->mark_clean_as_dirty(region_base_idx + cur);
- _scan_state->set_chunk_dirty(region_base_idx + cur);
- cur = bm->get_next_one_offset(cur + 1);
- }
- }
-
- void next_sparse_prt(uint const region_idx, SparsePRTEntry::card_elem_t* cards, uint const num_cards) {
- if (!remember_if_interesting(region_idx)) {
- return;
- }
-
- _merged_sparse++;
-
- size_t const region_base_idx = (size_t)region_idx << HeapRegion::LogCardsPerRegion;
- for (uint i = 0; i < num_cards; i++) {
- size_t card_idx = region_base_idx + cards[i];
- _ct->mark_clean_as_dirty(card_idx);
- _scan_state->set_chunk_dirty(card_idx);
- }
- }
-
- virtual bool do_heap_region(HeapRegion* r) {
- assert(r->in_collection_set() || r->is_starts_humongous(), "must be");
-
- HeapRegionRemSet* rem_set = r->rem_set();
- if (!rem_set->is_empty()) {
- rem_set->iterate_prts(*this);
- }
-
- return false;
- }
-
- size_t merged_sparse() const { return _merged_sparse; }
- size_t merged_fine() const { return _merged_fine; }
- size_t merged_coarse() const { return _merged_coarse; }
- };
-
- // Visitor for the remembered sets of humongous candidate regions to merge their
- // remembered set into the card table.
- class G1FlushHumongousCandidateRemSets : public HeapRegionClosure {
- G1MergeCardSetClosure _cl;
-
- public:
- G1FlushHumongousCandidateRemSets(G1RemSetScanState* scan_state) : _cl(scan_state) { }
-
- virtual bool do_heap_region(HeapRegion* r) {
- G1CollectedHeap* g1h = G1CollectedHeap::heap();
-
- if (!r->is_starts_humongous() ||
- !g1h->region_attr(r->hrm_index()).is_humongous() ||
- r->rem_set()->is_empty()) {
- return false;
- }
-
- guarantee(r->rem_set()->occupancy_less_or_equal_than(G1RSetSparseRegionEntries),
- "Found a not-small remembered set here. This is inconsistent with previous assumptions.");
-
- _cl.do_heap_region(r);
-
- // We should only clear the card based remembered set here as we will not
- // implicitly rebuild anything else during eager reclaim. Note that at the moment
- // (and probably never) we do not enter this path if there are other kind of
- // remembered sets for this region.
- r->rem_set()->clear_locked(true /* only_cardset */);
- // Clear_locked() above sets the state to Empty. However we want to continue
- // collecting remembered set entries for humongous regions that were not
- // reclaimed.
- r->rem_set()->set_state_complete();
-#ifdef ASSERT
- G1HeapRegionAttr region_attr = g1h->region_attr(r->hrm_index());
- assert(region_attr.needs_remset_update(), "must be");
-#endif
- assert(r->rem_set()->is_empty(), "At this point any humongous candidate remembered set must be empty.");
-
- return false;
- }
-
- size_t merged_sparse() const { return _cl.merged_sparse(); }
- size_t merged_fine() const { return _cl.merged_fine(); }
- size_t merged_coarse() const { return _cl.merged_coarse(); }
- };
-
- // Visitor for the log buffer entries to merge them into the card table.
- class G1MergeLogBufferCardsClosure : public G1CardTableEntryClosure {
- G1RemSetScanState* _scan_state;
- G1CardTable* _ct;
-
- size_t _cards_dirty;
- size_t _cards_skipped;
- public:
- G1MergeLogBufferCardsClosure(G1CollectedHeap* g1h, G1RemSetScanState* scan_state) :
- _scan_state(scan_state), _ct(g1h->card_table()), _cards_dirty(0), _cards_skipped(0)
- {}
-
- bool do_card_ptr(CardValue* card_ptr, uint worker_i) {
- // The only time we care about recording cards that
- // contain references that point into the collection set
- // is during RSet updating within an evacuation pause.
- // In this case worker_id should be the id of a GC worker thread.
- assert(SafepointSynchronize::is_at_safepoint(), "not during an evacuation pause");
-
- uint const region_idx = _ct->region_idx_for(card_ptr);
-
- // The second clause must come after - the log buffers might contain cards to uncommited
- // regions.
- // This code may count duplicate entries in the log buffers (even if rare) multiple
- // times.
- if (_scan_state->contains_cards_to_process(region_idx) && (*card_ptr == G1CardTable::dirty_card_val())) {
- _scan_state->add_dirty_region(region_idx);
- _scan_state->set_chunk_dirty(_ct->index_for_cardvalue(card_ptr));
- _cards_dirty++;
- } else {
- // We may have had dirty cards in the (initial) collection set (or the
- // young regions which are always in the initial collection set). We do
- // not fix their cards here: we already added these regions to the set of
- // regions to clear the card table at the end during the prepare() phase.
- _cards_skipped++;
- }
- return true;
- }
-
- size_t cards_dirty() const { return _cards_dirty; }
- size_t cards_skipped() const { return _cards_skipped; }
- };
-
- class G1MergeHeapRootsTask : public AbstractGangTask {
- HeapRegionClaimer _hr_claimer;
- G1RemSetScanState* _scan_state;
- bool _remembered_set_only;
-
- G1GCPhaseTimes::GCParPhases _merge_phase;
-
- volatile bool _fast_reclaim_handled;
-
- public:
- G1MergeHeapRootsTask(G1RemSetScanState* scan_state, uint num_workers, bool remembered_set_only, G1GCPhaseTimes::GCParPhases merge_phase) :
- AbstractGangTask("G1 Merge Heap Roots"),
- _hr_claimer(num_workers),
- _scan_state(scan_state),
- _remembered_set_only(remembered_set_only),
- _merge_phase(merge_phase),
- _fast_reclaim_handled(false) { }
-
- virtual void work(uint worker_id) {
- G1CollectedHeap* g1h = G1CollectedHeap::heap();
- G1GCPhaseTimes* p = g1h->phase_times();
-
- // We schedule flushing the remembered sets of humongous fast reclaim candidates
- // onto the card table first to allow the remaining parallelized tasks hide it.
- if (!_remembered_set_only &&
- p->fast_reclaim_humongous_candidates() > 0 &&
- !_fast_reclaim_handled &&
- !Atomic::cmpxchg(true, &_fast_reclaim_handled, false)) {
-
- G1FlushHumongousCandidateRemSets cl(_scan_state);
- g1h->heap_region_iterate(&cl);
-
- p->record_or_add_thread_work_item(_merge_phase, worker_id, cl.merged_sparse(), G1GCPhaseTimes::MergeRSMergedSparse);
- p->record_or_add_thread_work_item(_merge_phase, worker_id, cl.merged_fine(), G1GCPhaseTimes::MergeRSMergedFine);
- p->record_or_add_thread_work_item(_merge_phase, worker_id, cl.merged_coarse(), G1GCPhaseTimes::MergeRSMergedCoarse);
- }
-
- // Merge remembered sets of current candidates.
- {
- G1GCParPhaseTimesTracker x(p, _merge_phase, worker_id, !_remembered_set_only /* must_record */);
- G1MergeCardSetClosure cl(_scan_state);
- g1h->collection_set_iterate_increment_from(&cl, &_hr_claimer, worker_id);
-
- p->record_or_add_thread_work_item(_merge_phase, worker_id, cl.merged_sparse(), G1GCPhaseTimes::MergeRSMergedSparse);
- p->record_or_add_thread_work_item(_merge_phase, worker_id, cl.merged_fine(), G1GCPhaseTimes::MergeRSMergedFine);
- p->record_or_add_thread_work_item(_merge_phase, worker_id, cl.merged_coarse(), G1GCPhaseTimes::MergeRSMergedCoarse);
- }
-
- // Apply closure to log entries in the HCC.
- if (!_remembered_set_only && G1HotCardCache::default_use_cache()) {
- assert(_merge_phase == G1GCPhaseTimes::MergeRS, "Wrong merge phase");
- G1GCParPhaseTimesTracker x(p, G1GCPhaseTimes::MergeHCC, worker_id);
- G1MergeLogBufferCardsClosure cl(g1h, _scan_state);
- g1h->iterate_hcc_closure(&cl, worker_id);
- }
-
- // Now apply the closure to all remaining log entries.
- if (!_remembered_set_only) {
- assert(_merge_phase == G1GCPhaseTimes::MergeRS, "Wrong merge phase");
- G1GCParPhaseTimesTracker x(p, G1GCPhaseTimes::MergeLB, worker_id);
-
- G1MergeLogBufferCardsClosure cl(g1h, _scan_state);
- g1h->iterate_dirty_card_closure(&cl, worker_id);
-
- p->record_thread_work_item(G1GCPhaseTimes::MergeLB, worker_id, cl.cards_dirty(), G1GCPhaseTimes::MergeLBDirtyCards);
- p->record_thread_work_item(G1GCPhaseTimes::MergeLB, worker_id, cl.cards_skipped(), G1GCPhaseTimes::MergeLBSkippedCards);
- }
- }
- };
-
// Creates a snapshot of the current _top values at the start of collection to
// filter out card marks that we do not want to scan.
class G1ResetScanTopClosure : public HeapRegionClosure {
@@ -538,8 +297,10 @@
_max_regions(0),
_collection_set_iter_state(NULL),
_card_table_scan_state(NULL),
- _scan_chunks_per_region((uint)(HeapRegion::CardsPerRegion / CardsPerChunk)),
+ _scan_chunks_per_region(get_chunks_per_region(HeapRegion::LogOfHRGrainBytes)),
+ _log_scan_chunks_per_region(log2_uint(_scan_chunks_per_region)),
_region_scan_chunks(NULL),
+ _num_total_scan_chunks(0),
_scan_chunks_shift(0),
_all_dirty_regions(NULL),
_next_dirty_regions(NULL),
@@ -558,7 +319,8 @@
_max_regions = max_regions;
_collection_set_iter_state = NEW_C_HEAP_ARRAY(G1RemsetIterState, max_regions, mtGC);
_card_table_scan_state = NEW_C_HEAP_ARRAY(uint, max_regions, mtGC);
- _region_scan_chunks = NEW_C_HEAP_ARRAY(bool, max_regions * _scan_chunks_per_region, mtGC);
+ _num_total_scan_chunks = max_regions * _scan_chunks_per_region;
+ _region_scan_chunks = NEW_C_HEAP_ARRAY(bool, _num_total_scan_chunks, mtGC);
_scan_chunks_shift = (uint8_t)log2_intptr(HeapRegion::CardsPerRegion / _scan_chunks_per_region);
_scan_top = NEW_C_HEAP_ARRAY(HeapWord*, max_regions, mtGC);
@@ -571,61 +333,46 @@
}
_all_dirty_regions = new G1DirtyRegions(_max_regions);
+ _next_dirty_regions = new G1DirtyRegions(_max_regions);
G1ResetScanTopClosure cl(this);
G1CollectedHeap::heap()->heap_region_iterate(&cl);
-
- _next_dirty_regions = new G1DirtyRegions(_max_regions);
}
- void print_merge_heap_roots_stats() {
- size_t num_scan_chunks = 0;
- for (uint i = 0; i < _max_regions * _scan_chunks_per_region; i++) {
- if (_region_scan_chunks[i]) {
- num_scan_chunks++;
- }
- }
- size_t num_visited_cards = num_scan_chunks * CardsPerChunk;
- size_t total_dirty_region_cards = _next_dirty_regions->size() * HeapRegion::CardsPerRegion;
+ void prepare_for_merge_heap_roots() {
+ _all_dirty_regions->merge(_next_dirty_regions);
- G1CollectedHeap* g1h = G1CollectedHeap::heap();
- size_t total_old_region_cards =
- (g1h->num_regions() - (g1h->num_free_regions() - g1h->collection_set()->cur_length())) * HeapRegion::CardsPerRegion;
+ _next_dirty_regions->reset();
+ for (size_t i = 0; i < _max_regions; i++) {
+ _card_table_scan_state[i] = 0;
+ }
- log_debug(gc,remset)("Visited cards " SIZE_FORMAT " Total dirty " SIZE_FORMAT " (%.2lf%%) Total old " SIZE_FORMAT " (%.2lf%%)",
- num_visited_cards,
- total_dirty_region_cards,
- percent_of(num_visited_cards, total_dirty_region_cards),
- total_old_region_cards,
- percent_of(num_visited_cards, total_old_region_cards));
+ ::memset(_region_scan_chunks, false, _num_total_scan_chunks * sizeof(*_region_scan_chunks));
}
- void merge_heap_roots(WorkGang* workers, bool remembered_set_only, G1GCPhaseTimes::GCParPhases merge_phase) {
- {
- _all_dirty_regions->merge(_next_dirty_regions);
- _next_dirty_regions->reset();
- for (size_t i = 0; i < _max_regions; i++) {
- _card_table_scan_state[i] = 0;
- }
-
- ::memset(_region_scan_chunks, false, _max_regions * _scan_chunks_per_region * sizeof(*_region_scan_chunks));
- }
-
- size_t const increment_length = G1CollectedHeap::heap()->collection_set()->increment_length();
+ // Returns whether the given region contains cards we need to scan. The remembered
+ // set and other sources may contain cards that
+ // - are in uncommitted regions
+ // - are located in the collection set
+ // - are located in free regions
+ // as we do not clean up remembered sets before merging heap roots.
+ bool contains_cards_to_process(uint const region_idx) const {
+ HeapRegion* hr = G1CollectedHeap::heap()->region_at_or_null(region_idx);
+ return (hr != NULL && !hr->in_collection_set() && hr->is_old_or_humongous_or_archive());
+ }
- uint const num_workers = !remembered_set_only ? workers->active_workers() :
- MIN2(workers->active_workers(), (uint)increment_length);
+ size_t num_visited_cards() const {
+ size_t result = 0;
+ for (uint i = 0; i < _num_total_scan_chunks; i++) {
+ if (_region_scan_chunks[i]) {
+ result++;
+ }
+ }
+ return result * (HeapRegion::CardsPerRegion / _scan_chunks_per_region);
+ }
- {
- G1MergeHeapRootsTask cl(this, num_workers, remembered_set_only, merge_phase);
- log_debug(gc, ergo)("Running %s using %u workers for " SIZE_FORMAT " regions",
- cl.name(), num_workers, increment_length);
- workers->run_task(&cl, num_workers);
- }
-
- if (log_is_enabled(Debug, gc, remset)) {
- print_merge_heap_roots_stats();
- }
+ size_t num_cards_in_dirty_regions() const {
+ return _next_dirty_regions->size() * HeapRegion::CardsPerRegion;
}
void set_chunk_region_dirty(size_t const region_card_idx) {
@@ -636,9 +383,9 @@
}
void set_chunk_dirty(size_t const card_idx) {
- assert((card_idx >> _scan_chunks_shift) < (_max_regions * _scan_chunks_per_region),
+ assert((card_idx >> _scan_chunks_shift) < _num_total_scan_chunks,
"Trying to access index " SIZE_FORMAT " out of bounds " SIZE_FORMAT,
- card_idx >> _scan_chunks_shift, _max_regions * _scan_chunks_per_region);
+ card_idx >> _scan_chunks_shift, _num_total_scan_chunks);
size_t const chunk_idx = card_idx >> _scan_chunks_shift;
if (!_region_scan_chunks[chunk_idx]) {
_region_scan_chunks[chunk_idx] = true;
@@ -1169,8 +916,305 @@
_scan_state->prepare();
}
-void G1RemSet::merge_heap_roots(bool remembered_set_only, G1GCPhaseTimes::GCParPhases merge_phase) {
- _scan_state->merge_heap_roots(_g1h->workers(), remembered_set_only, merge_phase);
+class G1MergeHeapRootsTask : public AbstractGangTask {
+
+ // Visitor for remembered sets, dropping entries onto the card table.
+ class G1MergeCardSetClosure : public HeapRegionClosure {
+ G1RemSetScanState* _scan_state;
+ G1CardTable* _ct;
+
+ uint _merged_sparse;
+ uint _merged_fine;
+ uint _merged_coarse;
+
+ // Returns if the region contains cards we need to scan. If so, remember that
+ // region in the current set of dirty regions.
+ bool remember_if_interesting(uint const region_idx) {
+ if (!_scan_state->contains_cards_to_process(region_idx)) {
+ return false;
+ }
+ _scan_state->add_dirty_region(region_idx);
+ return true;
+ }
+ public:
+ G1MergeCardSetClosure(G1RemSetScanState* scan_state) :
+ _scan_state(scan_state),
+ _ct(G1CollectedHeap::heap()->card_table()),
+ _merged_sparse(0),
+ _merged_fine(0),
+ _merged_coarse(0) { }
+
+ void next_coarse_prt(uint const region_idx) {
+ if (!remember_if_interesting(region_idx)) {
+ return;
+ }
+
+ _merged_coarse++;
+
+ size_t region_base_idx = (size_t)region_idx << HeapRegion::LogCardsPerRegion;
+ _ct->mark_region_dirty(region_base_idx, HeapRegion::CardsPerRegion);
+ _scan_state->set_chunk_region_dirty(region_base_idx);
+ }
+
+ void next_fine_prt(uint const region_idx, BitMap* bm) {
+ if (!remember_if_interesting(region_idx)) {
+ return;
+ }
+
+ _merged_fine++;
+
+ size_t const region_base_idx = (size_t)region_idx << HeapRegion::LogCardsPerRegion;
+ BitMap::idx_t cur = bm->get_next_one_offset(0);
+ while (cur != bm->size()) {
+ _ct->mark_clean_as_dirty(region_base_idx + cur);
+ _scan_state->set_chunk_dirty(region_base_idx + cur);
+ cur = bm->get_next_one_offset(cur + 1);
+ }
+ }
+
+ void next_sparse_prt(uint const region_idx, SparsePRTEntry::card_elem_t* cards, uint const num_cards) {
+ if (!remember_if_interesting(region_idx)) {
+ return;
+ }
+
+ _merged_sparse++;
+
+ size_t const region_base_idx = (size_t)region_idx << HeapRegion::LogCardsPerRegion;
+ for (uint i = 0; i < num_cards; i++) {
+ size_t card_idx = region_base_idx + cards[i];
+ _ct->mark_clean_as_dirty(card_idx);
+ _scan_state->set_chunk_dirty(card_idx);
+ }
+ }
+
+ virtual bool do_heap_region(HeapRegion* r) {
+ assert(r->in_collection_set() || r->is_starts_humongous(), "must be");
+
+ HeapRegionRemSet* rem_set = r->rem_set();
+ if (!rem_set->is_empty()) {
+ rem_set->iterate_prts(*this);
+ }
+
+ return false;
+ }
+
+ size_t merged_sparse() const { return _merged_sparse; }
+ size_t merged_fine() const { return _merged_fine; }
+ size_t merged_coarse() const { return _merged_coarse; }
+ };
+
+ // Visitor for the remembered sets of humongous candidate regions to merge their
+ // remembered set into the card table.
+ class G1FlushHumongousCandidateRemSets : public HeapRegionClosure {
+ G1MergeCardSetClosure _cl;
+
+ public:
+ G1FlushHumongousCandidateRemSets(G1RemSetScanState* scan_state) : _cl(scan_state) { }
+
+ virtual bool do_heap_region(HeapRegion* r) {
+ G1CollectedHeap* g1h = G1CollectedHeap::heap();
+
+ if (!r->is_starts_humongous() ||
+ !g1h->region_attr(r->hrm_index()).is_humongous() ||
+ r->rem_set()->is_empty()) {
+ return false;
+ }
+
+ guarantee(r->rem_set()->occupancy_less_or_equal_than(G1RSetSparseRegionEntries),
+ "Found a not-small remembered set here. This is inconsistent with previous assumptions.");
+
+ _cl.do_heap_region(r);
+
+ // We should only clear the card based remembered set here as we will not
+ // implicitly rebuild anything else during eager reclaim. Note that at the moment
+ // (and probably never) we do not enter this path if there are other kind of
+ // remembered sets for this region.
+ r->rem_set()->clear_locked(true /* only_cardset */);
+ // Clear_locked() above sets the state to Empty. However we want to continue
+ // collecting remembered set entries for humongous regions that were not
+ // reclaimed.
+ r->rem_set()->set_state_complete();
+#ifdef ASSERT
+ G1HeapRegionAttr region_attr = g1h->region_attr(r->hrm_index());
+ assert(region_attr.needs_remset_update(), "must be");
+#endif
+ assert(r->rem_set()->is_empty(), "At this point any humongous candidate remembered set must be empty.");
+
+ return false;
+ }
+
+ size_t merged_sparse() const { return _cl.merged_sparse(); }
+ size_t merged_fine() const { return _cl.merged_fine(); }
+ size_t merged_coarse() const { return _cl.merged_coarse(); }
+ };
+
+ // Visitor for the log buffer entries to merge them into the card table.
+ class G1MergeLogBufferCardsClosure : public G1CardTableEntryClosure {
+ G1RemSetScanState* _scan_state;
+ G1CardTable* _ct;
+
+ size_t _cards_dirty;
+ size_t _cards_skipped;
+ public:
+ G1MergeLogBufferCardsClosure(G1CollectedHeap* g1h, G1RemSetScanState* scan_state) :
+ _scan_state(scan_state), _ct(g1h->card_table()), _cards_dirty(0), _cards_skipped(0)
+ {}
+
+ bool do_card_ptr(CardValue* card_ptr, uint worker_i) {
+ // The only time we care about recording cards that
+ // contain references that point into the collection set
+ // is during RSet updating within an evacuation pause.
+ // In this case worker_id should be the id of a GC worker thread.
+ assert(SafepointSynchronize::is_at_safepoint(), "not during an evacuation pause");
+
+ uint const region_idx = _ct->region_idx_for(card_ptr);
+
+ // The second clause must come after - the log buffers might contain cards to uncommited
+ // regions.
+ // This code may count duplicate entries in the log buffers (even if rare) multiple
+ // times.
+ if (_scan_state->contains_cards_to_process(region_idx) && (*card_ptr == G1CardTable::dirty_card_val())) {
+ _scan_state->add_dirty_region(region_idx);
+ _scan_state->set_chunk_dirty(_ct->index_for_cardvalue(card_ptr));
+ _cards_dirty++;
+ } else {
+ // We may have had dirty cards in the (initial) collection set (or the
+ // young regions which are always in the initial collection set). We do
+ // not fix their cards here: we already added these regions to the set of
+ // regions to clear the card table at the end during the prepare() phase.
+ _cards_skipped++;
+ }
+ return true;
+ }
+
+ size_t cards_dirty() const { return _cards_dirty; }
+ size_t cards_skipped() const { return _cards_skipped; }
+ };
+
+ HeapRegionClaimer _hr_claimer;
+ G1RemSetScanState* _scan_state;
+ bool _initial_evacuation;
+
+ volatile bool _fast_reclaim_handled;
+
+public:
+ G1MergeHeapRootsTask(G1RemSetScanState* scan_state, uint num_workers, bool initial_evacuation) :
+ AbstractGangTask("G1 Merge Heap Roots"),
+ _hr_claimer(num_workers),
+ _scan_state(scan_state),
+ _initial_evacuation(initial_evacuation),
+ _fast_reclaim_handled(false) { }
+
+ virtual void work(uint worker_id) {
+ G1CollectedHeap* g1h = G1CollectedHeap::heap();
+ G1GCPhaseTimes* p = g1h->phase_times();
+
+ G1GCPhaseTimes::GCParPhases merge_remset_phase = _initial_evacuation ?
+ G1GCPhaseTimes::MergeRS :
+ G1GCPhaseTimes::OptMergeRS;
+
+ // We schedule flushing the remembered sets of humongous fast reclaim candidates
+ // onto the card table first to allow the remaining parallelized tasks hide it.
+ if (_initial_evacuation &&
+ p->fast_reclaim_humongous_candidates() > 0 &&
+ !_fast_reclaim_handled &&
+ !Atomic::cmpxchg(true, &_fast_reclaim_handled, false)) {
+
+ G1GCParPhaseTimesTracker x(p, G1GCPhaseTimes::MergeER, worker_id);
+
+ G1FlushHumongousCandidateRemSets cl(_scan_state);
+ g1h->heap_region_iterate(&cl);
+
+ p->record_or_add_thread_work_item(merge_remset_phase, worker_id, cl.merged_sparse(), G1GCPhaseTimes::MergeRSMergedSparse);
+ p->record_or_add_thread_work_item(merge_remset_phase, worker_id, cl.merged_fine(), G1GCPhaseTimes::MergeRSMergedFine);
+ p->record_or_add_thread_work_item(merge_remset_phase, worker_id, cl.merged_coarse(), G1GCPhaseTimes::MergeRSMergedCoarse);
+ }
+
+ // Merge remembered sets of current candidates.
+ {
+ G1GCParPhaseTimesTracker x(p, merge_remset_phase, worker_id, _initial_evacuation /* must_record */);
+ G1MergeCardSetClosure cl(_scan_state);
+ g1h->collection_set_iterate_increment_from(&cl, &_hr_claimer, worker_id);
+
+ p->record_or_add_thread_work_item(merge_remset_phase, worker_id, cl.merged_sparse(), G1GCPhaseTimes::MergeRSMergedSparse);
+ p->record_or_add_thread_work_item(merge_remset_phase, worker_id, cl.merged_fine(), G1GCPhaseTimes::MergeRSMergedFine);
+ p->record_or_add_thread_work_item(merge_remset_phase, worker_id, cl.merged_coarse(), G1GCPhaseTimes::MergeRSMergedCoarse);
+ }
+
+ // Apply closure to log entries in the HCC.
+ if (_initial_evacuation && G1HotCardCache::default_use_cache()) {
+ assert(merge_remset_phase == G1GCPhaseTimes::MergeRS, "Wrong merge phase");
+ G1GCParPhaseTimesTracker x(p, G1GCPhaseTimes::MergeHCC, worker_id);
+ G1MergeLogBufferCardsClosure cl(g1h, _scan_state);
+ g1h->iterate_hcc_closure(&cl, worker_id);
+
+ p->record_thread_work_item(G1GCPhaseTimes::MergeHCC, worker_id, cl.cards_dirty(), G1GCPhaseTimes::MergeHCCDirtyCards);
+ p->record_thread_work_item(G1GCPhaseTimes::MergeHCC, worker_id, cl.cards_skipped(), G1GCPhaseTimes::MergeHCCSkippedCards);
+ }
+
+ // Now apply the closure to all remaining log entries.
+ if (_initial_evacuation) {
+ assert(merge_remset_phase == G1GCPhaseTimes::MergeRS, "Wrong merge phase");
+ G1GCParPhaseTimesTracker x(p, G1GCPhaseTimes::MergeLB, worker_id);
+
+ G1MergeLogBufferCardsClosure cl(g1h, _scan_state);
+ g1h->iterate_dirty_card_closure(&cl, worker_id);
+
+ p->record_thread_work_item(G1GCPhaseTimes::MergeLB, worker_id, cl.cards_dirty(), G1GCPhaseTimes::MergeLBDirtyCards);
+ p->record_thread_work_item(G1GCPhaseTimes::MergeLB, worker_id, cl.cards_skipped(), G1GCPhaseTimes::MergeLBSkippedCards);
+ }
+ }
+};
+
+void G1RemSet::print_merge_heap_roots_stats() {
+ size_t num_visited_cards = _scan_state->num_visited_cards();
+
+ size_t total_dirty_region_cards = _scan_state->num_cards_in_dirty_regions();
+
+ G1CollectedHeap* g1h = G1CollectedHeap::heap();
+ size_t total_old_region_cards =
+ (g1h->num_regions() - (g1h->num_free_regions() - g1h->collection_set()->cur_length())) * HeapRegion::CardsPerRegion;
+
+ log_debug(gc,remset)("Visited cards " SIZE_FORMAT " Total dirty " SIZE_FORMAT " (%.2lf%%) Total old " SIZE_FORMAT " (%.2lf%%)",
+ num_visited_cards,
+ total_dirty_region_cards,
+ percent_of(num_visited_cards, total_dirty_region_cards),
+ total_old_region_cards,
+ percent_of(num_visited_cards, total_old_region_cards));
+}
+
+void G1RemSet::merge_heap_roots(bool initial_evacuation) {
+ G1CollectedHeap* g1h = G1CollectedHeap::heap();
+
+ {
+ Ticks start = Ticks::now();
+
+ _scan_state->prepare_for_merge_heap_roots();
+
+ Tickspan total = Ticks::now() - start;
+ if (initial_evacuation) {
+ g1h->phase_times()->record_prepare_merge_heap_roots_time(total.seconds() * 1000.0);
+ } else {
+ g1h->phase_times()->record_or_add_optional_prepare_merge_heap_roots_time(total.seconds() * 1000.0);
+ }
+ }
+
+ WorkGang* workers = g1h->workers();
+ size_t const increment_length = g1h->collection_set()->increment_length();
+
+ uint const num_workers = initial_evacuation ? workers->active_workers() :
+ MIN2(workers->active_workers(), (uint)increment_length);
+
+ {
+ G1MergeHeapRootsTask cl(_scan_state, num_workers, initial_evacuation);
+ log_debug(gc, ergo)("Running %s using %u workers for " SIZE_FORMAT " regions",
+ cl.name(), num_workers, increment_length);
+ workers->run_task(&cl, num_workers);
+ }
+
+ if (log_is_enabled(Debug, gc, remset)) {
+ print_merge_heap_roots_stats();
+ }
}
void G1RemSet::prepare_for_scan_heap_roots(uint region_idx) {
--- a/src/hotspot/share/gc/g1/g1RemSet.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1RemSet.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -67,6 +67,7 @@
G1Policy* _g1p;
G1HotCardCache* _hot_card_cache;
+ void print_merge_heap_roots_stats();
public:
typedef CardTable::CardValue CardValue;
@@ -94,8 +95,8 @@
// Merge cards from various sources (remembered sets, hot card cache, log buffers)
// and calculate the cards that need to be scanned later (via scan_heap_roots()).
- // If remembered_set_only is set, only merge remembered set cards.
- void merge_heap_roots(bool remembered_set_only, G1GCPhaseTimes::GCParPhases merge_phase);
+ // If initial_evacuation is set, this is called during the initial evacuation.
+ void merge_heap_roots(bool initial_evacuation);
// Prepare for and cleanup after scanning the heap roots. Must be called
// once before and after in sequential code.
--- a/src/hotspot/share/gc/g1/g1RootProcessor.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1RootProcessor.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -44,9 +44,6 @@
#include "runtime/mutex.hpp"
#include "services/management.hpp"
#include "utilities/macros.hpp"
-#if INCLUDE_JVMCI
-#include "jvmci/jvmci.hpp"
-#endif
void G1RootProcessor::worker_has_discovered_all_strong_classes() {
assert(ClassUnloadingWithConcurrentMark, "Currently only needed when doing G1 Class Unloading");
@@ -261,15 +258,6 @@
}
#endif
-#if INCLUDE_JVMCI
- if (EnableJVMCI) {
- G1GCParPhaseTimesTracker x(phase_times, G1GCPhaseTimes::JVMCIRoots, worker_i);
- if (_process_strong_tasks.try_claim_task(G1RP_PS_JVMCI_oops_do)) {
- JVMCI::oops_do(strong_roots);
- }
- }
-#endif
-
{
G1GCParPhaseTimesTracker x(phase_times, G1GCPhaseTimes::SystemDictionaryRoots, worker_i);
if (_process_strong_tasks.try_claim_task(G1RP_PS_SystemDictionary_oops_do)) {
--- a/src/hotspot/share/gc/g1/g1RootProcessor.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1RootProcessor.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -64,7 +64,6 @@
G1RP_PS_jvmti_oops_do,
G1RP_PS_CodeCache_oops_do,
AOT_ONLY(G1RP_PS_aot_oops_do COMMA)
- JVMCI_ONLY(G1RP_PS_JVMCI_oops_do COMMA)
G1RP_PS_refProcessor_oops_do,
// Leave this one last.
G1RP_PS_NumElements
--- a/src/hotspot/share/gc/parallel/pcTasks.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/parallel/pcTasks.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -46,9 +46,6 @@
#include "runtime/vmThread.hpp"
#include "services/management.hpp"
#include "utilities/stack.inline.hpp"
-#if INCLUDE_JVMCI
-#include "jvmci/jvmci.hpp"
-#endif
//
// ThreadRootsMarkingTask
@@ -124,12 +121,6 @@
AOTLoader::oops_do(&mark_and_push_closure);
break;
-#if INCLUDE_JVMCI
- case jvmci:
- JVMCI::oops_do(&mark_and_push_closure);
- break;
-#endif
-
default:
fatal("Unknown root type");
}
--- a/src/hotspot/share/gc/parallel/pcTasks.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/parallel/pcTasks.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -99,7 +99,6 @@
system_dictionary = 7,
class_loader_data = 8,
code_cache = 9
- JVMCI_ONLY(COMMA jvmci = 10)
};
private:
RootType _root_type;
--- a/src/hotspot/share/gc/parallel/psMarkSweep.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/parallel/psMarkSweep.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -524,7 +524,6 @@
// Do not treat nmethods as strong roots for mark/sweep, since we can unload them.
//ScavengableNMethods::scavengable_nmethods_do(CodeBlobToOopClosure(mark_and_push_closure()));
AOT_ONLY(AOTLoader::oops_do(mark_and_push_closure());)
- JVMCI_ONLY(JVMCI::oops_do(mark_and_push_closure());)
}
// Flush marking stack.
@@ -620,8 +619,6 @@
CodeCache::blobs_do(&adjust_from_blobs);
AOT_ONLY(AOTLoader::oops_do(adjust_pointer_closure());)
- JVMCI_ONLY(JVMCI::oops_do(adjust_pointer_closure());)
-
ref_processor()->weak_oops_do(adjust_pointer_closure());
PSScavenge::reference_processor()->weak_oops_do(adjust_pointer_closure());
--- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -2127,7 +2127,6 @@
q->enqueue(new MarkFromRootsTask(MarkFromRootsTask::class_loader_data));
q->enqueue(new MarkFromRootsTask(MarkFromRootsTask::jvmti));
q->enqueue(new MarkFromRootsTask(MarkFromRootsTask::code_cache));
- JVMCI_ONLY(q->enqueue(new MarkFromRootsTask(MarkFromRootsTask::jvmci));)
if (active_gc_threads > 1) {
for (uint j = 0; j < active_gc_threads; j++) {
@@ -2217,8 +2216,6 @@
CodeCache::blobs_do(&adjust_from_blobs);
AOT_ONLY(AOTLoader::oops_do(&oop_closure);)
- JVMCI_ONLY(JVMCI::oops_do(&oop_closure);)
-
ref_processor()->weak_oops_do(&oop_closure);
// Roots were visited so references into the young gen in roots
// may have been scanned. Process them also.
--- a/src/hotspot/share/gc/parallel/psScavenge.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/parallel/psScavenge.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -379,7 +379,6 @@
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::class_loader_data));
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::jvmti));
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::code_cache));
- JVMCI_ONLY(q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::jvmci));)
TaskTerminator terminator(active_workers,
(TaskQueueSetSuper*) promotion_manager->stack_array_depth());
--- a/src/hotspot/share/gc/parallel/psTasks.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/parallel/psTasks.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -44,9 +44,6 @@
#include "runtime/thread.hpp"
#include "runtime/vmThread.hpp"
#include "services/management.hpp"
-#if INCLUDE_JVMCI
-#include "jvmci/jvmci.hpp"
-#endif
//
// ScavengeRootsTask
@@ -106,12 +103,6 @@
}
break;
-#if INCLUDE_JVMCI
- case jvmci:
- JVMCI::oops_do(&roots_closure);
- break;
-#endif
-
default:
fatal("Unknown root type");
}
--- a/src/hotspot/share/gc/parallel/psTasks.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/parallel/psTasks.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -61,7 +61,6 @@
management = 7,
jvmti = 8,
code_cache = 9
- JVMCI_ONLY(COMMA jvmci = 10)
};
private:
RootType _root_type;
--- a/src/hotspot/share/gc/shared/genCollectedHeap.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shared/genCollectedHeap.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -861,11 +861,6 @@
AOTLoader::oops_do(strong_roots);
}
#endif
-#if INCLUDE_JVMCI
- if (EnableJVMCI && _process_strong_tasks->try_claim_task(GCH_PS_jvmci_oops_do)) {
- JVMCI::oops_do(strong_roots);
- }
-#endif
if (_process_strong_tasks->try_claim_task(GCH_PS_SystemDictionary_oops_do)) {
SystemDictionary::oops_do(strong_roots);
}
--- a/src/hotspot/share/gc/shared/genCollectedHeap.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shared/genCollectedHeap.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -114,7 +114,6 @@
GCH_PS_jvmti_oops_do,
GCH_PS_CodeCache_oops_do,
AOT_ONLY(GCH_PS_aot_oops_do COMMA)
- JVMCI_ONLY(GCH_PS_jvmci_oops_do COMMA)
GCH_PS_younger_gens,
// Leave this one last.
GCH_PS_NumElements
--- a/src/hotspot/share/gc/shared/memAllocator.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shared/memAllocator.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -173,11 +173,8 @@
assert(!_thread->has_pending_exception(),
"shouldn't be allocating with pending exception");
if (StrictSafepointChecks) {
- assert(_thread->allow_allocation(),
- "Allocation done by thread for which allocation is blocked "
- "by No_Allocation_Verifier!");
// Allocation of an oop can always invoke a safepoint,
- // hence, the true argument
+ // hence, the true argument.
_thread->check_for_valid_safepoint_state(true);
}
}
@@ -370,6 +367,10 @@
HeapWord* mem = mem_allocate(allocation);
if (mem != NULL) {
obj = initialize(mem);
+ } else {
+ // The unhandled oop detector will poison local variable obj,
+ // so reset it to NULL if mem is NULL.
+ obj = NULL;
}
}
return obj;
--- a/src/hotspot/share/gc/shared/parallelCleaning.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shared/parallelCleaning.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -30,9 +30,6 @@
#include "logging/log.hpp"
#include "memory/resourceArea.hpp"
#include "logging/log.hpp"
-#if INCLUDE_JVMCI
-#include "jvmci/jvmci.hpp"
-#endif
StringDedupCleaningTask::StringDedupCleaningTask(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
@@ -160,56 +157,3 @@
clean_klass(klass);
}
}
-
-#if INCLUDE_JVMCI
-JVMCICleaningTask::JVMCICleaningTask() :
- _cleaning_claimed(0) {
-}
-
-bool JVMCICleaningTask::claim_cleaning_task() {
- if (_cleaning_claimed) {
- return false;
- }
-
- return Atomic::cmpxchg(1, &_cleaning_claimed, 0) == 0;
-}
-
-void JVMCICleaningTask::work(bool unloading_occurred) {
- // One worker will clean JVMCI metadata handles.
- if (unloading_occurred && EnableJVMCI && claim_cleaning_task()) {
- JVMCI::do_unloading(unloading_occurred);
- }
-}
-#endif // INCLUDE_JVMCI
-
-ParallelCleaningTask::ParallelCleaningTask(BoolObjectClosure* is_alive,
- uint num_workers,
- bool unloading_occurred,
- bool resize_dedup_table) :
- AbstractGangTask("Parallel Cleaning"),
- _unloading_occurred(unloading_occurred),
- _string_dedup_task(is_alive, NULL, resize_dedup_table),
- _code_cache_task(num_workers, is_alive, unloading_occurred),
- JVMCI_ONLY(_jvmci_cleaning_task() COMMA)
- _klass_cleaning_task() {
-}
-
-// The parallel work done by all worker threads.
-void ParallelCleaningTask::work(uint worker_id) {
- // Clean JVMCI metadata handles.
- // Execute this task first because it is serial task.
- JVMCI_ONLY(_jvmci_cleaning_task.work(_unloading_occurred);)
-
- // Do first pass of code cache cleaning.
- _code_cache_task.work(worker_id);
-
- // Clean the string dedup data structures.
- _string_dedup_task.work(worker_id);
-
- // Clean all klasses that were not unloaded.
- // The weak metadata in klass doesn't need to be
- // processed if there was no unloading.
- if (_unloading_occurred) {
- _klass_cleaning_task.work();
- }
-}
--- a/src/hotspot/share/gc/shared/parallelCleaning.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shared/parallelCleaning.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -31,8 +31,6 @@
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/workgroup.hpp"
-class ParallelCleaningTask;
-
class StringDedupCleaningTask : public AbstractGangTask {
StringDedupUnlinkOrOopsDoClosure _dedup_closure;
@@ -87,40 +85,4 @@
void work();
};
-#if INCLUDE_JVMCI
-class JVMCICleaningTask : public StackObj {
- volatile int _cleaning_claimed;
-
-public:
- JVMCICleaningTask();
- // Clean JVMCI metadata handles.
- void work(bool unloading_occurred);
-
-private:
- bool claim_cleaning_task();
-};
-#endif
-
-// Do cleanup of some weakly held data in the same parallel task.
-// Assumes a non-moving context.
-class ParallelCleaningTask : public AbstractGangTask {
-private:
- bool _unloading_occurred;
- StringDedupCleaningTask _string_dedup_task;
- CodeCacheUnloadingTask _code_cache_task;
-#if INCLUDE_JVMCI
- JVMCICleaningTask _jvmci_cleaning_task;
-#endif
- KlassCleaningTask _klass_cleaning_task;
-
-public:
- // The constructor is run in the VMThread.
- ParallelCleaningTask(BoolObjectClosure* is_alive,
- uint num_workers,
- bool unloading_occurred,
- bool resize_dedup_table);
-
- void work(uint worker_id);
-};
-
#endif // SHARE_GC_SHARED_PARALLELCLEANING_HPP
--- a/src/hotspot/share/gc/shared/ptrQueue.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shared/ptrQueue.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -296,6 +296,10 @@
class PtrQueueSet {
BufferNode::Allocator* _allocator;
+ // Noncopyable - not defined.
+ PtrQueueSet(const PtrQueueSet&);
+ PtrQueueSet& operator=(const PtrQueueSet&);
+
protected:
bool _all_active;
@@ -309,6 +313,9 @@
public:
+ // Return the associated BufferNode allocator.
+ BufferNode::Allocator* allocator() const { return _allocator; }
+
// Return the buffer for a BufferNode of size buffer_size().
void** allocate_buffer();
--- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -101,15 +101,15 @@
__ branch_destination(slow->continuation());
}
-LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, CodeEmitInfo* info, bool need_null_check) {
+LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier(LIRGenerator* gen, LIR_Opr obj) {
if (ShenandoahLoadRefBarrier) {
- return load_reference_barrier_impl(gen, obj, info, need_null_check);
+ return load_reference_barrier_impl(gen, obj);
} else {
return obj;
}
}
-LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, CodeEmitInfo* info, bool need_null_check) {
+LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj) {
assert(ShenandoahLoadRefBarrier, "Should be enabled");
obj = ensure_in_register(gen, obj);
@@ -140,7 +140,7 @@
}
__ cmp(lir_cond_notEqual, flag_val, LIR_OprFact::intConst(0));
- CodeStub* slow = new ShenandoahLoadReferenceBarrierStub(obj, result, info ? new CodeEmitInfo(info) : NULL, need_null_check);
+ CodeStub* slow = new ShenandoahLoadReferenceBarrierStub(obj, result);
__ branch(lir_cond_notEqual, T_INT, slow);
__ branch_destination(slow->continuation());
@@ -204,7 +204,7 @@
if (ShenandoahLoadRefBarrier) {
LIR_Opr tmp = gen->new_register(T_OBJECT);
BarrierSetC1::load_at_resolved(access, tmp);
- tmp = load_reference_barrier(access.gen(), tmp, access.access_emit_info(), true);
+ tmp = load_reference_barrier(access.gen(), tmp);
__ move(tmp, result);
} else {
BarrierSetC1::load_at_resolved(access, result);
--- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -90,12 +90,10 @@
private:
LIR_Opr _obj;
LIR_Opr _result;
- CodeEmitInfo* _info;
- bool _needs_null_check;
public:
- ShenandoahLoadReferenceBarrierStub(LIR_Opr obj, LIR_Opr result, CodeEmitInfo* info, bool needs_null_check) :
- _obj(obj), _result(result), _info(info), _needs_null_check(needs_null_check)
+ ShenandoahLoadReferenceBarrierStub(LIR_Opr obj, LIR_Opr result) :
+ _obj(obj), _result(result)
{
assert(_obj->is_register(), "should be register");
assert(_result->is_register(), "should be register");
@@ -103,8 +101,6 @@
LIR_Opr obj() const { return _obj; }
LIR_Opr result() const { return _result; }
- CodeEmitInfo* info() const { return _info; }
- bool needs_null_check() const { return _needs_null_check; }
virtual void emit_code(LIR_Assembler* e);
virtual void visit(LIR_OpVisitState* visitor) {
@@ -181,10 +177,10 @@
void pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, DecoratorSet decorators, LIR_Opr addr_opr, LIR_Opr pre_val);
- LIR_Opr load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, CodeEmitInfo* info, bool need_null_check);
+ LIR_Opr load_reference_barrier(LIRGenerator* gen, LIR_Opr obj);
LIR_Opr storeval_barrier(LIRGenerator* gen, LIR_Opr obj, CodeEmitInfo* info, DecoratorSet decorators);
- LIR_Opr load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, CodeEmitInfo* info, bool need_null_check);
+ LIR_Opr load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj);
LIR_Opr ensure_in_register(LIRGenerator* gen, LIR_Opr obj);
--- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -472,19 +472,6 @@
return TypeFunc::make(domain, range);
}
-const TypeFunc* ShenandoahBarrierSetC2::oop_load_from_native_barrier_Type() {
- const Type **fields = TypeTuple::fields(1);
- fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // original field value
- const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+1, fields);
-
- // create result type (range)
- fields = TypeTuple::fields(1);
- fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL;
- const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+1, fields);
-
- return TypeFunc::make(domain, range);
-}
-
const TypeFunc* ShenandoahBarrierSetC2::shenandoah_load_reference_barrier_Type() {
const Type **fields = TypeTuple::fields(1);
fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // original field value
@@ -555,22 +542,9 @@
Node* offset = adr->is_AddP() ? adr->in(AddPNode::Offset) : top;
Node* load = BarrierSetC2::load_at_resolved(access, val_type);
- if ((decorators & IN_NATIVE) != 0) {
- assert(access.is_oop(), "IN_NATIVE access only for oop values");
- assert(access.is_parse_access(), "IN_NATIVE access only during parsing");
- GraphKit* kit = static_cast<C2ParseAccess &>(access).kit();
- Node* call = kit->make_runtime_call(GraphKit::RC_LEAF,
- oop_load_from_native_barrier_Type(),
- CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_native),
- "ShenandoahRuntime::load_reference_barrier_native",
- NULL, load);
- Node* proj = kit->gvn().transform(new ProjNode(call, TypeFunc::Parms+0));
- return kit->gvn().transform(new CheckCastPPNode(kit->control(), proj, load->bottom_type()));
- }
-
if (access.is_oop()) {
if (ShenandoahLoadRefBarrier) {
- load = new ShenandoahLoadReferenceBarrierNode(NULL, load);
+ load = new ShenandoahLoadReferenceBarrierNode(NULL, load, (decorators & IN_NATIVE) != 0);
if (access.is_parse_access()) {
load = static_cast<C2ParseAccess &>(access).kit()->gvn().transform(load);
} else {
@@ -655,7 +629,7 @@
load_store = kit->gvn().transform(new DecodeNNode(load_store, load_store->get_ptr_type()));
}
#endif
- load_store = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(NULL, load_store));
+ load_store = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(NULL, load_store, false));
return load_store;
}
return BarrierSetC2::atomic_cmpxchg_val_at_resolved(access, expected_val, new_val, value_type);
@@ -723,7 +697,7 @@
}
Node* result = BarrierSetC2::atomic_xchg_at_resolved(access, val, value_type);
if (access.is_oop()) {
- result = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(NULL, result));
+ result = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(NULL, result, false));
shenandoah_write_barrier_pre(kit, false /* do_load */,
NULL, NULL, max_juint, NULL, NULL,
result /* pre_val */, T_OBJECT);
@@ -749,8 +723,7 @@
return strcmp(call->_name, "shenandoah_clone_barrier") == 0 ||
strcmp(call->_name, "shenandoah_cas_obj") == 0 ||
- strcmp(call->_name, "shenandoah_wb_pre") == 0 ||
- strcmp(call->_name, "ShenandoahRuntime::load_reference_barrier_native") == 0;
+ strcmp(call->_name, "shenandoah_wb_pre") == 0;
}
Node* ShenandoahBarrierSetC2::step_over_gc_barrier(Node* c) const {
@@ -1182,11 +1155,6 @@
case Op_ShenandoahLoadReferenceBarrier:
conn_graph->add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(ShenandoahLoadReferenceBarrierNode::ValueIn), delayed_worklist);
return true;
- case Op_CallLeaf:
- if (strcmp(n->as_CallLeaf()->_name, "ShenandoahRuntime::load_reference_barrier_native") == 0) {
- conn_graph->map_ideal_node(n, conn_graph->phantom_obj);
- return true;
- }
default:
// Nothing
break;
--- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -103,7 +103,6 @@
static const TypeFunc* write_ref_field_pre_entry_Type();
static const TypeFunc* shenandoah_clone_barrier_Type();
static const TypeFunc* shenandoah_load_reference_barrier_Type();
- static const TypeFunc* oop_load_from_native_barrier_Type();
virtual bool has_load_barriers() const { return true; }
// This is the entry-point for the backend to perform accesses through the Access API.
--- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -208,10 +208,7 @@
if (trace) {
tty->print("Found raw LoadP (OSR argument?)");
}
- } else if (in->Opcode() == Op_ShenandoahLoadReferenceBarrier ||
- (in->Opcode() == Op_Proj &&
- in->in(0)->Opcode() == Op_CallLeaf &&
- strcmp(in->in(0)->as_Call()->_name, "ShenandoahRuntime::load_reference_barrier_native") == 0)) {
+ } else if (in->Opcode() == Op_ShenandoahLoadReferenceBarrier) {
if (t == ShenandoahOopStore) {
uint i = 0;
for (; i < phis.size(); i++) {
@@ -530,7 +527,7 @@
if (!verify_helper(n->in(TypeFunc::Parms), phis, visited, ShenandoahStore, trace, barriers_used)) {
report_verify_failure("Shenandoah verification: _fill should have barriers", n);
}
- } else if (!strcmp(call->_name, "shenandoah_wb_pre") || !strcmp(call->_name, "ShenandoahRuntime::load_reference_barrier_native")) {
+ } else if (!strcmp(call->_name, "shenandoah_wb_pre")) {
// skip
} else {
const int calls_len = sizeof(calls) / sizeof(calls[0]);
@@ -1085,7 +1082,7 @@
phase->register_control(ctrl, loop, in_cset_fast_test_iff);
}
-void ShenandoahBarrierC2Support::call_lrb_stub(Node*& ctrl, Node*& val, Node*& result_mem, Node* raw_mem, PhaseIdealLoop* phase) {
+void ShenandoahBarrierC2Support::call_lrb_stub(Node*& ctrl, Node*& val, Node*& result_mem, Node* raw_mem, bool is_native, PhaseIdealLoop* phase) {
IdealLoopTree*loop = phase->get_loop(ctrl);
const TypePtr* obj_type = phase->igvn().type(val)->is_oopptr()->cast_to_nonconst();
@@ -1096,9 +1093,10 @@
mm->set_memory_at(Compile::AliasIdxRaw, raw_mem);
phase->register_new_node(mm, ctrl);
- Node* call = new CallLeafNode(ShenandoahBarrierSetC2::shenandoah_load_reference_barrier_Type(),
- CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier),
- "shenandoah_load_reference_barrier", TypeRawPtr::BOTTOM);
+ address calladdr = is_native ? CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_native)
+ : CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier);
+ const char* name = is_native ? "oop_load_from_native_barrier" : "load_reference_barrier";
+ Node* call = new CallLeafNode(ShenandoahBarrierSetC2::shenandoah_load_reference_barrier_Type(), calladdr, name, TypeRawPtr::BOTTOM);
call->init_req(TypeFunc::Control, ctrl);
call->init_req(TypeFunc::I_O, phase->C->top());
call->init_req(TypeFunc::Memory, mm);
@@ -1561,7 +1559,7 @@
Node* result_mem = NULL;
ctrl = if_not_fwd;
fwd = new_val;
- call_lrb_stub(ctrl, fwd, result_mem, raw_mem, phase);
+ call_lrb_stub(ctrl, fwd, result_mem, raw_mem, lrb->is_native(), phase);
region->init_req(_evac_path, ctrl);
val_phi->init_req(_evac_path, fwd);
raw_mem_phi->init_req(_evac_path, result_mem);
@@ -3004,11 +3002,28 @@
}
}
-ShenandoahLoadReferenceBarrierNode::ShenandoahLoadReferenceBarrierNode(Node* ctrl, Node* obj)
-: Node(ctrl, obj) {
+ShenandoahLoadReferenceBarrierNode::ShenandoahLoadReferenceBarrierNode(Node* ctrl, Node* obj, bool native)
+: Node(ctrl, obj), _native(native) {
ShenandoahBarrierSetC2::bsc2()->state()->add_load_reference_barrier(this);
}
+bool ShenandoahLoadReferenceBarrierNode::is_native() const {
+ return _native;
+}
+
+uint ShenandoahLoadReferenceBarrierNode::size_of() const {
+ return sizeof(*this);
+}
+
+uint ShenandoahLoadReferenceBarrierNode::hash() const {
+ return Node::hash() + (_native ? 1 : 0);
+}
+
+bool ShenandoahLoadReferenceBarrierNode::cmp( const Node &n ) const {
+ return Node::cmp(n) && n.Opcode() == Op_ShenandoahLoadReferenceBarrier &&
+ _native == ((const ShenandoahLoadReferenceBarrierNode&)n)._native;
+}
+
const Type* ShenandoahLoadReferenceBarrierNode::bottom_type() const {
if (in(ValueIn) == NULL || in(ValueIn)->is_top()) {
return Type::TOP;
--- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -60,7 +60,7 @@
static void test_null(Node*& ctrl, Node* val, Node*& null_ctrl, PhaseIdealLoop* phase);
static void test_heap_stable(Node*& ctrl, Node* raw_mem, Node*& heap_stable_ctrl,
PhaseIdealLoop* phase);
- static void call_lrb_stub(Node*& ctrl, Node*& val, Node*& result_mem, Node* raw_mem, PhaseIdealLoop* phase);
+ static void call_lrb_stub(Node*& ctrl, Node*& val, Node*& result_mem, Node* raw_mem, bool is_native, PhaseIdealLoop* phase);
static Node* clone_null_check(Node*& c, Node* val, Node* unc_ctrl, PhaseIdealLoop* phase);
static void fix_null_check(Node* unc, Node* unc_ctrl, Node* new_unc_ctrl, Unique_Node_List& uses,
PhaseIdealLoop* phase);
@@ -234,8 +234,13 @@
NONE, WEAK, STRONG, NA
};
- ShenandoahLoadReferenceBarrierNode(Node* ctrl, Node* val);
+private:
+ bool _native;
+public:
+ ShenandoahLoadReferenceBarrierNode(Node* ctrl, Node* val, bool native);
+
+ bool is_native() const;
virtual int Opcode() const;
virtual const Type* bottom_type() const;
virtual const Type* Value(PhaseGVN* phase) const;
@@ -247,9 +252,9 @@
virtual Node* Identity(PhaseGVN* phase);
- uint size_of() const {
- return sizeof(*this);
- }
+ virtual uint size_of() const;
+ virtual uint hash() const;
+ virtual bool cmp( const Node &n ) const;
Strength get_barrier_strength();
CallStaticJavaNode* pin_and_expand_null_check(PhaseIterGVN& igvn);
--- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -360,5 +360,14 @@
}
oop ShenandoahBarrierSet::oop_load_from_native_barrier(oop obj) {
- return load_reference_barrier(obj);
+ if (CompressedOops::is_null(obj)) {
+ return NULL;
+ }
+
+ if (_heap->is_evacuation_in_progress() &&
+ !_heap->complete_marking_context()->is_marked(obj)) {
+ return NULL;
+ }
+
+ return load_reference_barrier_not_null(obj);
}
--- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -179,6 +179,10 @@
template <typename T>
static oop oop_load_not_in_heap(T* addr);
+ // Used for catching bad stores
+ template <typename T>
+ static void oop_store_not_in_heap(T* addr, oop value);
+
template <typename T>
static oop oop_atomic_cmpxchg_not_in_heap(oop new_value, T* addr, oop compare_value);
--- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -25,6 +25,7 @@
#define SHARE_GC_SHENANDOAH_SHENANDOAHBARRIERSET_INLINE_HPP
#include "gc/shared/barrierSet.hpp"
+#include "gc/shenandoah/shenandoahAsserts.hpp"
#include "gc/shenandoah/shenandoahBarrierSet.hpp"
#include "gc/shenandoah/shenandoahForwarding.inline.hpp"
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
@@ -88,6 +89,13 @@
template <DecoratorSet decorators, typename BarrierSetT>
template <typename T>
+inline void ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_store_not_in_heap(T* addr, oop value) {
+ shenandoah_assert_marked_if(NULL, value, !CompressedOops::is_null(value) && ShenandoahHeap::heap()->is_evacuation_in_progress());
+ Raw::oop_store(addr, value);
+}
+
+template <DecoratorSet decorators, typename BarrierSetT>
+template <typename T>
inline oop ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_atomic_cmpxchg_not_in_heap(oop new_value, T* addr, oop compare_value) {
oop res;
oop expected = compare_value;
--- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -446,22 +446,11 @@
weak_refs_work(full_gc);
}
- weak_roots_work();
+ _heap->parallel_cleaning(full_gc);
- // And finally finish class unloading
- if (_heap->unload_classes()) {
- _heap->unload_classes_and_cleanup_tables(full_gc);
- } else if (ShenandoahStringDedup::is_enabled()) {
- ShenandoahIsAliveSelector alive;
- BoolObjectClosure* is_alive = alive.is_alive_closure();
- ShenandoahStringDedup::unlink_or_oops_do(is_alive, NULL, false);
- }
assert(task_queues()->is_empty(), "Should be empty");
TASKQUEUE_STATS_ONLY(task_queues()->print_taskqueue_stats());
TASKQUEUE_STATS_ONLY(task_queues()->reset_taskqueue_stats());
-
- // Resize Metaspace
- MetaspaceGC::compute_new_size();
}
// Weak Reference Closures
@@ -556,26 +545,6 @@
void do_oop(oop* p) { do_oop_work(p); }
};
-class ShenandoahWeakAssertNotForwardedClosure : public OopClosure {
-private:
- template <class T>
- inline void do_oop_work(T* p) {
-#ifdef ASSERT
- T o = RawAccess<>::oop_load(p);
- if (!CompressedOops::is_null(o)) {
- oop obj = CompressedOops::decode_not_null(o);
- shenandoah_assert_not_forwarded(p, obj);
- }
-#endif
- }
-
-public:
- ShenandoahWeakAssertNotForwardedClosure() {}
-
- void do_oop(narrowOop* p) { do_oop_work(p); }
- void do_oop(oop* p) { do_oop_work(p); }
-};
-
class ShenandoahRefProcTaskProxy : public AbstractGangTask {
private:
AbstractRefProcTaskExecutor::ProcessTask& _proc_task;
@@ -655,21 +624,6 @@
}
-// Process leftover weak oops: update them, if needed or assert they do not
-// need updating otherwise.
-// Weak processor API requires us to visit the oops, even if we are not doing
-// anything to them.
-void ShenandoahConcurrentMark::weak_roots_work() {
- WorkGang* workers = _heap->workers();
- OopClosure* keep_alive = &do_nothing_cl;
-#ifdef ASSERT
- ShenandoahWeakAssertNotForwardedClosure verify_cl;
- keep_alive = &verify_cl;
-#endif
- ShenandoahIsAliveClosure is_alive;
- WeakProcessor::weak_oops_do(workers, &is_alive, keep_alive, 1);
-}
-
void ShenandoahConcurrentMark::weak_refs_work_doit(bool full_gc) {
ReferenceProcessor* rp = _heap->ref_processor();
--- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -87,8 +87,6 @@
void weak_refs_work(bool full_gc);
void weak_refs_work_doit(bool full_gc);
- void weak_roots_work();
-
public:
void preclean_weak_refs();
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -29,7 +29,6 @@
#include "gc/shared/gcTimer.hpp"
#include "gc/shared/gcTraceTime.inline.hpp"
#include "gc/shared/memAllocator.hpp"
-#include "gc/shared/parallelCleaning.hpp"
#include "gc/shared/plab.hpp"
#include "gc/shenandoah/shenandoahAllocTracker.hpp"
@@ -53,6 +52,7 @@
#include "gc/shenandoah/shenandoahNormalMode.hpp"
#include "gc/shenandoah/shenandoahOopClosures.inline.hpp"
#include "gc/shenandoah/shenandoahPacer.inline.hpp"
+#include "gc/shenandoah/shenandoahParallelCleaning.inline.hpp"
#include "gc/shenandoah/shenandoahPassiveMode.hpp"
#include "gc/shenandoah/shenandoahRootProcessor.inline.hpp"
#include "gc/shenandoah/shenandoahStringDedup.hpp"
@@ -1950,16 +1950,8 @@
}
}
-void ShenandoahHeap::unload_classes_and_cleanup_tables(bool full_gc) {
- assert(heuristics()->can_unload_classes(), "Class unloading should be enabled");
-
- ShenandoahGCPhase root_phase(full_gc ?
- ShenandoahPhaseTimings::full_gc_purge :
- ShenandoahPhaseTimings::purge);
-
- ShenandoahIsAliveSelector alive;
- BoolObjectClosure* is_alive = alive.is_alive_closure();
-
+void ShenandoahHeap::stw_unload_classes(bool full_gc) {
+ if (!unload_classes()) return;
bool purged_class;
// Unload classes and purge SystemDictionary.
@@ -1974,17 +1966,61 @@
ShenandoahGCPhase phase(full_gc ?
ShenandoahPhaseTimings::full_gc_purge_par :
ShenandoahPhaseTimings::purge_par);
- uint active = _workers->active_workers();
- ParallelCleaningTask unlink_task(is_alive, active, purged_class, true);
+ ShenandoahIsAliveSelector is_alive;
+ uint num_workers = _workers->active_workers();
+ ShenandoahClassUnloadingTask unlink_task(is_alive.is_alive_closure(), num_workers, purged_class);
_workers->run_task(&unlink_task);
}
{
ShenandoahGCPhase phase(full_gc ?
- ShenandoahPhaseTimings::full_gc_purge_cldg :
- ShenandoahPhaseTimings::purge_cldg);
+ ShenandoahPhaseTimings::full_gc_purge_cldg :
+ ShenandoahPhaseTimings::purge_cldg);
ClassLoaderDataGraph::purge();
}
+ // Resize and verify metaspace
+ MetaspaceGC::compute_new_size();
+ MetaspaceUtils::verify_metrics();
+}
+
+// Process leftover weak oops: update them, if needed or assert they do not
+// need updating otherwise.
+// Weak processor API requires us to visit the oops, even if we are not doing
+// anything to them.
+void ShenandoahHeap::stw_process_weak_roots(bool full_gc) {
+ ShenandoahGCPhase root_phase(full_gc ?
+ ShenandoahPhaseTimings::full_gc_purge :
+ ShenandoahPhaseTimings::purge);
+ uint num_workers = _workers->active_workers();
+ ShenandoahPhaseTimings::Phase timing_phase = full_gc ?
+ ShenandoahPhaseTimings::full_gc_purge_par :
+ ShenandoahPhaseTimings::purge_par;
+ // Cleanup weak roots
+ ShenandoahGCPhase phase(timing_phase);
+ if (has_forwarded_objects()) {
+ ShenandoahForwardedIsAliveClosure is_alive;
+ ShenandoahUpdateRefsClosure keep_alive;
+ ShenandoahParallelWeakRootsCleaningTask<ShenandoahForwardedIsAliveClosure, ShenandoahUpdateRefsClosure>
+ cleaning_task(&is_alive, &keep_alive, num_workers);
+ _workers->run_task(&cleaning_task);
+ } else {
+ ShenandoahIsAliveClosure is_alive;
+#ifdef ASSERT
+ ShenandoahAssertNotForwardedClosure verify_cl;
+ ShenandoahParallelWeakRootsCleaningTask<ShenandoahIsAliveClosure, ShenandoahAssertNotForwardedClosure>
+ cleaning_task(&is_alive, &verify_cl, num_workers);
+#else
+ ShenandoahParallelWeakRootsCleaningTask<ShenandoahIsAliveClosure, DoNothingClosure>
+ cleaning_task(&is_alive, &do_nothing_cl, num_workers);
+#endif
+ _workers->run_task(&cleaning_task);
+ }
+}
+
+void ShenandoahHeap::parallel_cleaning(bool full_gc) {
+ assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
+ stw_process_weak_roots(full_gc);
+ stw_unload_classes(full_gc);
}
void ShenandoahHeap::set_has_forwarded_objects(bool cond) {
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -512,9 +512,12 @@
void set_unload_classes(bool uc);
bool unload_classes() const;
- // Delete entries for dead interned string and clean up unreferenced symbols
- // in symbol table, possibly in parallel.
- void unload_classes_and_cleanup_tables(bool full_gc);
+ // Perform STW class unloading and weak root cleaning
+ void parallel_cleaning(bool full_gc);
+
+private:
+ void stw_unload_classes(bool full_gc);
+ void stw_process_weak_roots(bool full_gc);
// ---------- Generic interface hooks
// Minor things that super-interface expects us to implement to play nice with
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019, Red Hat, Inc. All rights reserved.
+ *
+ * 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 "gc/shenandoah/shenandoahClosures.inline.hpp"
+#include "gc/shenandoah/shenandoahCodeRoots.hpp"
+#include "gc/shenandoah/shenandoahEvacOOMHandler.hpp"
+#include "gc/shenandoah/shenandoahParallelCleaning.hpp"
+#include "runtime/safepoint.hpp"
+
+ShenandoahClassUnloadingTask::ShenandoahClassUnloadingTask(BoolObjectClosure* is_alive,
+ uint num_workers,
+ bool unloading_occurred) :
+ AbstractGangTask("Parallel Class Unloading Task"),
+ _unloading_occurred(unloading_occurred),
+ _code_cache_task(num_workers, is_alive, unloading_occurred),
+ _klass_cleaning_task() {
+ assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
+}
+
+void ShenandoahClassUnloadingTask::work(uint worker_id) {
+ ShenandoahEvacOOMScope scope;
+ _code_cache_task.work(worker_id);
+ // Clean all klasses that were not unloaded.
+ // The weak metadata in klass doesn't need to be
+ // processed if there was no unloading.
+ if (_unloading_occurred) {
+ _klass_cleaning_task.work();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019, Red Hat, Inc. All rights reserved.
+ *
+ * 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_GC_SHENANDOAH_SHENANDOAHPARALLELCLEANING_HPP
+#define SHARE_GC_SHENANDOAH_SHENANDOAHPARALLELCLEANING_HPP
+
+#include "gc/shared/parallelCleaning.hpp"
+#include "gc/shared/weakProcessor.hpp"
+#include "gc/shared/weakProcessorPhaseTimes.hpp"
+#include "gc/shared/workgroup.hpp"
+#include "memory/iterator.hpp"
+
+// Perform weak root cleaning at a pause
+template <typename IsAlive, typename KeepAlive>
+class ShenandoahParallelWeakRootsCleaningTask : public AbstractGangTask {
+protected:
+ WeakProcessor::Task _weak_processing_task;
+ IsAlive* _is_alive;
+ KeepAlive* _keep_alive;
+public:
+ ShenandoahParallelWeakRootsCleaningTask(IsAlive* is_alive, KeepAlive* keep_alive, uint num_workers);
+ ~ShenandoahParallelWeakRootsCleaningTask();
+
+ void work(uint worker_id);
+};
+
+// Perform class unloading at a pause
+class ShenandoahClassUnloadingTask : public AbstractGangTask {
+private:
+ bool _unloading_occurred;
+ CodeCacheUnloadingTask _code_cache_task;
+ KlassCleaningTask _klass_cleaning_task;
+public:
+ ShenandoahClassUnloadingTask(BoolObjectClosure* is_alive,
+ uint num_workers,
+ bool unloading_occurred);
+
+ void work(uint worker_id);
+};
+
+#endif // SHARE_GC_SHENANDOAH_SHENANDOAHPARALLELCLEANING_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.inline.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2019, Red Hat, Inc. All rights reserved.
+ *
+ * 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_GC_SHENANDOAH_SHENANDOAHPARALLELCLEANING_INLINE_HPP
+#define SHARE_GC_SHENANDOAH_SHENANDOAHPARALLELCLEANING_INLINE_HPP
+
+#include "gc/shared/weakProcessor.inline.hpp"
+#include "gc/shenandoah/shenandoahHeap.hpp"
+#include "gc/shenandoah/shenandoahParallelCleaning.hpp"
+#include "gc/shenandoah/shenandoahUtils.hpp"
+#include "runtime/thread.hpp"
+#include "runtime/safepoint.hpp"
+
+template<typename IsAlive, typename KeepAlive>
+ShenandoahParallelWeakRootsCleaningTask<IsAlive, KeepAlive>::ShenandoahParallelWeakRootsCleaningTask(IsAlive* is_alive,
+ KeepAlive* keep_alive,
+ uint num_workers) :
+ AbstractGangTask("Parallel Weak Root Cleaning Task"),
+ _weak_processing_task(num_workers),
+ _is_alive(is_alive), _keep_alive(keep_alive) {
+ assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
+
+ if (ShenandoahStringDedup::is_enabled()) {
+ StringDedup::gc_prologue(false);
+ }
+}
+
+template<typename IsAlive, typename KeepAlive>
+ShenandoahParallelWeakRootsCleaningTask<IsAlive, KeepAlive>::~ShenandoahParallelWeakRootsCleaningTask() {
+ if (StringDedup::is_enabled()) {
+ StringDedup::gc_epilogue();
+ }
+}
+
+template<typename IsAlive, typename KeepAlive>
+void ShenandoahParallelWeakRootsCleaningTask<IsAlive, KeepAlive>::work(uint worker_id) {
+ _weak_processing_task.work<IsAlive, KeepAlive>(worker_id, _is_alive, _keep_alive);
+
+ if (ShenandoahStringDedup::is_enabled()) {
+ ShenandoahStringDedup::parallel_oops_do(_is_alive, _keep_alive, worker_id);
+ }
+}
+
+#endif // SHARE_GC_SHENANDOAH_SHENANDOAHPARALLELCLEANING_INLINE_HPP
--- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -52,11 +52,16 @@
}
}
+// Default the second argument for SD::oops_do.
+static void system_dictionary_oops_do(OopClosure* cl) {
+ SystemDictionary::oops_do(cl);
+}
+
ShenandoahSerialRoots::ShenandoahSerialRoots() :
_universe_root(&Universe::oops_do, ShenandoahPhaseTimings::UniverseRoots),
_object_synchronizer_root(&ObjectSynchronizer::oops_do, ShenandoahPhaseTimings::ObjectSynchronizerRoots),
_management_root(&Management::oops_do, ShenandoahPhaseTimings::ManagementRoots),
- _system_dictionary_root(&SystemDictionary::oops_do, ShenandoahPhaseTimings::SystemDictionaryRoots),
+ _system_dictionary_root(&system_dictionary_oops_do, ShenandoahPhaseTimings::SystemDictionaryRoots),
_jvmti_root(&JvmtiExport::oops_do, ShenandoahPhaseTimings::JVMTIRoots) {
}
--- a/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -75,16 +75,20 @@
assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
assert(is_enabled(), "String deduplication not enabled");
- ShenandoahWorkerTimings* worker_times = ShenandoahHeap::heap()->phase_timings()->worker_times();
-
StringDedupUnlinkOrOopsDoClosure sd_cl(is_alive, cl);
+ if (ShenandoahGCPhase::is_root_work_phase()) {
+ ShenandoahWorkerTimings* worker_times = ShenandoahHeap::heap()->phase_timings()->worker_times();
+ {
+ ShenandoahWorkerTimingsTracker x(worker_times, ShenandoahPhaseTimings::StringDedupQueueRoots, worker_id);
+ StringDedupQueue::unlink_or_oops_do(&sd_cl);
+ }
- {
- ShenandoahWorkerTimingsTracker x(worker_times, ShenandoahPhaseTimings::StringDedupQueueRoots, worker_id);
+ {
+ ShenandoahWorkerTimingsTracker x(worker_times, ShenandoahPhaseTimings::StringDedupTableRoots, worker_id);
+ StringDedupTable::unlink_or_oops_do(&sd_cl, worker_id);
+ }
+ } else {
StringDedupQueue::unlink_or_oops_do(&sd_cl);
- }
- {
- ShenandoahWorkerTimingsTracker x(worker_times, ShenandoahPhaseTimings::StringDedupTableRoots, worker_id);
StringDedupTable::unlink_or_oops_do(&sd_cl, worker_id);
}
}
--- a/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -28,7 +28,6 @@
#include "gc/shared/referenceProcessor.hpp"
#include "gc/shared/referenceProcessorPhaseTimes.hpp"
#include "gc/shared/workgroup.hpp"
-#include "gc/shared/weakProcessor.inline.hpp"
#include "gc/shenandoah/shenandoahBarrierSet.hpp"
#include "gc/shenandoah/shenandoahClosures.inline.hpp"
#include "gc/shenandoah/shenandoahCodeRoots.hpp"
@@ -596,9 +595,7 @@
if (!_heap->cancelled_gc()) {
fixup_roots();
- if (_heap->unload_classes()) {
- _heap->unload_classes_and_cleanup_tables(false);
- }
+ _heap->parallel_cleaning(false);
}
if (!_heap->cancelled_gc()) {
--- a/src/hotspot/share/gc/z/zNMethod.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/z/zNMethod.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -261,6 +261,24 @@
Atomic::store(true, &_failed);
}
+ void unlink(nmethod* nm) {
+ // Unlinking of the dependencies must happen before the
+ // handshake separating unlink and purge.
+ nm->flush_dependencies(false /* delete_immediately */);
+
+ // We don't need to take the lock when unlinking nmethods from
+ // the Method, because it is only concurrently unlinked by
+ // the entry barrier, which acquires the per nmethod lock.
+ nm->unlink_from_method(false /* acquire_lock */);
+
+ if (nm->is_osr_method()) {
+ // Invalidate the osr nmethod before the handshake. The nmethod
+ // will be made unloaded after the handshake. Then invalidate_osr_method()
+ // will be called again, which will be a no-op.
+ nm->invalidate_osr_method();
+ }
+ }
+
public:
ZNMethodUnlinkClosure(bool unloading_occurred) :
_unloading_occurred(unloading_occurred),
@@ -278,14 +296,7 @@
ZLocker<ZReentrantLock> locker(ZNMethod::lock_for_nmethod(nm));
if (nm->is_unloading()) {
- // Unlinking of the dependencies must happen before the
- // handshake separating unlink and purge.
- nm->flush_dependencies(false /* delete_immediately */);
-
- // We don't need to take the lock when unlinking nmethods from
- // the Method, because it is only concurrently unlinked by
- // the entry barrier, which acquires the per nmethod lock.
- nm->unlink_from_method(false /* acquire_lock */);
+ unlink(nm);
return;
}
--- a/src/hotspot/share/gc/z/zRootsIterator.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/z/zRootsIterator.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -69,6 +69,7 @@
static const ZStatSubPhase ZSubPhaseConcurrentRoots("Concurrent Roots");
static const ZStatSubPhase ZSubPhaseConcurrentRootsTeardown("Concurrent Roots Teardown");
static const ZStatSubPhase ZSubPhaseConcurrentRootsJNIHandles("Concurrent Roots JNIHandles");
+static const ZStatSubPhase ZSubPhaseConcurrentRootsVMHandles("Concurrent Roots VMHandles");
static const ZStatSubPhase ZSubPhaseConcurrentRootsClassLoaderDataGraph("Concurrent Roots ClassLoaderDataGraph");
static const ZStatSubPhase ZSubPhasePauseWeakRootsSetup("Pause Weak Roots Setup");
@@ -231,7 +232,8 @@
void ZRootsIterator::do_system_dictionary(ZRootsIteratorClosure* cl) {
ZStatTimer timer(ZSubPhasePauseRootsSystemDictionary);
- SystemDictionary::oops_do(cl);
+ // Handles are processed via _vm_handles.
+ SystemDictionary::oops_do(cl, false /* include_handles */);
}
void ZRootsIterator::do_threads(ZRootsIteratorClosure* cl) {
@@ -264,8 +266,10 @@
ZConcurrentRootsIterator::ZConcurrentRootsIterator(int cld_claim) :
_jni_handles_iter(JNIHandles::global_handles()),
+ _vm_handles_iter(SystemDictionary::vm_global_oop_storage()),
_cld_claim(cld_claim),
_jni_handles(this),
+ _vm_handles(this),
_class_loader_data_graph(this) {
ZStatTimer timer(ZSubPhaseConcurrentRootsSetup);
}
@@ -279,6 +283,11 @@
_jni_handles_iter.oops_do(cl);
}
+void ZConcurrentRootsIterator::do_vm_handles(ZRootsIteratorClosure* cl) {
+ ZStatTimer timer(ZSubPhaseConcurrentRootsVMHandles);
+ _vm_handles_iter.oops_do(cl);
+}
+
void ZConcurrentRootsIterator::do_class_loader_data_graph(ZRootsIteratorClosure* cl) {
ZStatTimer timer(ZSubPhaseConcurrentRootsClassLoaderDataGraph);
CLDToOopClosure cld_cl(cl, _cld_claim);
@@ -288,6 +297,7 @@
void ZConcurrentRootsIterator::oops_do(ZRootsIteratorClosure* cl) {
ZStatTimer timer(ZSubPhaseConcurrentRoots);
_jni_handles.oops_do(cl);
+ _vm_handles.oops_do(cl),
_class_loader_data_graph.oops_do(cl);
}
--- a/src/hotspot/share/gc/z/zRootsIterator.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/gc/z/zRootsIterator.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -112,12 +112,15 @@
class ZConcurrentRootsIterator {
private:
ZOopStorageIterator _jni_handles_iter;
+ ZOopStorageIterator _vm_handles_iter;
int _cld_claim;
void do_jni_handles(ZRootsIteratorClosure* cl);
+ void do_vm_handles(ZRootsIteratorClosure* cl);
void do_class_loader_data_graph(ZRootsIteratorClosure* cl);
ZParallelOopsDo<ZConcurrentRootsIterator, &ZConcurrentRootsIterator::do_jni_handles> _jni_handles;
+ ZParallelOopsDo<ZConcurrentRootsIterator, &ZConcurrentRootsIterator::do_vm_handles> _vm_handles;
ZParallelOopsDo<ZConcurrentRootsIterator, &ZConcurrentRootsIterator::do_class_loader_data_graph> _class_loader_data_graph;
public:
--- a/src/hotspot/share/include/cds.h Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/include/cds.h Thu Jul 25 08:24:01 2019 -0400
@@ -33,10 +33,10 @@
//
// Also, this is a C header file. Do not use C++ here.
-#define NUM_CDS_REGIONS 9
+#define NUM_CDS_REGIONS 8 // this must be the same as MetaspaceShared::n_regions
#define CDS_ARCHIVE_MAGIC 0xf00baba2
#define CDS_DYNAMIC_ARCHIVE_MAGIC 0xf00baba8
-#define CURRENT_CDS_ARCHIVE_VERSION 5
+#define CURRENT_CDS_ARCHIVE_VERSION 6
#define INVALID_CDS_ARCHIVE_VERSION -1
struct CDSFileMapRegion {
--- a/src/hotspot/share/jfr/leakprofiler/chains/rootSetClosure.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/jfr/leakprofiler/chains/rootSetClosure.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -42,9 +42,6 @@
#include "runtime/thread.hpp"
#include "services/management.hpp"
#include "utilities/align.hpp"
-#if INCLUDE_JVMCI
-#include "jvmci/jvmci.hpp"
-#endif
template <typename Delegate>
RootSetClosure<Delegate>::RootSetClosure(Delegate* delegate) : _delegate(delegate) {}
@@ -96,7 +93,6 @@
Management::oops_do(this);
StringTable::oops_do(this);
AOTLoader::oops_do(this);
- JVMCI_ONLY(JVMCI::oops_do(this);)
}
template class RootSetClosure<BFSClosure>;
--- a/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -41,9 +41,6 @@
#include "runtime/vframe_hp.hpp"
#include "services/management.hpp"
#include "utilities/growableArray.hpp"
-#if INCLUDE_JVMCI
-#include "jvmci/jvmci.hpp"
-#endif
class ReferenceLocateClosure : public OopClosure {
protected:
@@ -106,7 +103,6 @@
bool do_management_roots();
bool do_string_table_roots();
bool do_aot_loader_roots();
- JVMCI_ONLY(bool do_jvmci_roots();)
bool do_roots();
@@ -193,15 +189,6 @@
return rcl.complete();
}
-#if INCLUDE_JVMCI
-bool ReferenceToRootClosure::do_jvmci_roots() {
- assert(!complete(), "invariant");
- ReferenceLocateClosure rcl(_callback, OldObjectRoot::_jvmci, OldObjectRoot::_type_undetermined, NULL);
- JVMCI::oops_do(&rcl);
- return rcl.complete();
-}
-#endif
-
bool ReferenceToRootClosure::do_roots() {
assert(!complete(), "invariant");
assert(OldObjectRoot::_system_undetermined == _info._system, "invariant");
@@ -252,13 +239,6 @@
return true;
}
-#if INCLUDE_JVMCI
- if (do_jvmci_roots()) {
- _complete = true;
- return true;
- }
-#endif
-
return false;
}
--- a/src/hotspot/share/jvmci/jvmci.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/jvmci/jvmci.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -24,7 +24,7 @@
#include "precompiled.hpp"
#include "classfile/systemDictionary.hpp"
#include "gc/shared/collectedHeap.hpp"
-#include "gc/shared/oopStorage.inline.hpp"
+#include "gc/shared/oopStorage.hpp"
#include "jvmci/jvmci.hpp"
#include "jvmci/jvmciJavaClasses.hpp"
#include "jvmci/jvmciRuntime.hpp"
@@ -58,9 +58,7 @@
}
void JVMCI::initialize_globals() {
- _object_handles = new OopStorage("JVMCI Global Oop Handles",
- JVMCIGlobalAlloc_lock,
- JVMCIGlobalActive_lock);
+ _object_handles = SystemDictionary::vm_global_oop_storage();
_metadata_handles = MetadataHandleBlock::allocate_block();
if (UseJVMCINativeLibrary) {
// There are two runtimes.
@@ -93,6 +91,14 @@
return res;
}
+void JVMCI::destroy_global(jobject handle) {
+ // Assert before nulling out, for better debugging.
+ assert(is_global_handle(handle), "precondition");
+ oop* oop_ptr = reinterpret_cast<oop*>(handle);
+ NativeAccess<>::oop_store(oop_ptr, (oop)NULL);
+ object_handles()->release(oop_ptr);
+}
+
bool JVMCI::is_global_handle(jobject handle) {
const oop* ptr = reinterpret_cast<oop*>(handle);
return object_handles()->allocation_status(ptr) == OopStorage::ALLOCATED_ENTRY;
@@ -115,12 +121,6 @@
_metadata_handles->chain_free_list(handle);
}
-void JVMCI::oops_do(OopClosure* f) {
- if (_object_handles != NULL) {
- _object_handles->oops_do(f);
- }
-}
-
void JVMCI::metadata_do(void f(Metadata*)) {
if (_metadata_handles != NULL) {
_metadata_handles->metadata_do(f);
--- a/src/hotspot/share/jvmci/jvmci.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/jvmci/jvmci.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -74,8 +74,6 @@
static void metadata_do(void f(Metadata*));
- static void oops_do(OopClosure* f);
-
static void shutdown();
static bool shutdown_called();
@@ -92,6 +90,7 @@
static void initialize_compiler(TRAPS);
static jobject make_global(const Handle& obj);
+ static void destroy_global(jobject handle);
static bool is_global_handle(jobject handle);
static jmetadata allocate_handle(const methodHandle& handle);
--- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -2208,8 +2208,7 @@
C2V_VMENTRY(void, deleteGlobalHandle, (JNIEnv* env, jobject, jlong h))
jobject handle = (jobject)(address)h;
if (handle != NULL) {
- assert(JVMCI::is_global_handle(handle), "Invalid delete of global JNI handle");
- *((oop*)handle) = NULL; // Mark the handle as deleted, allocate will reuse it
+ JVMCI::destroy_global(handle);
}
}
--- a/src/hotspot/share/libadt/set.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/libadt/set.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -43,7 +43,6 @@
// The caller must deallocate the string.
char *Set::setstr() const
{
- if( this == NULL ) return os::strdup("{no set}");
Set &set = clone(); // Virtually copy the basic set.
set.Sort(); // Sort elements for in-order retrieval
--- a/src/hotspot/share/memory/allocation.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/memory/allocation.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -259,25 +259,6 @@
st->print("AllocatedObj(" INTPTR_FORMAT ")", p2i(this));
}
-AllocStats::AllocStats() {
- start_mallocs = os::num_mallocs;
- start_frees = os::num_frees;
- start_malloc_bytes = os::alloc_bytes;
- start_mfree_bytes = os::free_bytes;
- start_res_bytes = Arena::_bytes_allocated;
-}
-
-julong AllocStats::num_mallocs() { return os::num_mallocs - start_mallocs; }
-julong AllocStats::alloc_bytes() { return os::alloc_bytes - start_malloc_bytes; }
-julong AllocStats::num_frees() { return os::num_frees - start_frees; }
-julong AllocStats::free_bytes() { return os::free_bytes - start_mfree_bytes; }
-julong AllocStats::resource_bytes() { return Arena::_bytes_allocated - start_res_bytes; }
-void AllocStats::print() {
- tty->print_cr(UINT64_FORMAT " mallocs (" UINT64_FORMAT "MB), "
- UINT64_FORMAT " frees (" UINT64_FORMAT "MB), " UINT64_FORMAT "MB resrc",
- num_mallocs(), alloc_bytes()/M, num_frees(), free_bytes()/M, resource_bytes()/M);
-}
-
ReallocMark::ReallocMark() {
#ifdef ASSERT
Thread *thread = Thread::current();
--- a/src/hotspot/share/memory/allocation.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/memory/allocation.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -507,23 +507,6 @@
#define FREE_C_HEAP_OBJ(objname)\
FreeHeap((char*)objname);
-// for statistics
-#ifndef PRODUCT
-class AllocStats : StackObj {
- julong start_mallocs, start_frees;
- julong start_malloc_bytes, start_mfree_bytes, start_res_bytes;
- public:
- AllocStats();
-
- julong num_mallocs(); // since creation of receiver
- julong alloc_bytes();
- julong num_frees();
- julong free_bytes();
- julong resource_bytes();
- void print();
-};
-#endif
-
//------------------------------ReallocMark---------------------------------
// Code which uses REALLOC_RESOURCE_ARRAY should check an associated
--- a/src/hotspot/share/memory/allocation.inline.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/memory/allocation.inline.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -34,16 +34,13 @@
// Explicit C-heap memory management
#ifndef PRODUCT
-// Increments unsigned long value for statistics (not atomic on MP).
+// Increments unsigned long value for statistics (not atomic on MP, but avoids word-tearing on 32 bit).
inline void inc_stat_counter(volatile julong* dest, julong add_value) {
-#if defined(SPARC) || defined(X86)
- // Sparc and X86 have atomic jlong (8 bytes) instructions
+#ifdef _LP64
+ *dest += add_value;
+#else
julong value = Atomic::load(dest);
- value += add_value;
- Atomic::store(value, dest);
-#else
- // possible word-tearing during load/store
- *dest += add_value;
+ Atomic::store(value + add_value, dest);
#endif
}
#endif
--- a/src/hotspot/share/memory/arena.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/memory/arena.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -475,10 +475,6 @@
#ifndef PRODUCT
-julong Arena::_bytes_allocated = 0;
-
-void Arena::inc_bytes_allocated(size_t x) { inc_stat_counter(&_bytes_allocated, x); }
-
// debugging code
inline void Arena::free_all(char** start, char** end) {
for (char** p = start; p < end; p++) if (*p) os::free(*p);
--- a/src/hotspot/share/memory/arena.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/memory/arena.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -106,11 +106,8 @@
void* grow(size_t x, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM);
size_t _size_in_bytes; // Size of arena (used for native memory tracking)
- NOT_PRODUCT(static julong _bytes_allocated;) // total #bytes allocated since start
- friend class AllocStats;
debug_only(void* malloc(size_t size);)
debug_only(void* internal_malloc_4(size_t x);)
- NOT_PRODUCT(void inc_bytes_allocated(size_t x);)
void signal_out_of_memory(size_t request, const char* whence) const;
@@ -148,7 +145,6 @@
debug_only(if (UseMallocOnly) return malloc(x);)
if (!check_for_overflow(x, "Arena::Amalloc", alloc_failmode))
return NULL;
- NOT_PRODUCT(inc_bytes_allocated(x);)
if (_hwm + x > _max) {
return grow(x, alloc_failmode);
} else {
@@ -163,7 +159,6 @@
debug_only(if (UseMallocOnly) return malloc(x);)
if (!check_for_overflow(x, "Arena::Amalloc_4", alloc_failmode))
return NULL;
- NOT_PRODUCT(inc_bytes_allocated(x);)
if (_hwm + x > _max) {
return grow(x, alloc_failmode);
} else {
@@ -185,7 +180,6 @@
#endif
if (!check_for_overflow(x, "Arena::Amalloc_D", alloc_failmode))
return NULL;
- NOT_PRODUCT(inc_bytes_allocated(x);)
if (_hwm + x > _max) {
return grow(x, alloc_failmode); // grow() returns a result aligned >= 8 bytes.
} else {
--- a/src/hotspot/share/memory/filemap.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/memory/filemap.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -155,6 +155,8 @@
const char *vm_version = VM_Version::internal_vm_info_string();
const int version_len = (int)strlen(vm_version);
+ memset(header_version, 0, JVM_IDENT_MAX);
+
if (version_len < (JVM_IDENT_MAX-1)) {
strcpy(header_version, vm_version);
@@ -170,6 +172,8 @@
sprintf(&header_version[JVM_IDENT_MAX-9], "%08x", hash);
header_version[JVM_IDENT_MAX-1] = 0; // Null terminate.
}
+
+ assert(header_version[JVM_IDENT_MAX-1] == 0, "must be");
}
FileMapInfo::FileMapInfo(bool is_static) {
@@ -891,10 +895,59 @@
fail_continue("Unable to read the file header.");
return false;
}
+
+ if (!Arguments::has_jimage()) {
+ FileMapInfo::fail_continue("The shared archive file cannot be used with an exploded module build.");
+ return false;
+ }
+
+ unsigned int expected_magic = is_static ? CDS_ARCHIVE_MAGIC : CDS_DYNAMIC_ARCHIVE_MAGIC;
+ if (_header->_magic != expected_magic) {
+ log_info(cds)("_magic expected: 0x%08x", expected_magic);
+ log_info(cds)(" actual: 0x%08x", _header->_magic);
+ FileMapInfo::fail_continue("The shared archive file has a bad magic number.");
+ return false;
+ }
+
if (_header->_version != CURRENT_CDS_ARCHIVE_VERSION) {
+ log_info(cds)("_version expected: %d", CURRENT_CDS_ARCHIVE_VERSION);
+ log_info(cds)(" actual: %d", _header->_version);
fail_continue("The shared archive file has the wrong version.");
return false;
}
+
+ if (_header->_header_size != sz) {
+ log_info(cds)("_header_size expected: " SIZE_FORMAT, sz);
+ log_info(cds)(" actual: " SIZE_FORMAT, _header->_header_size);
+ FileMapInfo::fail_continue("The shared archive file has an incorrect header size.");
+ return false;
+ }
+
+ if (_header->_jvm_ident[JVM_IDENT_MAX-1] != 0) {
+ FileMapInfo::fail_continue("JVM version identifier is corrupted.");
+ return false;
+ }
+
+ char header_version[JVM_IDENT_MAX];
+ get_header_version(header_version);
+ if (strncmp(_header->_jvm_ident, header_version, JVM_IDENT_MAX-1) != 0) {
+ log_info(cds)("_jvm_ident expected: %s", header_version);
+ log_info(cds)(" actual: %s", _header->_jvm_ident);
+ FileMapInfo::fail_continue("The shared archive file was created by a different"
+ " version or build of HotSpot");
+ return false;
+ }
+
+ if (VerifySharedSpaces) {
+ int expected_crc = _header->compute_crc();
+ if (expected_crc != _header->_crc) {
+ log_info(cds)("_crc expected: %d", expected_crc);
+ log_info(cds)(" actual: %d", _header->_crc);
+ FileMapInfo::fail_continue("Header checksum verification failed.");
+ return false;
+ }
+ }
+
_file_offset = n;
size_t info_size = _header->_paths_misc_info_size;
@@ -909,10 +962,6 @@
_file_offset += n + _header->_base_archive_name_size; // accounts for the size of _base_archive_name
if (is_static) {
- if (_header->_magic != CDS_ARCHIVE_MAGIC) {
- fail_continue("Incorrect static archive magic number");
- return false;
- }
// just checking the last region is sufficient since the archive is written
// in sequential order
size_t len = lseek(fd, 0, SEEK_END);
@@ -1750,33 +1799,7 @@
// This function should only be called during run time with UseSharedSpaces enabled.
bool FileMapHeader::validate() {
- if (VerifySharedSpaces && compute_crc() != _crc) {
- FileMapInfo::fail_continue("Header checksum verification failed.");
- return false;
- }
- if (!Arguments::has_jimage()) {
- FileMapInfo::fail_continue("The shared archive file cannot be used with an exploded module build.");
- return false;
- }
-
- if (_version != CURRENT_CDS_ARCHIVE_VERSION) {
- FileMapInfo::fail_continue("The shared archive file is the wrong version.");
- return false;
- }
- if (_magic != CDS_ARCHIVE_MAGIC && _magic != CDS_DYNAMIC_ARCHIVE_MAGIC) {
- FileMapInfo::fail_continue("The shared archive file has a bad magic number.");
- return false;
- }
- char header_version[JVM_IDENT_MAX];
- get_header_version(header_version);
- if (strncmp(_jvm_ident, header_version, JVM_IDENT_MAX-1) != 0) {
- log_info(class, path)("expected: %s", header_version);
- log_info(class, path)("actual: %s", _jvm_ident);
- FileMapInfo::fail_continue("The shared archive file was created by a different"
- " version or build of HotSpot");
- return false;
- }
if (_obj_alignment != ObjectAlignmentInBytes) {
FileMapInfo::fail_continue("The shared archive file's ObjectAlignmentInBytes of %d"
" does not equal the current ObjectAlignmentInBytes of " INTX_FORMAT ".",
--- a/src/hotspot/share/oops/constantPool.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/oops/constantPool.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -760,6 +760,10 @@
// return the method type signature in the error message
message = this_cp->method_type_signature_at(which);
break;
+ case JVM_CONSTANT_Dynamic:
+ // return the name of the condy in the error message
+ message = this_cp->uncached_name_ref_at(which);
+ break;
default:
ShouldNotReachHere();
}
--- a/src/hotspot/share/oops/method.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/oops/method.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -146,6 +146,12 @@
return adapter()->get_c2i_unverified_entry();
}
+address Method::get_c2i_no_clinit_check_entry() {
+ assert(VM_Version::supports_fast_class_init_checks(), "");
+ assert(adapter() != NULL, "must have");
+ return adapter()->get_c2i_no_clinit_check_entry();
+}
+
char* Method::name_and_sig_as_C_string() const {
return name_and_sig_as_C_string(constants()->pool_holder(), name(), signature());
}
@@ -842,10 +848,7 @@
if (comp_level == CompLevel_all) {
tty->print("all levels ");
} else {
- tty->print("levels ");
- for (int i = (int)CompLevel_none; i <= comp_level; i++) {
- tty->print("%d ", i);
- }
+ tty->print("level %d ", comp_level);
}
this->print_short_name(tty);
int size = this->code_size();
@@ -1048,7 +1051,7 @@
_c2i_entry ---------------------------------+->[c2i entry..]
_i2i_entry -------------+ _i2c_entry ---------------+-> [i2c entry..] |
_from_interpreted_entry | _c2i_unverified_entry | |
- | | | |
+ | | _c2i_no_clinit_check_entry| |
| | (_cds_entry_table: CODE) | |
| +->[0]: jmp _entry_table[0] --> (i2i_entry_for "zero_locals") | |
| | (allocated at run time) | |
--- a/src/hotspot/share/oops/method.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/oops/method.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -481,6 +481,7 @@
address get_i2c_entry();
address get_c2i_entry();
address get_c2i_unverified_entry();
+ address get_c2i_no_clinit_check_entry();
AdapterHandlerEntry* adapter() const {
return constMethod()->adapter();
}
--- a/src/hotspot/share/prims/cdsoffsets.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/prims/cdsoffsets.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -49,6 +49,7 @@
ADD_NEXT(_all, "FileMapHeader::_magic", offset_of(FileMapHeader, _magic)); \
ADD_NEXT(_all, "FileMapHeader::_crc", offset_of(FileMapHeader, _crc)); \
ADD_NEXT(_all, "FileMapHeader::_version", offset_of(FileMapHeader, _version)); \
+ ADD_NEXT(_all, "FileMapHeader::_jvm_ident", offset_of(FileMapHeader, _jvm_ident)); \
ADD_NEXT(_all, "FileMapHeader::_space[0]", offset_of(FileMapHeader, _space)); \
ADD_NEXT(_all, "CDSFileMapRegion::_crc", offset_of(CDSFileMapRegion, _crc)); \
ADD_NEXT(_all, "CDSFileMapRegion::_used", offset_of(CDSFileMapRegion, _used)); \
--- a/src/hotspot/share/prims/jvmtiTagMap.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/prims/jvmtiTagMap.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -63,9 +63,6 @@
#if INCLUDE_ZGC
#include "gc/z/zGlobals.hpp"
#endif
-#if INCLUDE_JVMCI
-#include "jvmci/jvmci.hpp"
-#endif
// JvmtiTagHashmapEntry
//
@@ -3042,14 +3039,6 @@
return false;
}
-#if INCLUDE_JVMCI
- blk.set_kind(JVMTI_HEAP_REFERENCE_OTHER);
- JVMCI::oops_do(&blk);
- if (blk.stopped()) {
- return false;
- }
-#endif
-
return true;
}
--- a/src/hotspot/share/runtime/arguments.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/runtime/arguments.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -550,18 +550,6 @@
{ "SharedReadOnlySize", JDK_Version::undefined(), JDK_Version::jdk(10), JDK_Version::undefined() },
{ "SharedMiscDataSize", JDK_Version::undefined(), JDK_Version::jdk(10), JDK_Version::undefined() },
{ "SharedMiscCodeSize", JDK_Version::undefined(), JDK_Version::jdk(10), JDK_Version::undefined() },
- { "ProfilerPrintByteCodeStatistics", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) },
- { "ProfilerRecordPC", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) },
- { "ProfileVM", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) },
- { "ProfileIntervals", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) },
- { "ProfileIntervalsTicks", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) },
- { "ProfilerCheckIntervals", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) },
- { "ProfilerNumberOfInterpretedMethods", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) },
- { "ProfilerNumberOfCompiledMethods", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) },
- { "ProfilerNumberOfStubMethods", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) },
- { "ProfilerNumberOfRuntimeStubNodes", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) },
- { "UseImplicitStableValues", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) },
- { "NeedsDeoptSuspend", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) },
{ "FailOverToOldVerifier", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
#ifdef TEST_VERIFY_SPECIAL_JVM_FLAGS
--- a/src/hotspot/share/runtime/globals.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/runtime/globals.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -466,9 +466,6 @@
develop(bool, UseMallocOnly, false, \
"Use only malloc/free for allocation (no resource area/arena)") \
\
- develop(bool, PrintMallocStatistics, false, \
- "Print malloc/free statistics") \
- \
develop(bool, ZapResourceArea, trueInDebug, \
"Zap freed resource/arena space with 0xABABABAB") \
\
--- a/src/hotspot/share/runtime/java.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/runtime/java.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -203,9 +203,6 @@
}
}
-AllocStats alloc_stats;
-
-
// General statistics printing (profiling ...)
void print_statistics() {
@@ -329,11 +326,6 @@
}
print_bytecode_count();
- if (PrintMallocStatistics) {
- tty->print("allocation stats: ");
- alloc_stats.print();
- tty->cr();
- }
if (PrintSystemDictionaryAtExit) {
ResourceMark rm;
--- a/src/hotspot/share/runtime/javaCalls.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/runtime/javaCalls.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -346,9 +346,6 @@
assert(!SafepointSynchronize::is_at_safepoint(), "call to Java code during VM operation");
assert(!thread->handle_area()->no_handle_mark_active(), "cannot call out to Java here");
-
- CHECK_UNHANDLED_OOPS_ONLY(thread->clear_unhandled_oops();)
-
#if INCLUDE_JVMCI
// Gets the nmethod (if any) that should be called instead of normal target
nmethod* alternative_target = args->alternative_target();
@@ -395,10 +392,6 @@
BasicType result_type = runtime_type_from(result);
bool oop_result_flag = (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY);
- // NOTE: if we move the computation of the result_val_address inside
- // the call to call_stub, the optimizer produces wrong code.
- intptr_t* result_val_address = (intptr_t*)(result->get_value_addr());
-
// Find receiver
Handle receiver = (!method->is_static()) ? args->receiver() : Handle();
@@ -436,6 +429,11 @@
{ JavaCallWrapper link(method, receiver, result, CHECK);
{ HandleMark hm(thread); // HandleMark used by HandleMarkCleaner
+ // NOTE: if we move the computation of the result_val_address inside
+ // the call to call_stub, the optimizer produces wrong code.
+ intptr_t* result_val_address = (intptr_t*)(result->get_value_addr());
+ intptr_t* parameter_address = args->parameters();
+
StubRoutines::call_stub()(
(address)&link,
// (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
@@ -443,7 +441,7 @@
result_type,
method(),
entry_point,
- args->parameters(),
+ parameter_address,
args->size_of_parameters(),
CHECK
);
--- a/src/hotspot/share/runtime/mutex.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/runtime/mutex.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -64,8 +64,8 @@
tty = access + 2,
special = tty + 1,
suspend_resume = special + 1,
- vmweak = suspend_resume + 2,
- leaf = vmweak + 2,
+ oopstorage = suspend_resume + 2,
+ leaf = oopstorage + 2,
safepoint = leaf + 10,
barrier = safepoint + 1,
nonleaf = barrier + 1,
--- a/src/hotspot/share/runtime/mutexLocker.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/runtime/mutexLocker.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -53,6 +53,8 @@
Mutex* StringTableWeakAlloc_lock = NULL;
Mutex* StringTableWeakActive_lock = NULL;
Mutex* JNIHandleBlockFreeList_lock = NULL;
+Mutex* VMGlobalAlloc_lock = NULL;
+Mutex* VMGlobalActive_lock = NULL;
Mutex* VMWeakAlloc_lock = NULL;
Mutex* VMWeakActive_lock = NULL;
Mutex* ResolvedMethodTableWeakAlloc_lock = NULL;
@@ -161,8 +163,6 @@
#if INCLUDE_JVMCI
Monitor* JVMCI_lock = NULL;
-Mutex* JVMCIGlobalAlloc_lock = NULL;
-Mutex* JVMCIGlobalActive_lock = NULL;
#endif
@@ -216,14 +216,17 @@
def(CGC_lock , PaddedMonitor, special, true, Monitor::_safepoint_check_never); // coordinate between fore- and background GC
def(STS_lock , PaddedMonitor, leaf, true, Monitor::_safepoint_check_never);
- def(VMWeakAlloc_lock , PaddedMutex , vmweak, true, Monitor::_safepoint_check_never);
- def(VMWeakActive_lock , PaddedMutex , vmweak-1, true, Monitor::_safepoint_check_never);
+ def(VMGlobalAlloc_lock , PaddedMutex , oopstorage, true, Monitor::_safepoint_check_never);
+ def(VMGlobalActive_lock , PaddedMutex , oopstorage-1,true, Monitor::_safepoint_check_never);
+
+ def(VMWeakAlloc_lock , PaddedMutex , oopstorage, true, Monitor::_safepoint_check_never);
+ def(VMWeakActive_lock , PaddedMutex , oopstorage-1,true, Monitor::_safepoint_check_never);
- def(StringTableWeakAlloc_lock , PaddedMutex , vmweak, true, Monitor::_safepoint_check_never);
- def(StringTableWeakActive_lock , PaddedMutex , vmweak-1, true, Monitor::_safepoint_check_never);
+ def(StringTableWeakAlloc_lock , PaddedMutex , oopstorage, true, Monitor::_safepoint_check_never);
+ def(StringTableWeakActive_lock , PaddedMutex , oopstorage-1,true, Monitor::_safepoint_check_never);
- def(ResolvedMethodTableWeakAlloc_lock , PaddedMutex , vmweak, true, Monitor::_safepoint_check_never);
- def(ResolvedMethodTableWeakActive_lock , PaddedMutex , vmweak-1, true, Monitor::_safepoint_check_never);
+ def(ResolvedMethodTableWeakAlloc_lock , PaddedMutex , oopstorage, true, Monitor::_safepoint_check_never);
+ def(ResolvedMethodTableWeakActive_lock , PaddedMutex , oopstorage-1, true, Monitor::_safepoint_check_never);
def(FullGCCount_lock , PaddedMonitor, leaf, true, Monitor::_safepoint_check_never); // in support of ExplicitGCInvokesConcurrent
if (UseG1GC) {
@@ -356,8 +359,6 @@
#if INCLUDE_JVMCI
def(JVMCI_lock , PaddedMonitor, nonleaf+2, true, Monitor::_safepoint_check_always);
- def(JVMCIGlobalAlloc_lock , PaddedMutex , nonleaf, true, Monitor::_safepoint_check_never);
- def(JVMCIGlobalActive_lock , PaddedMutex , nonleaf-1, true, Monitor::_safepoint_check_never);
#endif
def(DumpTimeTable_lock , PaddedMutex , leaf, true, Monitor::_safepoint_check_never);
#endif // INCLUDE_CDS
--- a/src/hotspot/share/runtime/mutexLocker.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/runtime/mutexLocker.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -46,6 +46,8 @@
extern Mutex* StringTableWeakAlloc_lock; // StringTable weak storage allocate list lock
extern Mutex* StringTableWeakActive_lock; // STringTable weak storage active list lock
extern Mutex* JNIHandleBlockFreeList_lock; // a lock on the JNI handle block free list
+extern Mutex* VMGlobalAlloc_lock; // VM Global Handles storage allocate list lock
+extern Mutex* VMGlobalActive_lock; // VM Global Handles storage active list lock
extern Mutex* VMWeakAlloc_lock; // VM Weak Handles storage allocate list lock
extern Mutex* VMWeakActive_lock; // VM Weak Handles storage active list lock
extern Mutex* ResolvedMethodTableWeakAlloc_lock; // ResolvedMethodTable weak storage allocate list
@@ -157,8 +159,6 @@
#if INCLUDE_JVMCI
extern Monitor* JVMCI_lock; // Monitor to control initialization of JVMCI
-extern Mutex* JVMCIGlobalAlloc_lock; // JVMCI global storage allocate list lock
-extern Mutex* JVMCIGlobalActive_lock; // JVMCI global storage active list lock
#endif
// A MutexLocker provides mutual exclusion with respect to a given mutex
--- a/src/hotspot/share/runtime/os.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/runtime/os.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -360,11 +360,33 @@
switch (sig) {
case SIGBREAK: {
+#if INCLUDE_SERVICES
// Check if the signal is a trigger to start the Attach Listener - in that
// case don't print stack traces.
- if (!DisableAttachMechanism && AttachListener::is_init_trigger()) {
- continue;
+ if (!DisableAttachMechanism) {
+ // Attempt to transit state to AL_INITIALIZING.
+ AttachListenerState cur_state = AttachListener::transit_state(AL_INITIALIZING, AL_NOT_INITIALIZED);
+ if (cur_state == AL_INITIALIZING) {
+ // Attach Listener has been started to initialize. Ignore this signal.
+ continue;
+ } else if (cur_state == AL_NOT_INITIALIZED) {
+ // Start to initialize.
+ if (AttachListener::is_init_trigger()) {
+ // Attach Listener has been initialized.
+ // Accept subsequent request.
+ continue;
+ } else {
+ // Attach Listener could not be started.
+ // So we need to transit the state to AL_NOT_INITIALIZED.
+ AttachListener::set_state(AL_NOT_INITIALIZED);
+ }
+ } else if (AttachListener::check_socket_file()) {
+ // Attach Listener has been started, but unix domain socket file
+ // does not exist. So restart Attach Listener.
+ continue;
+ }
}
+#endif
// Print stack traces
// Any SIGBREAK operations added here should make sure to flush
// the output stream (e.g. tty->flush()) after output. See 4803766.
--- a/src/hotspot/share/runtime/safepointVerifiers.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/runtime/safepointVerifiers.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -91,14 +91,12 @@
_activated(activated) {
_thread = Thread::current();
if (_activated) {
- _thread->_allow_allocation_count++;
_thread->_allow_safepoint_count++;
}
}
~NoSafepointVerifier() {
if (_activated) {
- _thread->_allow_allocation_count--;
_thread->_allow_safepoint_count--;
}
}
@@ -126,14 +124,12 @@
_nsv = nsv;
if (_nsv->_activated) {
- _nsv->_thread->_allow_allocation_count--;
_nsv->_thread->_allow_safepoint_count--;
}
}
~PauseNoSafepointVerifier() {
if (_nsv->_activated) {
- _nsv->_thread->_allow_allocation_count++;
_nsv->_thread->_allow_safepoint_count++;
}
}
@@ -144,32 +140,4 @@
#endif
};
-// A NoAllocVerifier object can be placed in methods where one assumes that
-// no allocation will occur. The destructor will verify this property
-// unless the constructor is called with argument false (not activated).
-//
-// The check will only be done in debug mode and if activated.
-// Note: this only makes sense at safepoints (otherwise, other threads may
-// allocate concurrently.)
-
-class NoAllocVerifier : public StackObj {
- private:
- bool _activated;
-
- public:
-#ifdef ASSERT
- NoAllocVerifier(bool activated = true) {
- _activated = activated;
- if (_activated) Thread::current()->_allow_allocation_count++;
- }
-
- ~NoAllocVerifier() {
- if (_activated) Thread::current()->_allow_allocation_count--;
- }
-#else
- NoAllocVerifier(bool activated = true) {}
- ~NoAllocVerifier() {}
-#endif
-};
-
#endif // SHARE_RUNTIME_SAFEPOINTVERIFIERS_HPP
--- a/src/hotspot/share/runtime/serviceThread.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/runtime/serviceThread.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -94,6 +94,7 @@
JNIHandles::global_handles(),
JNIHandles::weak_global_handles(),
StringTable::weak_storage(),
+ SystemDictionary::vm_global_oop_storage(),
SystemDictionary::vm_weak_oop_storage()
};
const size_t oopstorage_count = ARRAY_SIZE(oopstorages);
--- a/src/hotspot/share/runtime/sharedRuntime.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1446,7 +1446,19 @@
guarantee(callee != NULL && callee->is_method(), "bad handshake");
thread->set_vm_result_2(callee);
thread->set_callee_target(NULL);
- return callee->get_c2i_entry();
+ if (caller_frame.is_entry_frame() && VM_Version::supports_fast_class_init_checks()) {
+ // Bypass class initialization checks in c2i when caller is in native.
+ // JNI calls to static methods don't have class initialization checks.
+ // Fast class initialization checks are present in c2i adapters and call into
+ // SharedRuntime::handle_wrong_method() on the slow path.
+ //
+ // JVM upcalls may land here as well, but there's a proper check present in
+ // LinkResolver::resolve_static_call (called from JavaCalls::call_static),
+ // so bypassing it in c2i adapter is benign.
+ return callee->get_c2i_no_clinit_check_entry();
+ } else {
+ return callee->get_c2i_entry();
+ }
}
// Must be compiled to compiled path which is safe to stackwalk
@@ -2450,9 +2462,9 @@
: BasicHashtable<mtCode>(293, (DumpSharedSpaces ? sizeof(CDSAdapterHandlerEntry) : sizeof(AdapterHandlerEntry))) { }
// Create a new entry suitable for insertion in the table
- AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry) {
+ AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry, address c2i_no_clinit_check_entry) {
AdapterHandlerEntry* entry = (AdapterHandlerEntry*)BasicHashtable<mtCode>::new_entry(fingerprint->compute_hash());
- entry->init(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+ entry->init(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
if (DumpSharedSpaces) {
((CDSAdapterHandlerEntry*)entry)->init();
}
@@ -2601,8 +2613,9 @@
AdapterHandlerEntry* AdapterHandlerLibrary::new_entry(AdapterFingerPrint* fingerprint,
address i2c_entry,
address c2i_entry,
- address c2i_unverified_entry) {
- return _adapters->new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+ address c2i_unverified_entry,
+ address c2i_no_clinit_check_entry) {
+ return _adapters->new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
}
AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(const methodHandle& method) {
@@ -2778,6 +2791,7 @@
if (base == NULL) base = _c2i_entry;
assert(base <= _c2i_entry || _c2i_entry == NULL, "");
assert(base <= _c2i_unverified_entry || _c2i_unverified_entry == NULL, "");
+ assert(base <= _c2i_no_clinit_check_entry || _c2i_no_clinit_check_entry == NULL, "");
return base;
}
@@ -2791,6 +2805,8 @@
_c2i_entry += delta;
if (_c2i_unverified_entry != NULL)
_c2i_unverified_entry += delta;
+ if (_c2i_no_clinit_check_entry != NULL)
+ _c2i_no_clinit_check_entry += delta;
assert(base_address() == new_base, "");
}
@@ -3129,10 +3145,20 @@
}
void AdapterHandlerEntry::print_adapter_on(outputStream* st) const {
- st->print_cr("AHE@" INTPTR_FORMAT ": %s i2c: " INTPTR_FORMAT " c2i: " INTPTR_FORMAT " c2iUV: " INTPTR_FORMAT,
- p2i(this), fingerprint()->as_string(),
- p2i(get_i2c_entry()), p2i(get_c2i_entry()), p2i(get_c2i_unverified_entry()));
-
+ st->print("AHE@" INTPTR_FORMAT ": %s", p2i(this), fingerprint()->as_string());
+ if (get_i2c_entry() != NULL) {
+ st->print(" i2c: " INTPTR_FORMAT, p2i(get_i2c_entry()));
+ }
+ if (get_c2i_entry() != NULL) {
+ st->print(" c2i: " INTPTR_FORMAT, p2i(get_c2i_entry()));
+ }
+ if (get_c2i_unverified_entry() != NULL) {
+ st->print(" c2iUV: " INTPTR_FORMAT, p2i(get_c2i_unverified_entry()));
+ }
+ if (get_c2i_no_clinit_check_entry() != NULL) {
+ st->print(" c2iNCI: " INTPTR_FORMAT, p2i(get_c2i_no_clinit_check_entry()));
+ }
+ st->cr();
}
#if INCLUDE_CDS
--- a/src/hotspot/share/runtime/sharedRuntime.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/runtime/sharedRuntime.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -636,6 +636,7 @@
address _i2c_entry;
address _c2i_entry;
address _c2i_unverified_entry;
+ address _c2i_no_clinit_check_entry;
#ifdef ASSERT
// Captures code and signature used to generate this adapter when
@@ -644,11 +645,12 @@
int _saved_code_length;
#endif
- void init(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry) {
+ void init(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry, address c2i_no_clinit_check_entry) {
_fingerprint = fingerprint;
_i2c_entry = i2c_entry;
_c2i_entry = c2i_entry;
_c2i_unverified_entry = c2i_unverified_entry;
+ _c2i_no_clinit_check_entry = c2i_no_clinit_check_entry;
#ifdef ASSERT
_saved_code = NULL;
_saved_code_length = 0;
@@ -661,9 +663,11 @@
AdapterHandlerEntry();
public:
- address get_i2c_entry() const { return _i2c_entry; }
- address get_c2i_entry() const { return _c2i_entry; }
- address get_c2i_unverified_entry() const { return _c2i_unverified_entry; }
+ address get_i2c_entry() const { return _i2c_entry; }
+ address get_c2i_entry() const { return _c2i_entry; }
+ address get_c2i_unverified_entry() const { return _c2i_unverified_entry; }
+ address get_c2i_no_clinit_check_entry() const { return _c2i_no_clinit_check_entry; }
+
address base_address();
void relocate(address new_base);
@@ -709,7 +713,10 @@
public:
static AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint,
- address i2c_entry, address c2i_entry, address c2i_unverified_entry);
+ address i2c_entry,
+ address c2i_entry,
+ address c2i_unverified_entry,
+ address c2i_no_clinit_check_entry = NULL);
static void create_native_wrapper(const methodHandle& method);
static AdapterHandlerEntry* get_adapter(const methodHandle& method);
--- a/src/hotspot/share/runtime/thread.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/runtime/thread.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -250,7 +250,6 @@
// plain initialization
debug_only(_owned_locks = NULL;)
- debug_only(_allow_allocation_count = 0;)
NOT_PRODUCT(_allow_safepoint_count = 0;)
NOT_PRODUCT(_skip_gcalot = false;)
_jvmti_env_iteration_count = 0;
@@ -2969,11 +2968,6 @@
}
}
- // callee_target is never live across a gc point so NULL it here should
- // it still contain a methdOop.
-
- set_callee_target(NULL);
-
assert(vframe_array_head() == NULL, "deopt in progress at a safepoint!");
// If we have deferred set_locals there might be oops waiting to be
// written
--- a/src/hotspot/share/runtime/thread.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/runtime/thread.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -375,18 +375,14 @@
// GC points in the VM can happen because of allocation, invoking a VM operation, or blocking on
// mutex, or blocking on an object synchronizer (Java locking).
// If !allow_safepoint(), then an assertion failure will happen in any of the above cases
- // If !allow_allocation(), then an assertion failure will happen during allocation
- // (Hence, !allow_safepoint() => !allow_allocation()).
//
- // The two classes NoSafepointVerifier and No_Allocation_Verifier are used to set these counters.
+ // The class NoSafepointVerifier is used to set this counter.
//
NOT_PRODUCT(int _allow_safepoint_count;) // If 0, thread allow a safepoint to happen
- debug_only(int _allow_allocation_count;) // If 0, the thread is allowed to allocate oops.
// Used by SkipGCALot class.
NOT_PRODUCT(bool _skip_gcalot;) // Should we elide gc-a-lot?
- friend class NoAllocVerifier;
friend class NoSafepointVerifier;
friend class PauseNoSafepointVerifier;
friend class GCLocker;
@@ -754,7 +750,6 @@
bool owns_locks_but_compiled_lock() const;
// Deadlock detection
- bool allow_allocation() { return _allow_allocation_count == 0; }
ResourceMark* current_resource_mark() { return _current_resource_mark; }
void set_current_resource_mark(ResourceMark* rm) { _current_resource_mark = rm; }
#endif
--- a/src/hotspot/share/runtime/unhandledOops.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/runtime/unhandledOops.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -55,15 +55,15 @@
// For debugging unhandled oop detector _in the debugger_
// You don't want to turn it on in compiled code here.
-static bool unhandled_oop_print=0;
+static Thread* unhandled_oop_print = NULL;
void UnhandledOops::register_unhandled_oop(oop* op, address pc) {
if (!_thread->is_in_stack((address)op))
return;
- _level ++;
- if (unhandled_oop_print) {
- for (int i=0; i<_level; i++) tty->print(" ");
+ _level++;
+ if (unhandled_oop_print == _thread) {
+ for (int i=0; i < _level; i++) tty->print(" ");
tty->print_cr("r " INTPTR_FORMAT, p2i(op));
}
UnhandledOopEntry entry(op, pc);
@@ -98,11 +98,11 @@
void UnhandledOops::unregister_unhandled_oop(oop* op) {
if (!_thread->is_in_stack((address)op)) return;
- _level --;
- if (unhandled_oop_print) {
- for (int i=0; i<_level; i++) tty->print(" ");
+ if (unhandled_oop_print == _thread) {
+ for (int i=0; i < _level; i++) tty->print(" ");
tty->print_cr("u " INTPTR_FORMAT, p2i(op));
}
+ _level--;
int i = _oop_list->find_from_end(op, match_oop_entry);
assert(i!=-1, "oop not in unhandled_oop_list");
--- a/src/hotspot/share/services/attachListener.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/services/attachListener.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -45,7 +45,7 @@
#include "utilities/debug.hpp"
#include "utilities/formatBuffer.hpp"
-volatile bool AttachListener::_initialized;
+volatile AttachListenerState AttachListener::_state = AL_NOT_INITIALIZED;
// Implementation of "properties" command.
//
@@ -372,6 +372,7 @@
"Should already be setup");
if (AttachListener::pd_init() != 0) {
+ AttachListener::set_state(AL_NOT_INITIALIZED);
return;
}
AttachListener::set_initialized();
@@ -379,6 +380,7 @@
for (;;) {
AttachOperation* op = AttachListener::dequeue();
if (op == NULL) {
+ AttachListener::set_state(AL_NOT_INITIALIZED);
return; // dequeue failed or shutdown
}
@@ -422,6 +424,8 @@
// operation complete - send result and output to client
op->complete(res, &st);
}
+
+ ShouldNotReachHere();
}
bool AttachListener::has_init_error(TRAPS) {
@@ -445,6 +449,7 @@
const char thread_name[] = "Attach Listener";
Handle string = java_lang_String::create_from_str(thread_name, THREAD);
if (has_init_error(THREAD)) {
+ set_state(AL_NOT_INITIALIZED);
return;
}
@@ -456,6 +461,7 @@
string,
THREAD);
if (has_init_error(THREAD)) {
+ set_state(AL_NOT_INITIALIZED);
return;
}
@@ -469,6 +475,7 @@
thread_oop,
THREAD);
if (has_init_error(THREAD)) {
+ set_state(AL_NOT_INITIALIZED);
return;
}
--- a/src/hotspot/share/services/attachListener.hpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/services/attachListener.hpp Thu Jul 25 08:24:01 2019 -0400
@@ -26,6 +26,8 @@
#define SHARE_SERVICES_ATTACHLISTENER_HPP
#include "memory/allocation.hpp"
+#include "metaprogramming/isRegisteredEnum.hpp"
+#include "runtime/atomic.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"
@@ -49,6 +51,14 @@
AttachOperationFunction func;
};
+enum AttachListenerState {
+ AL_NOT_INITIALIZED,
+ AL_INITIALIZING,
+ AL_INITIALIZED
+};
+
+template<> struct IsRegisteredEnum<AttachListenerState> : public TrueType {};
+
class AttachListener: AllStatic {
public:
static void vm_start() NOT_SERVICES_RETURN;
@@ -58,6 +68,9 @@
// invoke to perform clean-up tasks when all clients detach
static void detachall() NOT_SERVICES_RETURN;
+ // check unix domain socket file on filesystem
+ static bool check_socket_file() NOT_SERVICES_RETURN_(false);
+
// indicates if the Attach Listener needs to be created at startup
static bool init_at_startup() NOT_SERVICES_RETURN_(false);
@@ -67,12 +80,31 @@
#if !INCLUDE_SERVICES
static bool is_attach_supported() { return false; }
#else
+
private:
- static volatile bool _initialized;
+ static volatile AttachListenerState _state;
public:
- static bool is_initialized() { return _initialized; }
- static void set_initialized() { _initialized = true; }
+ static void set_state(AttachListenerState new_state) {
+ Atomic::store(new_state, &_state);
+ }
+
+ static AttachListenerState get_state() {
+ return Atomic::load(&_state);
+ }
+
+ static AttachListenerState transit_state(AttachListenerState new_state,
+ AttachListenerState cmp_state) {
+ return Atomic::cmpxchg(new_state, &_state, cmp_state);
+ }
+
+ static bool is_initialized() {
+ return Atomic::load(&_state) == AL_INITIALIZED;
+ }
+
+ static void set_initialized() {
+ Atomic::store(AL_INITIALIZED, &_state);
+ }
// indicates if this VM supports attach-on-demand
static bool is_attach_supported() { return !DisableAttachMechanism; }
--- a/src/hotspot/share/services/gcNotifier.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/src/hotspot/share/services/gcNotifier.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -159,7 +159,7 @@
gcInfoklass,
vmSymbols::com_sun_management_GcInfo_constructor_signature(),
&constructor_args,
- CHECK_NH);
+ THREAD);
}
void GCNotifier::sendNotification(TRAPS) {
--- a/src/java.base/aix/classes/sun/nio/fs/AixNativeDispatcher.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/aix/classes/sun/nio/fs/AixNativeDispatcher.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -26,9 +26,6 @@
package sun.nio.fs;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-
/**
* AIX specific system calls.
*/
@@ -46,11 +43,7 @@
private static native void init();
static {
- AccessController.doPrivileged(new PrivilegedAction<Void>() {
- public Void run() {
- System.loadLibrary("nio");
- return null;
- }});
+ jdk.internal.loader.BootLoader.loadLibrary("nio");
init();
}
}
--- a/src/java.base/aix/native/libjli/java_md_aix.c Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/aix/native/libjli/java_md_aix.c Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018 SAP SE. All rights reserved.
+ * Copyright (c) 2016, 2019 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,6 +23,7 @@
* questions.
*/
#include <stdio.h>
+#include <string.h>
#include <sys/ldr.h>
#include "java_md_aix.h"
--- a/src/java.base/aix/native/libnio/fs/AixNativeDispatcher.c Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/aix/native/libnio/fs/AixNativeDispatcher.c Thu Jul 25 08:24:01 2019 -0400
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013 SAP SE. All rights reserved.
+ * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019 SAP SE. 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
@@ -25,6 +25,7 @@
*/
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mntctl.h>
--- a/src/java.base/linux/classes/sun/nio/fs/LinuxNativeDispatcher.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/linux/classes/sun/nio/fs/LinuxNativeDispatcher.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2019, 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
@@ -25,9 +25,6 @@
package sun.nio.fs;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-
/**
* Linux specific system calls.
*/
@@ -121,11 +118,7 @@
private static native void init();
static {
- AccessController.doPrivileged(new PrivilegedAction<>() {
- public Void run() {
- System.loadLibrary("nio");
- return null;
- }});
+ jdk.internal.loader.BootLoader.loadLibrary("nio");
init();
}
}
--- a/src/java.base/linux/classes/sun/nio/fs/LinuxWatchService.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/linux/classes/sun/nio/fs/LinuxWatchService.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2019, 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
@@ -26,8 +26,6 @@
package sun.nio.fs;
import java.nio.file.*;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.util.*;
import java.io.IOException;
import jdk.internal.misc.Unsafe;
@@ -459,10 +457,6 @@
private static native int poll(int fd1, int fd2) throws UnixException;
static {
- AccessController.doPrivileged(new PrivilegedAction<>() {
- public Void run() {
- System.loadLibrary("nio");
- return null;
- }});
+ jdk.internal.loader.BootLoader.loadLibrary("nio");
}
}
--- a/src/java.base/macosx/classes/apple/security/KeychainStore.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/macosx/classes/apple/security/KeychainStore.java Thu Jul 25 08:24:01 2019 -0400
@@ -105,13 +105,7 @@
private static final Debug debug = Debug.getInstance("keystore");
static {
- AccessController.doPrivileged(
- new PrivilegedAction<Void>() {
- public Void run() {
- System.loadLibrary("osxsecurity");
- return null;
- }
- });
+ jdk.internal.loader.BootLoader.loadLibrary("osxsecurity");
try {
PKCS8ShroudedKeyBag_OID = new ObjectIdentifier(keyBag);
pbeWithSHAAnd3KeyTripleDESCBC_OID = new ObjectIdentifier(pbeWithSHAAnd3KeyTripleDESCBC);
--- a/src/java.base/macosx/classes/sun/nio/fs/UTIFileTypeDetector.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/macosx/classes/sun/nio/fs/UTIFileTypeDetector.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -27,8 +27,6 @@
import java.io.IOException;
import java.nio.file.Path;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
/**
* File type detector that uses a file extension to look up its MIME type
@@ -55,12 +53,6 @@
}
static {
- AccessController.doPrivileged(new PrivilegedAction<>() {
- @Override
- public Void run() {
- System.loadLibrary("nio");
- return null;
- }
- });
+ jdk.internal.loader.BootLoader.loadLibrary("nio");
}
}
--- a/src/java.base/share/classes/com/sun/crypto/provider/ChaCha20Cipher.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/com/sun/crypto/provider/ChaCha20Cipher.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -1363,8 +1363,11 @@
// Calculate and compare the tag. Only do the decryption
// if and only if the tag matches.
authFinalizeData(ctPlusTag, 0, ctLen, tag, 0);
- if (Arrays.compare(ctPlusTag, ctLen, ctPlusTagLen,
- tag, 0, tag.length) != 0) {
+ long tagCompare = ((long)asLongView.get(ctPlusTag, ctLen) ^
+ (long)asLongView.get(tag, 0)) |
+ ((long)asLongView.get(ctPlusTag, ctLen + Long.BYTES) ^
+ (long)asLongView.get(tag, Long.BYTES));
+ if (tagCompare != 0) {
throw new AEADBadTagException("Tag mismatch");
}
chaCha20Transform(ctPlusTag, 0, ctLen, out, outOff);
--- a/src/java.base/share/classes/java/io/PrintStream.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/io/PrintStream.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2019, 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
@@ -45,10 +45,10 @@
* ({@code '\n'}) is written.
*
* <p> All characters printed by a {@code PrintStream} are converted into
- * bytes using the given encoding or charset, or platform's default character
- * encoding if not specified.
+ * bytes using the given encoding or charset, or the platform's default
+ * character encoding if not specified.
* The {@link PrintWriter} class should be used in situations that require
- * writing characters rather than bytes.
+ * writing characters rather than bytes.
*
* <p> This class always replaces malformed and unmappable character sequences with
* the charset's default replacement string.
@@ -121,7 +121,9 @@
}
/**
- * Creates a new print stream. This stream will not flush automatically.
+ * Creates a new print stream, without automatic line flushing, with the
+ * specified OutputStream. Characters written to the stream are converted
+ * to bytes using the platform's default character encoding.
*
* @param out The output stream to which values and objects will be
* printed
@@ -133,11 +135,13 @@
}
/**
- * Creates a new print stream.
+ * Creates a new print stream, with the specified OutputStream and line
+ * flushing. Characters written to the stream are converted to bytes using
+ * the platform's default character encoding.
*
* @param out The output stream to which values and objects will be
* printed
- * @param autoFlush A boolean; if true, the output buffer will be flushed
+ * @param autoFlush Whether the output buffer will be flushed
* whenever a byte array is written, one of the
* {@code println} methods is invoked, or a newline
* character or byte ({@code '\n'}) is written
@@ -149,11 +153,12 @@
}
/**
- * Creates a new print stream.
+ * Creates a new print stream, with the specified OutputStream, line
+ * flushing, and character encoding.
*
* @param out The output stream to which values and objects will be
* printed
- * @param autoFlush A boolean; if true, the output buffer will be flushed
+ * @param autoFlush Whether the output buffer will be flushed
* whenever a byte array is written, one of the
* {@code println} methods is invoked, or a newline
* character or byte ({@code '\n'}) is written
@@ -173,14 +178,14 @@
}
/**
- * Creates a new print stream, with the specified OutputStream, automatic line
+ * Creates a new print stream, with the specified OutputStream, line
* flushing and charset. This convenience constructor creates the necessary
* intermediate {@link java.io.OutputStreamWriter OutputStreamWriter},
* which will encode characters using the provided charset.
*
* @param out The output stream to which values and objects will be
* printed
- * @param autoFlush A boolean; if true, the output buffer will be flushed
+ * @param autoFlush Whether the output buffer will be flushed
* whenever a byte array is written, one of the
* {@code println} methods is invoked, or a newline
* character or byte ({@code '\n'}) is written
@@ -700,9 +705,9 @@
/**
* Prints a character. The character is translated into one or more bytes
- * according to the platform's default character encoding, and these bytes
- * are written in exactly the manner of the
- * {@link #write(int)} method.
+ * according to the character encoding given to the constructor, or the
+ * platform's default character encoding if none specified. These bytes
+ * are written in exactly the manner of the {@link #write(int)} method.
*
* @param c The {@code char} to be printed
*/
@@ -768,9 +773,9 @@
/**
* Prints an array of characters. The characters are converted into bytes
- * according to the platform's default character encoding, and these bytes
- * are written in exactly the manner of the
- * {@link #write(int)} method.
+ * according to the character encoding given to the constructor, or the
+ * platform's default character encoding if none specified. These bytes
+ * are written in exactly the manner of the {@link #write(int)} method.
*
* @param s The array of chars to be printed
*
@@ -783,8 +788,9 @@
/**
* Prints a string. If the argument is {@code null} then the string
* {@code "null"} is printed. Otherwise, the string's characters are
- * converted into bytes according to the platform's default character
- * encoding, and these bytes are written in exactly the manner of the
+ * converted into bytes according to the character encoding given to the
+ * constructor, or the platform's default character encoding if none
+ * specified. These bytes are written in exactly the manner of the
* {@link #write(int)} method.
*
* @param s The {@code String} to be printed
--- a/src/java.base/share/classes/java/io/PushbackInputStream.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/io/PushbackInputStream.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2019, 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
@@ -217,6 +217,7 @@
* @param b the byte array to push back.
* @param off the start offset of the data.
* @param len the number of bytes to push back.
+ * @exception NullPointerException If <code>b</code> is <code>null</code>.
* @exception IOException If there is not enough room in the pushback
* buffer for the specified number of bytes,
* or this input stream has been closed by
@@ -239,6 +240,7 @@
* value <code>b[1]</code>, and so forth.
*
* @param b the byte array to push back
+ * @exception NullPointerException If <code>b</code> is <code>null</code>.
* @exception IOException If there is not enough room in the pushback
* buffer for the specified number of bytes,
* or this input stream has been closed by
--- a/src/java.base/share/classes/java/lang/System.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/lang/System.java Thu Jul 25 08:24:01 2019 -0400
@@ -2274,6 +2274,11 @@
public void setCause(Throwable t, Throwable cause) {
t.setCause(cause);
}
+
+ public void loadLibrary(Class<?> caller, String library) {
+ assert library.indexOf(java.io.File.separatorChar) < 0;
+ ClassLoader.loadLibrary(caller, library, false);
+ }
});
}
}
--- a/src/java.base/share/classes/java/lang/Throwable.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/lang/Throwable.java Thu Jul 25 08:24:01 2019 -0400
@@ -25,8 +25,8 @@
package java.lang;
-import java.io.*;
-import java.util.*;
+import java.io.*;
+import java.util.*;
/**
* The {@code Throwable} class is the superclass of all errors and
@@ -904,24 +904,36 @@
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject(); // read in all fields
- if (suppressedExceptions != null) {
- List<Throwable> suppressed = null;
- if (suppressedExceptions.isEmpty()) {
- // Use the sentinel for a zero-length list
- suppressed = SUPPRESSED_SENTINEL;
- } else { // Copy Throwables to new list
- suppressed = new ArrayList<>(1);
- for (Throwable t : suppressedExceptions) {
+
+ // Set suppressed exceptions and stack trace elements fields
+ // to marker values until the contents from the serial stream
+ // are validated.
+ List<Throwable> candidateSuppressedExceptions = suppressedExceptions;
+ suppressedExceptions = SUPPRESSED_SENTINEL;
+
+ StackTraceElement[] candidateStackTrace = stackTrace;
+ stackTrace = UNASSIGNED_STACK.clone();
+
+ if (candidateSuppressedExceptions != null) {
+ int suppressedSize = validateSuppressedExceptionsList(candidateSuppressedExceptions);
+ if (suppressedSize > 0) { // Copy valid Throwables to new list
+ var suppList = new ArrayList<Throwable>(Math.min(100, suppressedSize));
+
+ for (Throwable t : candidateSuppressedExceptions) {
// Enforce constraints on suppressed exceptions in
// case of corrupt or malicious stream.
Objects.requireNonNull(t, NULL_CAUSE_MESSAGE);
if (t == this)
throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE);
- suppressed.add(t);
+ suppList.add(t);
}
+ // If there are any invalid suppressed exceptions,
+ // implicitly use the sentinel value assigned earlier.
+ suppressedExceptions = suppList;
}
- suppressedExceptions = suppressed;
- } // else a null suppressedExceptions field remains null
+ } else {
+ suppressedExceptions = null;
+ }
/*
* For zero-length stack traces, use a clone of
@@ -932,24 +944,41 @@
* the stackTrace needs to be constructed from the information
* in backtrace.
*/
- if (stackTrace != null) {
- if (stackTrace.length == 0) {
- stackTrace = UNASSIGNED_STACK.clone();
- } else if (stackTrace.length == 1 &&
+ if (candidateStackTrace != null) {
+ // Work from a clone of the candidateStackTrace to ensure
+ // consistency of checks.
+ candidateStackTrace = candidateStackTrace.clone();
+ if (candidateStackTrace.length >= 1) {
+ if (candidateStackTrace.length == 1 &&
// Check for the marker of an immutable stack trace
- SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL.equals(stackTrace[0])) {
- stackTrace = null;
- } else { // Verify stack trace elements are non-null.
- for(StackTraceElement ste : stackTrace) {
- Objects.requireNonNull(ste, "null StackTraceElement in serial stream.");
+ SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL.equals(candidateStackTrace[0])) {
+ stackTrace = null;
+ } else { // Verify stack trace elements are non-null.
+ for (StackTraceElement ste : candidateStackTrace) {
+ Objects.requireNonNull(ste, "null StackTraceElement in serial stream.");
+ }
+ stackTrace = candidateStackTrace;
}
}
+ }
+ // A null stackTrace field in the serial form can result from
+ // an exception serialized without that field in older JDK
+ // releases; treat such exceptions as having empty stack
+ // traces by leaving stackTrace assigned to a clone of
+ // UNASSIGNED_STACK.
+ }
+
+ private int validateSuppressedExceptionsList(List<Throwable> deserSuppressedExceptions)
+ throws IOException {
+ if (!Object.class.getModule().
+ equals(deserSuppressedExceptions.getClass().getModule())) {
+ throw new StreamCorruptedException("List implementation not in base module.");
} else {
- // A null stackTrace field in the serial form can result
- // from an exception serialized without that field in
- // older JDK releases; treat such exceptions as having
- // empty stack traces.
- stackTrace = UNASSIGNED_STACK.clone();
+ int size = deserSuppressedExceptions.size();
+ if (size < 0) {
+ throw new StreamCorruptedException("Negative list size reported.");
+ }
+ return size;
}
}
--- a/src/java.base/share/classes/java/lang/invoke/MemberName.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/lang/invoke/MemberName.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2019, 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
@@ -521,7 +521,7 @@
public boolean isAccessibleFrom(Class<?> lookupClass) {
int mode = (ALL_ACCESS|MethodHandles.Lookup.PACKAGE|MethodHandles.Lookup.MODULE);
return VerifyAccess.isMemberAccessible(this.getDeclaringClass(), this.getDeclaringClass(), flags,
- lookupClass, mode);
+ lookupClass, null, mode);
}
/**
@@ -930,13 +930,21 @@
message += ", from public Lookup";
} else {
Module m;
+ Class<?> plc;
if (from instanceof MethodHandles.Lookup) {
MethodHandles.Lookup lookup = (MethodHandles.Lookup)from;
+ from = lookup.lookupClass();
m = lookup.lookupClass().getModule();
+ plc = lookup.previousLookupClass();
} else {
- m = from.getClass().getModule();
+ m = ((Class<?>)from).getModule();
+ plc = null;
}
message += ", from " + from + " (" + m + ")";
+ if (plc != null) {
+ message += ", previous lookup " +
+ plc.getName() + " (" + plc.getModule() + ")";
+ }
}
}
return new IllegalAccessException(message);
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Thu Jul 25 08:24:01 2019 -0400
@@ -125,7 +125,7 @@
/**
* Returns a {@link Lookup lookup object} which is trusted minimally.
- * The lookup has the {@code PUBLIC} and {@code UNCONDITIONAL} modes.
+ * The lookup has the {@code UNCONDITIONAL} mode.
* It can only be used to create method handles to public members of
* public classes in packages that are exported unconditionally.
* <p>
@@ -134,14 +134,14 @@
*
* @apiNote The use of Object is conventional, and because the lookup modes are
* limited, there is no special access provided to the internals of Object, its package
- * or its module. Consequently, the lookup context of this lookup object will be the
- * bootstrap class loader, which means it cannot find user classes.
+ * or its module. This public lookup object or other lookup object with
+ * {@code UNCONDITIONAL} mode assumes readability. Consequently, the lookup class
+ * is not used to determine the lookup context.
*
* <p style="font-size:smaller;">
* <em>Discussion:</em>
* The lookup class can be changed to any other class {@code C} using an expression of the form
* {@link Lookup#in publicLookup().in(C.class)}.
- * but may change the lookup context by virtue of changing the class loader.
* A public lookup object is always subject to
* <a href="MethodHandles.Lookup.html#secmgr">security manager checks</a>.
* Also, it cannot access
@@ -156,64 +156,106 @@
}
/**
- * Returns a {@link Lookup lookup object} with full capabilities to emulate all
- * supported bytecode behaviors, including <a href="MethodHandles.Lookup.html#privacc">
- * private access</a>, on a target class.
- * This method checks that a caller, specified as a {@code Lookup} object, is allowed to
- * do <em>deep reflection</em> on the target class. If {@code m1} is the module containing
- * the {@link Lookup#lookupClass() lookup class}, and {@code m2} is the module containing
- * the target class, then this check ensures that
+ * Returns a {@link Lookup lookup} object on a target class to emulate all supported
+ * bytecode behaviors, including <a href="MethodHandles.Lookup.html#privacc"> private access</a>.
+ * The returned lookup object can provide access to classes in modules and packages,
+ * and members of those classes, outside the normal rules of Java access control,
+ * instead conforming to the more permissive rules for modular <em>deep reflection</em>.
+ * <p>
+ * A caller, specified as a {@code Lookup} object, in module {@code M1} is
+ * allowed to do deep reflection on module {@code M2} and package of the target class
+ * if and only if all of the following conditions are {@code true}:
* <ul>
- * <li>{@code m1} {@link Module#canRead reads} {@code m2}.</li>
- * <li>{@code m2} {@link Module#isOpen(String,Module) opens} the package containing
- * the target class to at least {@code m1}.</li>
- * <li>The lookup has the {@link Lookup#MODULE MODULE} lookup mode.</li>
+ * <li>If there is a security manager, its {@code checkPermission} method is
+ * called to check {@code ReflectPermission("suppressAccessChecks")} and
+ * that must return normally.
+ * <li>The caller lookup object must have the {@link Lookup#MODULE MODULE} lookup mode.
+ * (This is because otherwise there would be no way to ensure the original lookup
+ * creator was a member of any particular module, and so any subsequent checks
+ * for readability and qualified exports would become ineffective.)
+ * <li>The caller lookup object must have {@link Lookup#PRIVATE PRIVATE} access.
+ * (This is because an application intending to share intra-module access
+ * using {@link Lookup#MODULE MODULE} alone will inadvertently also share
+ * deep reflection to its own module.)
+ * <li>The target class must be a proper class, not a primitive or array class.
+ * (Thus, {@code M2} is well-defined.)
+ * <li>If the caller module {@code M1} differs from
+ * the target module {@code M2} then both of the following must be true:
+ * <ul>
+ * <li>{@code M1} {@link Module#canRead reads} {@code M2}.</li>
+ * <li>{@code M2} {@link Module#isOpen(String,Module) opens} the package
+ * containing the target class to at least {@code M1}.</li>
+ * </ul>
* </ul>
* <p>
- * If there is a security manager, its {@code checkPermission} method is called to
- * check {@code ReflectPermission("suppressAccessChecks")}.
- * @apiNote The {@code MODULE} lookup mode serves to authenticate that the lookup object
- * was created by code in the caller module (or derived from a lookup object originally
- * created by the caller). A lookup object with the {@code MODULE} lookup mode can be
- * shared with trusted parties without giving away {@code PRIVATE} and {@code PACKAGE}
- * access to the caller.
+ * If any of the above checks is violated, this method fails with an
+ * exception.
+ * <p>
+ * Otherwise, if {@code M1} and {@code M2} are the same module, this method
+ * returns a {@code Lookup} on {@code targetClass} with full capabilities and
+ * {@code null} previous lookup class.
+ * <p>
+ * Otherwise, {@code M1} and {@code M2} are two different modules. This method
+ * returns a {@code Lookup} on {@code targetClass} that records
+ * the lookup class of the caller as the new previous lookup class and
+ * drops {@code MODULE} access from the full capabilities mode.
+ *
* @param targetClass the target class
- * @param lookup the caller lookup object
+ * @param caller the caller lookup object
* @return a lookup object for the target class, with private access
- * @throws IllegalArgumentException if {@code targetClass} is a primitve type or array class
+ * @throws IllegalArgumentException if {@code targetClass} is a primitive type or array class
* @throws NullPointerException if {@code targetClass} or {@code caller} is {@code null}
- * @throws IllegalAccessException if the access check specified above fails
* @throws SecurityException if denied by the security manager
+ * @throws IllegalAccessException if any of the other access checks specified above fails
* @since 9
* @spec JPMS
* @see Lookup#dropLookupMode
+ * @see <a href="MethodHandles.Lookup.html#cross-module-lookup">Cross-module lookups</a>
*/
- public static Lookup privateLookupIn(Class<?> targetClass, Lookup lookup) throws IllegalAccessException {
+ public static Lookup privateLookupIn(Class<?> targetClass, Lookup caller) throws IllegalAccessException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
if (targetClass.isPrimitive())
throw new IllegalArgumentException(targetClass + " is a primitive class");
if (targetClass.isArray())
throw new IllegalArgumentException(targetClass + " is an array class");
- Module targetModule = targetClass.getModule();
- Module callerModule = lookup.lookupClass().getModule();
- if (!callerModule.canRead(targetModule))
- throw new IllegalAccessException(callerModule + " does not read " + targetModule);
- if (targetModule.isNamed()) {
- String pn = targetClass.getPackageName();
- assert !pn.isEmpty() : "unnamed package cannot be in named module";
- if (!targetModule.isOpen(pn, callerModule))
- throw new IllegalAccessException(targetModule + " does not open " + pn + " to " + callerModule);
+ // Ensure that we can reason accurately about private and module access.
+ if ((caller.lookupModes() & Lookup.PRIVATE) == 0)
+ throw new IllegalAccessException("caller does not have PRIVATE lookup mode");
+ if ((caller.lookupModes() & Lookup.MODULE) == 0)
+ throw new IllegalAccessException("caller does not have MODULE lookup mode");
+
+ // previous lookup class is never set if it has MODULE access
+ assert caller.previousLookupClass() == null;
+
+ Class<?> callerClass = caller.lookupClass();
+ Module callerModule = callerClass.getModule(); // M1
+ Module targetModule = targetClass.getModule(); // M2
+ Class<?> newPreviousClass = null;
+ int newModes = Lookup.FULL_POWER_MODES;
+
+ if (targetModule != callerModule) {
+ if (!callerModule.canRead(targetModule))
+ throw new IllegalAccessException(callerModule + " does not read " + targetModule);
+ if (targetModule.isNamed()) {
+ String pn = targetClass.getPackageName();
+ assert !pn.isEmpty() : "unnamed package cannot be in named module";
+ if (!targetModule.isOpen(pn, callerModule))
+ throw new IllegalAccessException(targetModule + " does not open " + pn + " to " + callerModule);
+ }
+
+ // M2 != M1, set previous lookup class to M1 and drop MODULE access
+ newPreviousClass = callerClass;
+ newModes &= ~Lookup.MODULE;
}
- if ((lookup.lookupModes() & Lookup.MODULE) == 0)
- throw new IllegalAccessException("lookup does not have MODULE lookup mode");
+
if (!callerModule.isNamed() && targetModule.isNamed()) {
IllegalAccessLogger logger = IllegalAccessLogger.illegalAccessLogger();
if (logger != null) {
- logger.logIfOpenedForIllegalAccess(lookup, targetClass);
+ logger.logIfOpenedForIllegalAccess(caller, targetClass);
}
}
- return new Lookup(targetClass);
+ return Lookup.newLookup(targetClass, newPreviousClass, newModes);
}
/**
@@ -533,6 +575,514 @@
* whose <a href="MethodHandles.Lookup.html#equiv">bytecode behaviors</a> and Java language access permissions
* can be reliably determined and emulated by method handles.
*
+ * <h2><a id="cross-module-lookup"></a>Cross-module lookups</h2>
+ * When a lookup class in one module {@code M1} accesses a class in another module
+ * {@code M2}, extra access checking is performed beyond the access mode bits.
+ * A {@code Lookup} with {@link #PUBLIC} mode and a lookup class in {@code M1}
+ * can access public types in {@code M2} when {@code M2} is readable to {@code M1}
+ * and when the type is in a package of {@code M2} that is exported to
+ * at least {@code M1}.
+ * <p>
+ * A {@code Lookup} on {@code C} can also <em>teleport</em> to a target class
+ * via {@link #in(Class) Lookup.in} and {@link MethodHandles#privateLookupIn(Class, Lookup)
+ * MethodHandles.privateLookupIn} methods.
+ * Teleporting across modules will always record the original lookup class as
+ * the <em>{@linkplain #previousLookupClass() previous lookup class}</em>
+ * and drops {@link Lookup#MODULE MODULE} access.
+ * If the target class is in the same module as the lookup class {@code C},
+ * then the target class becomes the new lookup class
+ * and there is no change to the previous lookup class.
+ * If the target class is in a different module from {@code M1} ({@code C}'s module),
+ * {@code C} becomes the new previous lookup class
+ * and the target class becomes the new lookup class.
+ * In that case, if there was already a previous lookup class in {@code M0},
+ * and it differs from {@code M1} and {@code M2}, then the resulting lookup
+ * drops all privileges.
+ * For example,
+ * <blockquote><pre>
+ * {@code
+ * Lookup lookup = MethodHandles.lookup(); // in class C
+ * Lookup lookup2 = lookup.in(D.class);
+ * MethodHandle mh = lookup2.findStatic(E.class, "m", MT);
+ * }</pre></blockquote>
+ * <p>
+ * The {@link #lookup()} factory method produces a {@code Lookup} object
+ * with {@code null} previous lookup class.
+ * {@link Lookup#in lookup.in(D.class)} transforms the {@code lookup} on class {@code C}
+ * to class {@code D} without elevation of privileges.
+ * If {@code C} and {@code D} are in the same module,
+ * {@code lookup2} records {@code D} as the new lookup class and keeps the
+ * same previous lookup class as the original {@code lookup}, or
+ * {@code null} if not present.
+ * <p>
+ * When a {@code Lookup} teleports from a class
+ * in one nest to another nest, {@code PRIVATE} access is dropped.
+ * When a {@code Lookup} teleports from a class in one package to
+ * another package, {@code PACKAGE} access is dropped.
+ * When a {@code Lookup} teleports from a class in one module to another module,
+ * {@code MODULE} access is dropped.
+ * Teleporting across modules drops the ability to access non-exported classes
+ * in both the module of the new lookup class and the module of the old lookup class
+ * and the resulting {@code Lookup} remains only {@code PUBLIC} access.
+ * A {@code Lookup} can teleport back and forth to a class in the module of
+ * the lookup class and the module of the previous class lookup.
+ * Teleporting across modules can only decrease access but cannot increase it.
+ * Teleporting to some third module drops all accesses.
+ * <p>
+ * In the above example, if {@code C} and {@code D} are in different modules,
+ * {@code lookup2} records {@code D} as its lookup class and
+ * {@code C} as its previous lookup class and {@code lookup2} has only
+ * {@code PUBLIC} access. {@code lookup2} can teleport to other class in
+ * {@code C}'s module and {@code D}'s module.
+ * If class {@code E} is in a third module, {@code lookup2.in(E.class)} creates
+ * a {@code Lookup} on {@code E} with no access and {@code lookup2}'s lookup
+ * class {@code D} is recorded as its previous lookup class.
+ * <p>
+ * Teleporting across modules restricts access to the public types that
+ * both the lookup class and the previous lookup class can equally access
+ * (see below).
+ * <p>
+ * {@link MethodHandles#privateLookupIn(Class, Lookup) MethodHandles.privateLookupIn(T.class, lookup)}
+ * can be used to teleport a {@code lookup} from class {@code C} to class {@code T}
+ * and create a new {@code Lookup} with <a href="#privcc">private access</a>
+ * if the lookup class is allowed to do <em>deep reflection</em> on {@code T}.
+ * The {@code lookup} must have {@link #MODULE} and {@link #PRIVATE} access
+ * to call {@code privateLookupIn}.
+ * A {@code lookup} on {@code C} in module {@code M1} is allowed to do deep reflection
+ * on all classes in {@code M1}. If {@code T} is in {@code M1}, {@code privateLookupIn}
+ * produces a new {@code Lookup} on {@code T} with full capabilities.
+ * A {@code lookup} on {@code C} is also allowed
+ * to do deep reflection on {@code T} in another module {@code M2} if
+ * {@code M1} reads {@code M2} and {@code M2} {@link Module#isOpen(String,Module) opens}
+ * the package containing {@code T} to at least {@code M1}.
+ * {@code T} becomes the new lookup class and {@code C} becomes the new previous
+ * lookup class and {@code MODULE} access is dropped from the resulting {@code Lookup}.
+ * The resulting {@code Lookup} can be used to do member lookup or teleport
+ * to another lookup class by calling {@link #in Lookup::in}. But
+ * it cannot be used to obtain another private {@code Lookup} by calling
+ * {@link MethodHandles#privateLookupIn(Class, Lookup) privateLookupIn}
+ * because it has no {@code MODULE} access.
+ *
+ * <h2><a id="module-access-check"></a>Cross-module access checks</h2>
+ *
+ * A {@code Lookup} with {@link #PUBLIC} or with {@link #UNCONDITIONAL} mode
+ * allows cross-module access. The access checking is performed with respect
+ * to both the lookup class and the previous lookup class if present.
+ * <p>
+ * A {@code Lookup} with {@link #UNCONDITIONAL} mode can access public type
+ * in all modules when the type is in a package that is {@linkplain Module#isExported(String)
+ * exported unconditionally}.
+ * <p>
+ * If a {@code Lookup} on {@code LC} in {@code M1} has no previous lookup class,
+ * the lookup with {@link #PUBLIC} mode can access all public types in modules
+ * that are readable to {@code M1} and the type is in a package that is exported
+ * at least to {@code M1}.
+ * <p>
+ * If a {@code Lookup} on {@code LC} in {@code M1} has a previous lookup class
+ * {@code PLC} on {@code M0}, the lookup with {@link #PUBLIC} mode can access
+ * the intersection of all public types that are accessible to {@code M1}
+ * with all public types that are accessible to {@code M0}. {@code M0}
+ * reads {@code M1} and hence the set of accessible types includes:
+ *
+ * <table class="striped">
+ * <caption style="display:none">
+ * Public types in the following packages are accessible to the
+ * lookup class and the previous lookup class.
+ * </caption>
+ * <thead>
+ * <tr>
+ * <th scope="col">Equally accessible types to {@code M0} and {@code M1}</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <th scope="row" style="text-align:left">unconditional-exported packages from {@code M1}</th>
+ * </tr>
+ * <tr>
+ * <th scope="row" style="text-align:left">unconditional-exported packages from {@code M0} if {@code M1} reads {@code M0}</th>
+ * </tr>
+ * <tr>
+ * <th scope="row" style="text-align:left">unconditional-exported packages from a third module {@code M2}
+ * if both {@code M0} and {@code M1} read {@code M2}</th>
+ * </tr>
+ * <tr>
+ * <th scope="row" style="text-align:left">qualified-exported packages from {@code M1} to {@code M0}</th>
+ * </tr>
+ * <tr>
+ * <th scope="row" style="text-align:left">qualified-exported packages from {@code M0} to {@code M1}
+ * if {@code M1} reads {@code M0}</th>
+ * </tr>
+ * <tr>
+ * <th scope="row" style="text-align:left">qualified-exported packages from a third module {@code M2} to
+ * both {@code M0} and {@code M1} if both {@code M0} and {@code M1} read {@code M2}</th>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * <h2><a id="access-modes"></a>Access modes</h2>
+ *
+ * The table below shows the access modes of a {@code Lookup} produced by
+ * any of the following factory or transformation methods:
+ * <ul>
+ * <li>{@link #lookup() MethodHandles.lookup()}</li>
+ * <li>{@link #publicLookup() MethodHandles.publicLookup()}</li>
+ * <li>{@link #privateLookupIn(Class, Lookup) MethodHandles.privateLookupIn}</li>
+ * <li>{@link Lookup#in}</li>
+ * <li>{@link Lookup#dropLookupMode(int)}</li>
+ * </ul>
+ *
+ * <table class="striped">
+ * <caption style="display:none">
+ * Access mode summary
+ * </caption>
+ * <thead>
+ * <tr>
+ * <th scope="col">Lookup object</th>
+ * <th style="text-align:center">protected</th>
+ * <th style="text-align:center">private</th>
+ * <th style="text-align:center">package</th>
+ * <th style="text-align:center">module</th>
+ * <th style="text-align:center">public</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <th scope="row" style="text-align:left">{@code CL = MethodHandles.lookup()} in {@code C}</th>
+ * <td style="text-align:center">PRO</td>
+ * <td style="text-align:center">PRI</td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <th scope="row" style="text-align:left">{@code CL.in(C1)} same package</th>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <th scope="row" style="text-align:left">{@code CL.in(C1)} same module</th>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <th scope="row" style="text-align:left">{@code CL.in(D)} different module</th>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code CL.in(D).in(C)} hop back to module</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1 = privateLookupIn(C1,CL)}</td>
+ * <td style="text-align:center">PRO</td>
+ * <td style="text-align:center">PRI</td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1a = privateLookupIn(C,PRI1)}</td>
+ * <td style="text-align:center">PRO</td>
+ * <td style="text-align:center">PRI</td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1.in(C1)} same package</td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1.in(C1)} different package</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1.in(D)} different module</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1.dropLookupMode(PROTECTED)}</td>
+ * <td></td>
+ * <td style="text-align:center">PRI</td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1.dropLookupMode(PRIVATE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1.dropLookupMode(PACKAGE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1.dropLookupMode(MODULE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1.dropLookupMode(PUBLIC)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">none</td>
+ * <tr>
+ * <td>{@code PRI2 = privateLookupIn(D,CL)}</td>
+ * <td style="text-align:center">PRO</td>
+ * <td style="text-align:center">PRI</td>
+ * <td style="text-align:center">PAC</td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code privateLookupIn(D,PRI1)}</td>
+ * <td style="text-align:center">PRO</td>
+ * <td style="text-align:center">PRI</td>
+ * <td style="text-align:center">PAC</td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code privateLookupIn(C,PRI2)} fails</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">IAE</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.in(D2)} same package</td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">PAC</td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.in(D2)} different package</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.in(C1)} hop back to module</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.in(E)} hop to third module</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">none</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.dropLookupMode(PROTECTED)}</td>
+ * <td></td>
+ * <td style="text-align:center">PRI</td>
+ * <td style="text-align:center">PAC</td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.dropLookupMode(PRIVATE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">PAC</td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.dropLookupMode(PACKAGE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.dropLookupMode(MODULE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.dropLookupMode(PUBLIC)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">none</td>
+ * </tr>
+ * <tr>
+ * <td>{@code CL.dropLookupMode(PROTECTED)}</td>
+ * <td></td>
+ * <td style="text-align:center">PRI</td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code CL.dropLookupMode(PRIVATE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code CL.dropLookupMode(PACKAGE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code CL.dropLookupMode(MODULE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code CL.dropLookupMode(PUBLIC)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">none</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PUB = publicLookup()}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">U</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PUB.in(D)} different module</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">U</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PUB.in(D).in(E)} third module</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">U</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PUB.dropLookupMode(UNCONDITIONAL)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">none</td>
+ * </tr>
+ * <tr>
+ * <td>{@code privateLookupIn(C1,PUB)} fails</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">IAE</td>
+ * </tr>
+ * <tr>
+ * <td>{@code ANY.in(X)}, for inaccessible <code>X</code></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">none</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * <p>
+ * Notes:
+ * <ul>
+ * <li>Class {@code C} and class {@code C1} are in module {@code M1},
+ * but {@code D} and {@code D2} are in module {@code M2}, and {@code E}
+ * is in module {@code M3}. {@code X} stands for class which is inaccessible
+ * to the lookup. {@code ANY} stands for any of the example lookups.</li>
+ * <li>{@code PRO} indicates {@link #PROTECTED} bit set,
+ * {@code PRI} indicates {@link #PRIVATE} bit set,
+ * {@code PAC} indicates {@link #PACKAGE} bit set,
+ * {@code MOD} indicates {@link #MODULE} bit set,
+ * {@code 1R} and {@code 2R} indicate {@link #PUBLIC} bit set,
+ * {@code U} indicates {@link #UNCONDITIONAL} bit set,
+ * {@code IAE} indicates {@code IllegalAccessException} thrown.</li>
+ * <li>Public access comes in three kinds:
+ * <ul>
+ * <li>unconditional ({@code U}): the lookup assumes readability.
+ * The lookup has {@code null} previous lookup class.
+ * <li>one-module-reads ({@code 1R}): the module access checking is
+ * performed with respect to the lookup class. The lookup has {@code null}
+ * previous lookup class.
+ * <li>two-module-reads ({@code 2R}): the module access checking is
+ * performed with respect to the lookup class and the previous lookup class.
+ * The lookup has a non-null previous lookup class which is in a
+ * different module from the current lookup class.
+ * </ul>
+ * <li>Any attempt to reach a third module loses all access.</li>
+ * <li>If a target class {@code X} is not accessible to {@code Lookup::in}
+ * all access modes are dropped.</li>
+ * </ul>
+ *
* <h2><a id="secmgr"></a>Security manager interactions</h2>
* Although bytecode instructions can only refer to classes in
* a related class loader, this API can search for methods in any
@@ -645,6 +1195,9 @@
/** The class on behalf of whom the lookup is being performed. */
private final Class<?> lookupClass;
+ /** previous lookup class */
+ private final Class<?> prevLookupClass;
+
/** The allowed sorts of members which may be looked up (PUBLIC, etc.). */
private final int allowedModes;
@@ -656,6 +1209,10 @@
* which may contribute to the result of {@link #lookupModes lookupModes}.
* The value, {@code 0x01}, happens to be the same as the value of the
* {@code public} {@linkplain java.lang.reflect.Modifier#PUBLIC modifier bit}.
+ * <p>
+ * A {@code Lookup} with this lookup mode performs cross-module access check
+ * with respect to the {@linkplain #lookupClass() lookup class} and
+ * {@linkplain #previousLookupClass() previous lookup class} if present.
*/
public static final int PUBLIC = Modifier.PUBLIC;
@@ -680,7 +1237,7 @@
*/
public static final int PACKAGE = Modifier.STATIC;
- /** A single-bit mask representing {@code module} access (default access),
+ /** A single-bit mask representing {@code module} access,
* which may contribute to the result of {@link #lookupModes lookupModes}.
* The value is {@code 0x10}, which does not correspond meaningfully to
* any particular {@linkplain java.lang.reflect.Modifier modifier bit}.
@@ -688,6 +1245,10 @@
* with this lookup mode can access all public types in the module of the
* lookup class and public types in packages exported by other modules
* to the module of the lookup class.
+ * <p>
+ * If this lookup mode is set, the {@linkplain #previousLookupClass()
+ * previous lookup class} is always {@code null}.
+ *
* @since 9
* @spec JPMS
*/
@@ -699,10 +1260,14 @@
* any particular {@linkplain java.lang.reflect.Modifier modifier bit}.
* A {@code Lookup} with this lookup mode assumes {@linkplain
* java.lang.Module#canRead(java.lang.Module) readability}.
- * In conjunction with the {@code PUBLIC} modifier bit, a {@code Lookup}
- * with this lookup mode can access all public members of public types
- * of all modules where the type is in a package that is {@link
+ * This lookup mode can access all public members of public types
+ * of all modules when the type is in a package that is {@link
* java.lang.Module#isExported(String) exported unconditionally}.
+ *
+ * <p>
+ * If this lookup mode is set, the {@linkplain #previousLookupClass()
+ * previous lookup class} is always {@code null}.
+ *
* @since 9
* @spec JPMS
* @see #publicLookup()
@@ -713,24 +1278,55 @@
private static final int FULL_POWER_MODES = (ALL_MODES & ~UNCONDITIONAL);
private static final int TRUSTED = -1;
+ /*
+ * Adjust PUBLIC => PUBLIC|MODULE|UNCONDITIONAL
+ * Adjust 0 => PACKAGE
+ */
private static int fixmods(int mods) {
mods &= (ALL_MODES - PACKAGE - MODULE - UNCONDITIONAL);
- return (mods != 0) ? mods : (PACKAGE | MODULE | UNCONDITIONAL);
+ if (Modifier.isPublic(mods))
+ mods |= UNCONDITIONAL;
+ return (mods != 0) ? mods : PACKAGE;
}
/** Tells which class is performing the lookup. It is this class against
* which checks are performed for visibility and access permissions.
* <p>
+ * If this lookup object has a {@linkplain #previousLookupClass() previous lookup class},
+ * access checks are performed against both the lookup class and the previous lookup class.
+ * <p>
* The class implies a maximum level of access permission,
* but the permissions may be additionally limited by the bitmask
* {@link #lookupModes lookupModes}, which controls whether non-public members
* can be accessed.
* @return the lookup class, on behalf of which this lookup object finds members
+ * @see <a href="#cross-module-lookup">Cross-module lookups</a>
*/
public Class<?> lookupClass() {
return lookupClass;
}
+ /** Reports a lookup class in another module that this lookup object
+ * was previously teleported from, or {@code null}.
+ * <p>
+ * A {@code Lookup} object produced by the factory methods, such as the
+ * {@link #lookup() lookup()} and {@link #publicLookup() publicLookup()} method,
+ * has {@code null} previous lookup class.
+ * A {@code Lookup} object has a non-null previous lookup class
+ * when this lookup was teleported from an old lookup class
+ * in one module to a new lookup class in another module.
+ *
+ * @return the lookup class in another module that this lookup object was
+ * previously teleported from, or {@code null}
+ * @since 14
+ * @see #in(Class)
+ * @see MethodHandles#privateLookupIn(Class, Lookup)
+ * @see <a href="#cross-module-lookup">Cross-module lookups</a>
+ */
+ public Class<?> previousLookupClass() {
+ return prevLookupClass;
+ }
+
// This is just for calling out to MethodHandleImpl.
private Class<?> lookupClassOrNull() {
return (allowedModes == TRUSTED) ? null : lookupClass;
@@ -774,48 +1370,69 @@
* which in turn is called by a method not in this package.
*/
Lookup(Class<?> lookupClass) {
- this(lookupClass, FULL_POWER_MODES);
+ this(lookupClass, null, FULL_POWER_MODES);
// make sure we haven't accidentally picked up a privileged class:
checkUnprivilegedlookupClass(lookupClass);
}
- private Lookup(Class<?> lookupClass, int allowedModes) {
+ private Lookup(Class<?> lookupClass, Class<?> prevLookupClass, int allowedModes) {
+ assert prevLookupClass == null || ((allowedModes & MODULE) == 0
+ && prevLookupClass.getModule() != lookupClass.getModule());
+
this.lookupClass = lookupClass;
+ this.prevLookupClass = prevLookupClass;
this.allowedModes = allowedModes;
}
+ private static Lookup newLookup(Class<?> lookupClass, Class<?> prevLookupClass, int allowedModes) {
+ // make sure we haven't accidentally picked up a privileged class:
+ checkUnprivilegedlookupClass(lookupClass);
+ return new Lookup(lookupClass, prevLookupClass, allowedModes);
+ }
+
/**
* Creates a lookup on the specified new lookup class.
* The resulting object will report the specified
* class as its own {@link #lookupClass() lookupClass}.
+ *
* <p>
* However, the resulting {@code Lookup} object is guaranteed
* to have no more access capabilities than the original.
* In particular, access capabilities can be lost as follows:<ul>
- * <li>If the old lookup class is in a {@link Module#isNamed() named} module, and
- * the new lookup class is in a different module {@code M}, then no members, not
- * even public members in {@code M}'s exported packages, will be accessible.
- * The exception to this is when this lookup is {@link #publicLookup()
- * publicLookup}, in which case {@code PUBLIC} access is not lost.
- * <li>If the old lookup class is in an unnamed module, and the new lookup class
- * is a different module then {@link #MODULE MODULE} access is lost.
- * <li>If the new lookup class differs from the old one then {@code UNCONDITIONAL} is lost.
+ * <li>If the new lookup class is in a different module from the old one,
+ * i.e. {@link #MODULE MODULE} access is lost.
* <li>If the new lookup class is in a different package
- * than the old one, protected and default (package) members will not be accessible.
+ * than the old one, protected and default (package) members will not be accessible,
+ * i.e. {@link #PROTECTED PROTECTED} and {@link #PACKAGE PACKAGE} access are lost.
* <li>If the new lookup class is not within the same package member
* as the old one, private members will not be accessible, and protected members
- * will not be accessible by virtue of inheritance.
+ * will not be accessible by virtue of inheritance,
+ * i.e. {@link #PRIVATE PRIVATE} access is lost.
* (Protected members may continue to be accessible because of package sharing.)
- * <li>If the new lookup class is not accessible to the old lookup class,
- * then no members, not even public members, will be accessible.
- * (In all other cases, public members will continue to be accessible.)
+ * <li>If the new lookup class is not
+ * {@linkplain #accessClass(Class) accessible} to this lookup,
+ * then no members, not even public members, will be accessible
+ * i.e. all access modes are lost.
+ * <li>If the new lookup class, the old lookup class and the previous lookup class
+ * are all in different modules i.e. teleporting to a third module,
+ * all access modes are lost.
* </ul>
* <p>
+ * The new previous lookup class is chosen as follows:
+ * <ul>
+ * <li>If the new lookup object has {@link #UNCONDITIONAL UNCONDITIONAL} bit,
+ * the new previous lookup class is {@code null}.
+ * <li>If the new lookup class is in the same module as the old lookup class,
+ * the new previous lookup class is the old previous lookup class.
+ * <li>If the new lookup class is in a different module from the old lookup class,
+ * the new previous lookup class is the the old lookup class.
+ *</ul>
+ * <p>
* The resulting lookup's capabilities for loading classes
* (used during {@link #findClass} invocations)
* are determined by the lookup class' loader,
* which may change due to this operation.
- *
+ * <p>
* @param requestedLookupClass the desired lookup class for the new lookup object
* @return a lookup object which reports the desired lookup class, or the same object
* if there is no change
@@ -823,22 +1440,32 @@
*
* @revised 9
* @spec JPMS
+ * @see #accessClass(Class)
+ * @see <a href="#cross-module-lookup">Cross-module lookups</a>
*/
public Lookup in(Class<?> requestedLookupClass) {
Objects.requireNonNull(requestedLookupClass);
if (allowedModes == TRUSTED) // IMPL_LOOKUP can make any lookup at all
- return new Lookup(requestedLookupClass, FULL_POWER_MODES);
+ return new Lookup(requestedLookupClass, null, FULL_POWER_MODES);
if (requestedLookupClass == this.lookupClass)
return this; // keep same capabilities
int newModes = (allowedModes & FULL_POWER_MODES);
- if (!VerifyAccess.isSameModule(this.lookupClass, requestedLookupClass)) {
- // Need to drop all access when teleporting from a named module to another
- // module. The exception is publicLookup where PUBLIC is not lost.
- if (this.lookupClass.getModule().isNamed()
- && (this.allowedModes & UNCONDITIONAL) == 0)
+ Module fromModule = this.lookupClass.getModule();
+ Module targetModule = requestedLookupClass.getModule();
+ Class<?> plc = this.previousLookupClass();
+ if ((this.allowedModes & UNCONDITIONAL) != 0) {
+ assert plc == null;
+ newModes = UNCONDITIONAL;
+ } else if (fromModule != targetModule) {
+ if (plc != null && !VerifyAccess.isSameModule(plc, requestedLookupClass)) {
+ // allow hopping back and forth between fromModule and plc's module
+ // but not the third module
newModes = 0;
- else
- newModes &= ~(MODULE|PACKAGE|PRIVATE|PROTECTED);
+ }
+ // drop MODULE access
+ newModes &= ~(MODULE|PACKAGE|PRIVATE|PROTECTED);
+ // teleport from this lookup class
+ plc = this.lookupClass;
}
if ((newModes & PACKAGE) != 0
&& !VerifyAccess.isSamePackage(this.lookupClass, requestedLookupClass)) {
@@ -849,29 +1476,39 @@
&& !VerifyAccess.isSamePackageMember(this.lookupClass, requestedLookupClass)) {
newModes &= ~(PRIVATE|PROTECTED);
}
- if ((newModes & PUBLIC) != 0
- && !VerifyAccess.isClassAccessible(requestedLookupClass, this.lookupClass, allowedModes)) {
+ if ((newModes & (PUBLIC|UNCONDITIONAL)) != 0
+ && !VerifyAccess.isClassAccessible(requestedLookupClass, this.lookupClass, this.prevLookupClass, allowedModes)) {
// The requested class it not accessible from the lookup class.
// No permissions.
newModes = 0;
}
-
- checkUnprivilegedlookupClass(requestedLookupClass);
- return new Lookup(requestedLookupClass, newModes);
+ return newLookup(requestedLookupClass, plc, newModes);
}
-
/**
* Creates a lookup on the same lookup class which this lookup object
* finds members, but with a lookup mode that has lost the given lookup mode.
* The lookup mode to drop is one of {@link #PUBLIC PUBLIC}, {@link #MODULE
* MODULE}, {@link #PACKAGE PACKAGE}, {@link #PROTECTED PROTECTED} or {@link #PRIVATE PRIVATE}.
- * {@link #PROTECTED PROTECTED} and {@link #UNCONDITIONAL UNCONDITIONAL} are always
- * dropped and so the resulting lookup mode will never have these access capabilities.
+ * {@link #PROTECTED PROTECTED} is always
+ * dropped and so the resulting lookup mode will never have this access capability.
* When dropping {@code PACKAGE} then the resulting lookup will not have {@code PACKAGE}
* or {@code PRIVATE} access. When dropping {@code MODULE} then the resulting lookup will
* not have {@code MODULE}, {@code PACKAGE}, or {@code PRIVATE} access. If {@code PUBLIC}
+ * is dropped then the resulting lookup has no access. If {@code UNCONDITIONAL}
* is dropped then the resulting lookup has no access.
+ *
+ * @apiNote
+ * A lookup with {@code PACKAGE} but not {@code PRIVATE} mode can safely
+ * delegate non-public access within the package of the lookup class without
+ * conferring private access. A lookup with {@code MODULE} but not
+ * {@code PACKAGE} mode can safely delegate {@code PUBLIC} access within
+ * the module of the lookup class without conferring package access.
+ * A lookup with a {@linkplain #previousLookupClass() previous lookup class}
+ * (and {@code PUBLIC} but not {@code MODULE} mode) can safely delegate access
+ * to public classes accessible to both the module of the lookup class
+ * and the module of the previous lookup class.
+ *
* @param modeToDrop the lookup mode to drop
* @return a lookup object which lacks the indicated mode, or the same object if there is no change
* @throws IllegalArgumentException if {@code modeToDrop} is not one of {@code PUBLIC},
@@ -881,9 +1518,9 @@
*/
public Lookup dropLookupMode(int modeToDrop) {
int oldModes = lookupModes();
- int newModes = oldModes & ~(modeToDrop | PROTECTED | UNCONDITIONAL);
+ int newModes = oldModes & ~(modeToDrop | PROTECTED);
switch (modeToDrop) {
- case PUBLIC: newModes &= ~(ALL_MODES); break;
+ case PUBLIC: newModes &= ~(FULL_POWER_MODES); break;
case MODULE: newModes &= ~(PACKAGE | PRIVATE); break;
case PACKAGE: newModes &= ~(PRIVATE); break;
case PROTECTED:
@@ -892,7 +1529,7 @@
default: throw new IllegalArgumentException(modeToDrop + " is not a valid mode to drop");
}
if (newModes == oldModes) return this; // return self if no change
- return new Lookup(lookupClass(), newModes);
+ return newLookup(lookupClass(), previousLookupClass(), newModes);
}
/**
@@ -997,13 +1634,13 @@
static { IMPL_NAMES.getClass(); }
/** Package-private version of lookup which is trusted. */
- static final Lookup IMPL_LOOKUP = new Lookup(Object.class, TRUSTED);
+ static final Lookup IMPL_LOOKUP = new Lookup(Object.class, null, TRUSTED);
/** Version of lookup which is trusted minimally.
* It can only be used to create method handles to publicly accessible
* members in packages that are exported unconditionally.
*/
- static final Lookup PUBLIC_LOOKUP = new Lookup(Object.class, (PUBLIC|UNCONDITIONAL));
+ static final Lookup PUBLIC_LOOKUP = new Lookup(Object.class, null, UNCONDITIONAL);
private static void checkUnprivilegedlookupClass(Class<?> lookupClass) {
String name = lookupClass.getName();
@@ -1013,6 +1650,8 @@
/**
* Displays the name of the class from which lookups are to be made.
+ * followed with "/" and the name of the {@linkplain #previousLookupClass()
+ * previous lookup class} if present.
* (The name is the one reported by {@link java.lang.Class#getName() Class.getName}.)
* If there are restrictions on the access permitted to this lookup,
* this is indicated by adding a suffix to the class name, consisting
@@ -1020,14 +1659,14 @@
* allowed access, and is chosen as follows:
* <ul>
* <li>If no access is allowed, the suffix is "/noaccess".
+ * <li>If only unconditional access is allowed, the suffix is "/publicLookup".
* <li>If only public access to types in exported packages is allowed, the suffix is "/public".
- * <li>If only public access and unconditional access are allowed, the suffix is "/publicLookup".
* <li>If only public and module access are allowed, the suffix is "/module".
- * <li>If only public, module and package access are allowed, the suffix is "/package".
- * <li>If only public, module, package, and private access are allowed, the suffix is "/private".
+ * <li>If public and package access are allowed, the suffix is "/package".
+ * <li>If public, package, and private access are allowed, the suffix is "/private".
* </ul>
- * If none of the above cases apply, it is the case that full
- * access (public, module, package, private, and protected) is allowed.
+ * If none of the above cases apply, it is the case that full access
+ * (public, module, package, private, and protected) is allowed.
* In this case, no suffix is added.
* This is true only of an object obtained originally from
* {@link java.lang.invoke.MethodHandles#lookup MethodHandles.lookup}.
@@ -1047,20 +1686,25 @@
@Override
public String toString() {
String cname = lookupClass.getName();
+ if (prevLookupClass != null)
+ cname += "/" + prevLookupClass.getName();
switch (allowedModes) {
case 0: // no privileges
return cname + "/noaccess";
+ case UNCONDITIONAL:
+ return cname + "/publicLookup";
case PUBLIC:
return cname + "/public";
- case PUBLIC|UNCONDITIONAL:
- return cname + "/publicLookup";
case PUBLIC|MODULE:
return cname + "/module";
+ case PUBLIC|PACKAGE:
case PUBLIC|MODULE|PACKAGE:
return cname + "/package";
- case FULL_POWER_MODES & ~PROTECTED:
- return cname + "/private";
+ case FULL_POWER_MODES & (~PROTECTED):
+ case FULL_POWER_MODES & ~(PROTECTED|MODULE):
+ return cname + "/private";
case FULL_POWER_MODES:
+ case FULL_POWER_MODES & (~MODULE):
return cname;
case TRUSTED:
return "/trusted"; // internal only; not exported
@@ -1301,24 +1945,75 @@
}
/**
- * Determines if a class can be accessed from the lookup context defined by this {@code Lookup} object. The
- * static initializer of the class is not run.
+ * Determines if a class can be accessed from the lookup context defined by
+ * this {@code Lookup} object. The static initializer of the class is not run.
+ * <p>
+ * If the {@code targetClass} is in the same module as the lookup class,
+ * the lookup class is {@code LC} in module {@code M1} and
+ * the previous lookup class is in module {@code M0} or
+ * {@code null} if not present,
+ * {@code targetClass} is accessible if and only if one of the following is true:
+ * <ul>
+ * <li>If this lookup has {@link #PRIVATE} access, {@code targetClass} is
+ * {@code LC} or other class in the same nest of {@code LC}.</li>
+ * <li>If this lookup has {@link #PACKAGE} access, {@code targetClass} is
+ * in the same runtime package of {@code LC}.</li>
+ * <li>If this lookup has {@link #MODULE} access, {@code targetClass} is
+ * a public type in {@code M1}.</li>
+ * <li>If this lookup has {@link #PUBLIC} access, {@code targetClass} is
+ * a public type in a package exported by {@code M1} to at least {@code M0}
+ * if the previous lookup class is present; otherwise, {@code targetClass}
+ * is a public type in a package exported by {@code M1} unconditionally.</li>
+ * </ul>
+ *
+ * <p>
+ * Otherwise, if this lookup has {@link #UNCONDITIONAL} access, this lookup
+ * can access public types in all modules when the type is in a package
+ * that is exported unconditionally.
* <p>
- * The lookup context here is determined by the {@linkplain #lookupClass() lookup class} and the
- * {@linkplain #lookupModes() lookup modes}.
+ * Otherwise, the target class is in a different module from {@code lookupClass},
+ * and if this lookup does not have {@code PUBLIC} access, {@code lookupClass}
+ * is inaccessible.
+ * <p>
+ * Otherwise, if this lookup has no {@linkplain #previousLookupClass() previous lookup class},
+ * {@code M1} is the module containing {@code lookupClass} and
+ * {@code M2} is the module containing {@code targetClass},
+ * then {@code targetClass} is accessible if and only if
+ * <ul>
+ * <li>{@code M1} reads {@code M2}, and
+ * <li>{@code targetClass} is public and in a package exported by
+ * {@code M2} at least to {@code M1}.
+ * </ul>
+ * <p>
+ * Otherwise, if this lookup has a {@linkplain #previousLookupClass() previous lookup class},
+ * {@code M1} and {@code M2} are as before, and {@code M0} is the module
+ * containing the previous lookup class, then {@code targetClass} is accessible
+ * if and only if one of the following is true:
+ * <ul>
+ * <li>{@code targetClass} is in {@code M0} and {@code M1}
+ * {@linkplain Module#reads reads} {@code M0} and the type is
+ * in a package that is exported to at least {@code M1}.
+ * <li>{@code targetClass} is in {@code M1} and {@code M0}
+ * {@linkplain Module#reads reads} {@code M1} and the type is
+ * in a package that is exported to at least {@code M0}.
+ * <li>{@code targetClass} is in a third module {@code M2} and both {@code M0}
+ * and {@code M1} reads {@code M2} and the type is in a package
+ * that is exported to at least both {@code M0} and {@code M2}.
+ * </ul>
+ * <p>
+ * Otherwise, {@code targetClass} is not accessible.
*
* @param targetClass the class to be access-checked
- *
* @return the class that has been access-checked
- *
- * @throws IllegalAccessException if the class is not accessible from the lookup class, using the allowed access
- * modes.
+ * @throws IllegalAccessException if the class is not accessible from the lookup class
+ * and previous lookup class, if present, using the allowed access modes.
* @exception SecurityException if a security manager is present and it
* <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
* @since 9
+ * @see <a href="#cross-module-lookup">Cross-module lookups</a>
*/
public Class<?> accessClass(Class<?> targetClass) throws IllegalAccessException {
- if (!VerifyAccess.isClassAccessible(targetClass, lookupClass, allowedModes)) {
+ if (!VerifyAccess.isClassAccessible(targetClass, lookupClass, prevLookupClass, allowedModes)) {
throw new MemberName(targetClass).makeAccessException("access violation", this);
}
checkSecurityManager(targetClass, null);
@@ -2083,7 +2778,7 @@
boolean isClassAccessible(Class<?> refc) {
Objects.requireNonNull(refc);
Class<?> caller = lookupClassOrNull();
- return caller == null || VerifyAccess.isClassAccessible(refc, caller, allowedModes);
+ return caller == null || VerifyAccess.isClassAccessible(refc, caller, prevLookupClass, allowedModes);
}
/** Check name for an illegal leading "<" character. */
@@ -2220,7 +2915,7 @@
int requestedModes = fixmods(mods); // adjust 0 => PACKAGE
if ((requestedModes & allowedModes) != 0) {
if (VerifyAccess.isMemberAccessible(refc, m.getDeclaringClass(),
- mods, lookupClass(), allowedModes))
+ mods, lookupClass(), previousLookupClass(), allowedModes))
return;
} else {
// Protected members can also be checked as if they were package-private.
@@ -2239,9 +2934,10 @@
(defc == refc ||
Modifier.isPublic(refc.getModifiers())));
if (!classOK && (allowedModes & PACKAGE) != 0) {
- classOK = (VerifyAccess.isClassAccessible(defc, lookupClass(), FULL_POWER_MODES) &&
+ // ignore previous lookup class to check if default package access
+ classOK = (VerifyAccess.isClassAccessible(defc, lookupClass(), null, FULL_POWER_MODES) &&
(defc == refc ||
- VerifyAccess.isClassAccessible(refc, lookupClass(), FULL_POWER_MODES)));
+ VerifyAccess.isClassAccessible(refc, lookupClass(), null, FULL_POWER_MODES)));
}
if (!classOK)
return "class is not public";
--- a/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java Thu Jul 25 08:24:01 2019 -0400
@@ -30,6 +30,7 @@
import java.lang.ref.WeakReference;
import java.security.AccessController;
+import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.VM;
import jdk.internal.module.IllegalAccessLogger;
import jdk.internal.reflect.CallerSensitive;
@@ -77,6 +78,10 @@
* @spec JPMS
*/
public class AccessibleObject implements AnnotatedElement {
+ static {
+ // AccessibleObject is initialized early in initPhase1
+ SharedSecrets.setJavaLangReflectAccess(new ReflectAccess());
+ }
static void checkPermission() {
SecurityManager sm = System.getSecurityManager();
--- a/src/java.base/share/classes/java/lang/reflect/Constructor.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/lang/reflect/Constructor.java Thu Jul 25 08:24:01 2019 -0400
@@ -111,7 +111,7 @@
/**
* Package-private constructor used by ReflectAccess to enable
* instantiation of these objects in Java code from the java.lang
- * package via sun.reflect.LangReflectAccess.
+ * package via jdk.internal.access.JavaLangReflectAccess.
*/
Constructor(Class<T> declaringClass,
Class<?>[] parameterTypes,
--- a/src/java.base/share/classes/java/lang/reflect/Field.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/lang/reflect/Field.java Thu Jul 25 08:24:01 2019 -0400
@@ -113,9 +113,7 @@
/**
- * Package-private constructor used by ReflectAccess to enable
- * instantiation of these objects in Java code from the java.lang
- * package via sun.reflect.LangReflectAccess.
+ * Package-private constructor
*/
Field(Class<?> declaringClass,
String name,
--- a/src/java.base/share/classes/java/lang/reflect/Method.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/lang/reflect/Method.java Thu Jul 25 08:24:01 2019 -0400
@@ -113,9 +113,7 @@
}
/**
- * Package-private constructor used by ReflectAccess to enable
- * instantiation of these objects in Java code from the java.lang
- * package via sun.reflect.LangReflectAccess.
+ * Package-private constructor
*/
Method(Class<?> declaringClass,
String name,
--- a/src/java.base/share/classes/java/lang/reflect/Modifier.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/lang/reflect/Modifier.java Thu Jul 25 08:24:01 2019 -0400
@@ -25,10 +25,7 @@
package java.lang.reflect;
-import java.security.AccessController;
import java.util.StringJoiner;
-import jdk.internal.reflect.LangReflectAccess;
-import jdk.internal.reflect.ReflectionFactory;
/**
* The Modifier class provides {@code static} methods and
@@ -47,16 +44,6 @@
*/
public class Modifier {
- /*
- * Bootstrapping protocol between java.lang and java.lang.reflect
- * packages
- */
- static {
- ReflectionFactory factory = AccessController.doPrivileged(
- new ReflectionFactory.GetReflectionFactoryAction());
- factory.setLangReflectAccess(new java.lang.reflect.ReflectAccess());
- }
-
/**
* Return {@code true} if the integer argument includes the
* {@code public} modifier, {@code false} otherwise.
--- a/src/java.base/share/classes/java/lang/reflect/ReflectAccess.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/lang/reflect/ReflectAccess.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, 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
@@ -29,52 +29,10 @@
import jdk.internal.reflect.ConstructorAccessor;
/** Package-private class implementing the
- sun.reflect.LangReflectAccess interface, allowing the java.lang
+ jdk.internal.access.JavaLangReflectAccess interface, allowing the java.lang
package to instantiate objects in this package. */
-class ReflectAccess implements jdk.internal.reflect.LangReflectAccess {
- public Field newField(Class<?> declaringClass,
- String name,
- Class<?> type,
- int modifiers,
- int slot,
- String signature,
- byte[] annotations)
- {
- return new Field(declaringClass,
- name,
- type,
- modifiers,
- slot,
- signature,
- annotations);
- }
-
- public Method newMethod(Class<?> declaringClass,
- String name,
- Class<?>[] parameterTypes,
- Class<?> returnType,
- Class<?>[] checkedExceptions,
- int modifiers,
- int slot,
- String signature,
- byte[] annotations,
- byte[] parameterAnnotations,
- byte[] annotationDefault)
- {
- return new Method(declaringClass,
- name,
- parameterTypes,
- returnType,
- checkedExceptions,
- modifiers,
- slot,
- signature,
- annotations,
- parameterAnnotations,
- annotationDefault);
- }
-
+class ReflectAccess implements jdk.internal.access.JavaLangReflectAccess {
public <T> Constructor<T> newConstructor(Class<T> declaringClass,
Class<?>[] parameterTypes,
Class<?>[] checkedExceptions,
--- a/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -67,13 +67,7 @@
* Load net library into runtime.
*/
static {
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction<>() {
- public Void run() {
- System.loadLibrary("net");
- return null;
- }
- });
+ jdk.internal.loader.BootLoader.loadLibrary("net");
}
private static volatile boolean checkedReusePort;
--- a/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -94,13 +94,7 @@
* Load net library into runtime.
*/
static {
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction<>() {
- public Void run() {
- System.loadLibrary("net");
- return null;
- }
- });
+ jdk.internal.loader.BootLoader.loadLibrary("net");
}
private static volatile boolean checkedReusePort;
--- a/src/java.base/share/classes/java/net/DatagramPacket.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/net/DatagramPacket.java Thu Jul 25 08:24:01 2019 -0400
@@ -46,13 +46,7 @@
* Perform class initialization
*/
static {
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction<>() {
- public Void run() {
- System.loadLibrary("net");
- return null;
- }
- });
+ jdk.internal.loader.BootLoader.loadLibrary("net");
init();
}
--- a/src/java.base/share/classes/java/net/InetAddress.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/net/InetAddress.java Thu Jul 25 08:24:01 2019 -0400
@@ -317,13 +317,7 @@
} else {
preferIPv6Address = PREFER_IPV4_VALUE;
}
- AccessController.doPrivileged(
- new java.security.PrivilegedAction<>() {
- public Void run() {
- System.loadLibrary("net");
- return null;
- }
- });
+ jdk.internal.loader.BootLoader.loadLibrary("net");
SharedSecrets.setJavaNetInetAddressAccess(
new JavaNetInetAddressAccess() {
public String getOriginalHostName(InetAddress ia) {
--- a/src/java.base/share/classes/java/net/NetworkInterface.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/net/NetworkInterface.java Thu Jul 25 08:24:01 2019 -0400
@@ -28,7 +28,6 @@
import java.util.Arrays;
import java.util.Enumeration;
import java.util.NoSuchElementException;
-import java.security.AccessController;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
@@ -57,13 +56,7 @@
private static final int defaultIndex; /* index of defaultInterface */
static {
- AccessController.doPrivileged(
- new java.security.PrivilegedAction<>() {
- public Void run() {
- System.loadLibrary("net");
- return null;
- }
- });
+ jdk.internal.loader.BootLoader.loadLibrary("net");
init();
defaultInterface = DefaultInterface.getDefault();
--- a/src/java.base/share/classes/java/net/ServerSocket.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/net/ServerSocket.java Thu Jul 25 08:24:01 2019 -0400
@@ -27,17 +27,11 @@
import java.io.FileDescriptor;
import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
import java.nio.channels.ServerSocketChannel;
-import java.security.AccessController;
-import java.security.PrivilegedExceptionAction;
import java.util.Objects;
import java.util.Set;
import java.util.Collections;
-import jdk.internal.access.JavaNetSocketAccess;
-import jdk.internal.access.SharedSecrets;
import sun.net.PlatformSocketImpl;
/**
@@ -1116,27 +1110,4 @@
}
return options;
}
-
- static {
- SharedSecrets.setJavaNetSocketAccess(
- new JavaNetSocketAccess() {
- @Override
- public ServerSocket newServerSocket(SocketImpl impl) {
- return new ServerSocket(impl);
- }
-
- @Override
- public SocketImpl newSocketImpl(Class<? extends SocketImpl> implClass) {
- try {
- Constructor<? extends SocketImpl> ctor =
- implClass.getDeclaredConstructor();
- return ctor.newInstance();
- } catch (NoSuchMethodException | InstantiationException |
- IllegalAccessException | InvocationTargetException e) {
- throw new AssertionError(e);
- }
- }
- }
- );
- }
}
--- a/src/java.base/share/classes/java/net/URL.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/net/URL.java Thu Jul 25 08:24:01 2019 -0400
@@ -45,6 +45,7 @@
import jdk.internal.access.JavaNetURLAccess;
import jdk.internal.access.SharedSecrets;
+import sun.net.util.IPAddressUtil;
import sun.security.util.SecurityConstants;
import sun.security.action.GetPropertyAction;
@@ -466,13 +467,19 @@
this.file = path;
}
- // Note: we don't do validation of the URL here. Too risky to change
+ // Note: we don't do full validation of the URL here. Too risky to change
// right now, but worth considering for future reference. -br
if (handler == null &&
(handler = getURLStreamHandler(protocol)) == null) {
throw new MalformedURLException("unknown protocol: " + protocol);
}
this.handler = handler;
+ if (host != null && isBuiltinStreamHandler(handler)) {
+ String s = IPAddressUtil.checkExternalForm(this);
+ if (s != null) {
+ throw new MalformedURLException(s);
+ }
+ }
}
/**
@@ -1038,7 +1045,12 @@
* @since 1.5
*/
public URI toURI() throws URISyntaxException {
- return new URI (toString());
+ URI uri = new URI(toString());
+ if (authority != null && isBuiltinStreamHandler(handler)) {
+ String s = IPAddressUtil.checkAuthority(this);
+ if (s != null) throw new URISyntaxException(authority, s);
+ }
+ return uri;
}
/**
@@ -1635,6 +1647,10 @@
return replacementURL;
}
+ boolean isBuiltinStreamHandler(URLStreamHandler handler) {
+ return isBuiltinStreamHandler(handler.getClass().getName());
+ }
+
private boolean isBuiltinStreamHandler(String handlerClassName) {
return (handlerClassName.startsWith(BUILTIN_HANDLERS_PREFIX));
}
--- a/src/java.base/share/classes/java/net/URLClassLoader.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/net/URLClassLoader.java Thu Jul 25 08:24:01 2019 -0400
@@ -52,7 +52,6 @@
import jdk.internal.loader.Resource;
import jdk.internal.loader.URLClassPath;
-import jdk.internal.access.JavaNetURLClassLoaderAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.perf.PerfCounter;
import sun.net.www.ParseUtil;
@@ -822,14 +821,6 @@
}
static {
- SharedSecrets.setJavaNetURLClassLoaderAccess(
- new JavaNetURLClassLoaderAccess() {
- @Override
- public AccessControlContext getAccessControlContext(URLClassLoader u) {
- return u.acc;
- }
- }
- );
ClassLoader.registerAsParallelCapable();
}
}
--- a/src/java.base/share/classes/java/net/URLStreamHandler.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/net/URLStreamHandler.java Thu Jul 25 08:24:01 2019 -0400
@@ -516,12 +516,15 @@
* different from this one
* @since 1.3
*/
- protected void setURL(URL u, String protocol, String host, int port,
+ protected void setURL(URL u, String protocol, String host, int port,
String authority, String userInfo, String path,
String query, String ref) {
if (this != u.handler) {
throw new SecurityException("handler for url different from " +
"this handler");
+ } else if (host != null && u.isBuiltinStreamHandler(this)) {
+ String s = IPAddressUtil.checkHostString(host);
+ if (s != null) throw new IllegalArgumentException(s);
}
// ensure that no one can reset the protocol on a given URL.
u.set(u.getProtocol(), host, port, authority, userInfo, path, query, ref);
--- a/src/java.base/share/classes/java/security/AccessController.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/security/AccessController.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -445,7 +445,8 @@
throw new NullPointerException("null permissions parameter");
}
Class<?> caller = Reflection.getCallerClass();
- return AccessController.doPrivileged(action, createWrapper(null,
+ DomainCombiner dc = (context == null) ? null : context.getCombiner();
+ return AccessController.doPrivileged(action, createWrapper(dc,
caller, parent, context, perms));
}
@@ -860,7 +861,8 @@
throw new NullPointerException("null permissions parameter");
}
Class<?> caller = Reflection.getCallerClass();
- return AccessController.doPrivileged(action, createWrapper(null, caller, parent, context, perms));
+ DomainCombiner dc = (context == null) ? null : context.getCombiner();
+ return AccessController.doPrivileged(action, createWrapper(dc, caller, parent, context, perms));
}
--- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -85,7 +85,6 @@
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.chrono.ChronoLocalDate;
-import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.Chronology;
import java.time.chrono.Era;
import java.time.chrono.IsoChronology;
@@ -122,7 +121,6 @@
import sun.text.spi.JavaTimeDateTimePatternProvider;
import sun.util.locale.provider.CalendarDataUtility;
import sun.util.locale.provider.LocaleProviderAdapter;
-import sun.util.locale.provider.LocaleResources;
import sun.util.locale.provider.TimeZoneNameUtility;
/**
@@ -3871,7 +3869,11 @@
if (offsetSecs == null) {
return false;
}
- String gmtText = "GMT"; // TODO: get localized version of 'GMT'
+ String key = "timezone.gmtZeroFormat";
+ String gmtText = DateTimeTextProvider.getLocalizedResource(key, context.getLocale());
+ if (gmtText == null) {
+ gmtText = "GMT"; // Default to "GMT"
+ }
buf.append(gmtText);
int totalSecs = Math.toIntExact(offsetSecs);
if (totalSecs != 0) {
@@ -3917,7 +3919,11 @@
public int parse(DateTimeParseContext context, CharSequence text, int position) {
int pos = position;
int end = text.length();
- String gmtText = "GMT"; // TODO: get localized version of 'GMT'
+ String key = "timezone.gmtZeroFormat";
+ String gmtText = DateTimeTextProvider.getLocalizedResource(key, context.getLocale());
+ if (gmtText == null) {
+ gmtText = "GMT"; // Default to "GMT"
+ }
if (!context.subSequenceEquals(text, pos, gmtText, 0, gmtText.length())) {
return ~position;
}
--- a/src/java.base/share/classes/java/util/Collections.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/util/Collections.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -26,6 +26,7 @@
package java.util;
import java.io.IOException;
+import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
@@ -39,6 +40,7 @@
import java.util.stream.IntStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
+import jdk.internal.access.SharedSecrets;
/**
* This class consists exclusively of static methods that operate on or return
@@ -5163,6 +5165,11 @@
public Spliterator<E> spliterator() {
return stream().spliterator();
}
+
+ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+ ois.defaultReadObject();
+ SharedSecrets.getJavaObjectInputStreamAccess().checkArray(ois, Object[].class, n);
+ }
}
/**
--- a/src/java.base/share/classes/java/util/EnumSet.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/util/EnumSet.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -76,11 +76,12 @@
* @since 1.5
* @see EnumMap
*/
-@SuppressWarnings("serial") // No serialVersionUID due to usage of
- // serial proxy pattern
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
implements Cloneable, java.io.Serializable
{
+ // declare EnumSet.class serialization compatibility with JDK 8
+ private static final long serialVersionUID = 1009687484059888093L;
+
/**
* The class of all the elements of this set.
*/
--- a/src/java.base/share/classes/java/util/zip/ZipUtils.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/java/util/zip/ZipUtils.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -27,8 +27,6 @@
import java.nio.ByteBuffer;
import java.nio.file.attribute.FileTime;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDateTime;
@@ -278,13 +276,7 @@
* Loads zip native library, if not already laoded
*/
static void loadLibrary() {
- SecurityManager sm = System.getSecurityManager();
- if (sm == null) {
- System.loadLibrary("zip");
- } else {
- PrivilegedAction<Void> pa = () -> { System.loadLibrary("zip"); return null; };
- AccessController.doPrivileged(pa);
- }
+ jdk.internal.loader.BootLoader.loadLibrary("zip");
}
private static final Unsafe unsafe = Unsafe.getUnsafe();
--- a/src/java.base/share/classes/javax/net/ssl/SSLSessionContext.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/javax/net/ssl/SSLSessionContext.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -35,7 +35,11 @@
* it could be associated with a server or client who participates in many
* sessions concurrently.
* <p>
- * Not all environments will contain session contexts.
+ * Not all environments will contain session contexts. For example, stateless
+ * session resumption.
+ * <p>
+ * Session contexts may not contain all sessions. For example, stateless
+ * sessions are not stored in the session context.
* <p>
* There are <code>SSLSessionContext</code> parameters that affect how
* sessions are stored:
@@ -68,8 +72,11 @@
public SSLSession getSession(byte[] sessionId);
/**
- * Returns an Enumeration of all session id's grouped under this
+ * Returns an Enumeration of all known session id's grouped under this
* <code>SSLSessionContext</code>.
+ * <p>Session contexts may not contain all sessions. For example,
+ * stateless sessions are not stored in the session context.
+ * <p>
*
* @return an enumeration of all the Session id's
*/
--- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -311,4 +311,12 @@
* @param cause set t's cause to new value
*/
void setCause(Throwable t, Throwable cause);
+
+ /**
+ * Privileged System.loadLibrary
+ *
+ * @param caller on behalf of which the library is being loaded
+ * @param library name of the library to load
+ */
+ void loadLibrary(Class<?> caller, String library);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/access/JavaLangReflectAccess.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2001, 2019, 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 jdk.internal.access;
+
+import java.lang.reflect.*;
+import jdk.internal.reflect.*;
+
+/** An interface which gives privileged packages Java-level access to
+ internals of java.lang.reflect. */
+
+public interface JavaLangReflectAccess {
+ /** Creates a new java.lang.reflect.Constructor. Access checks as
+ per java.lang.reflect.AccessibleObject are not overridden. */
+ public <T> Constructor<T> newConstructor(Class<T> declaringClass,
+ Class<?>[] parameterTypes,
+ Class<?>[] checkedExceptions,
+ int modifiers,
+ int slot,
+ String signature,
+ byte[] annotations,
+ byte[] parameterAnnotations);
+
+ /** Gets the MethodAccessor object for a java.lang.reflect.Method */
+ public MethodAccessor getMethodAccessor(Method m);
+
+ /** Sets the MethodAccessor object for a java.lang.reflect.Method */
+ public void setMethodAccessor(Method m, MethodAccessor accessor);
+
+ /** Gets the ConstructorAccessor object for a
+ java.lang.reflect.Constructor */
+ public ConstructorAccessor getConstructorAccessor(Constructor<?> c);
+
+ /** Sets the ConstructorAccessor object for a
+ java.lang.reflect.Constructor */
+ public void setConstructorAccessor(Constructor<?> c,
+ ConstructorAccessor accessor);
+
+ /** Gets the byte[] that encodes TypeAnnotations on an Executable. */
+ public byte[] getExecutableTypeAnnotationBytes(Executable ex);
+
+ /** Gets the "slot" field from a Constructor (used for serialization) */
+ public int getConstructorSlot(Constructor<?> c);
+
+ /** Gets the "signature" field from a Constructor (used for serialization) */
+ public String getConstructorSignature(Constructor<?> c);
+
+ /** Gets the "annotations" field from a Constructor (used for serialization) */
+ public byte[] getConstructorAnnotations(Constructor<?> c);
+
+ /** Gets the "parameterAnnotations" field from a Constructor (used for serialization) */
+ public byte[] getConstructorParameterAnnotations(Constructor<?> c);
+
+ /** Gets the shared array of parameter types of an Executable. */
+ public Class<?>[] getExecutableSharedParameterTypes(Executable ex);
+
+ //
+ // Copying routines, needed to quickly fabricate new Field,
+ // Method, and Constructor objects from templates
+ //
+
+ /** Makes a "child" copy of a Method */
+ public Method copyMethod(Method arg);
+
+ /** Makes a copy of this non-root a Method */
+ public Method leafCopyMethod(Method arg);
+
+ /** Makes a "child" copy of a Field */
+ public Field copyField(Field arg);
+
+ /** Makes a "child" copy of a Constructor */
+ public <T> Constructor<T> copyConstructor(Constructor<T> arg);
+
+ /** Gets the root of the given AccessibleObject object; null if arg is the root */
+ public <T extends AccessibleObject> T getRoot(T obj);
+
+ /** Returns a new instance created by the given constructor with access check */
+ public <T> T newInstance(Constructor<T> ctor, Object[] args, Class<?> caller)
+ throws IllegalAccessException, InstantiationException, InvocationTargetException;
+}
--- a/src/java.base/share/classes/jdk/internal/access/JavaNetSocketAccess.java Thu Jul 25 08:13:44 2019 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * 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 jdk.internal.access;
-
-import java.net.ServerSocket;
-import java.net.SocketImpl;
-
-public interface JavaNetSocketAccess {
- /**
- * Creates a ServerSocket associated with the given SocketImpl.
- */
- ServerSocket newServerSocket(SocketImpl impl);
-
- /*
- * Constructs a SocketImpl instance of the given class.
- */
- SocketImpl newSocketImpl(Class<? extends SocketImpl> implClass);
-}
--- a/src/java.base/share/classes/jdk/internal/access/JavaNetURLClassLoaderAccess.java Thu Jul 25 08:13:44 2019 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * 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 jdk.internal.access;
-
-import java.net.URLClassLoader;
-import java.security.AccessControlContext;
-
-public interface JavaNetURLClassLoaderAccess {
- AccessControlContext getAccessControlContext(URLClassLoader u);;
-}
--- a/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java Thu Jul 25 08:24:01 2019 -0400
@@ -50,30 +50,29 @@
public class SharedSecrets {
private static final Unsafe unsafe = Unsafe.getUnsafe();
- private static JavaUtilJarAccess javaUtilJarAccess;
- private static JavaLangAccess javaLangAccess;
- private static JavaLangModuleAccess javaLangModuleAccess;
- private static JavaLangInvokeAccess javaLangInvokeAccess;
- private static JavaLangRefAccess javaLangRefAccess;
- private static JavaIOAccess javaIOAccess;
- private static JavaNetInetAddressAccess javaNetInetAddressAccess;
- private static JavaNetHttpCookieAccess javaNetHttpCookieAccess;
- private static JavaNetSocketAccess javaNetSocketAccess;
- private static JavaNetUriAccess javaNetUriAccess;
- private static JavaNetURLAccess javaNetURLAccess;
- private static JavaNetURLClassLoaderAccess javaNetURLClassLoaderAccess;
- private static JavaNioAccess javaNioAccess;
- private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess;
- private static JavaIOFilePermissionAccess javaIOFilePermissionAccess;
- private static JavaSecurityAccess javaSecurityAccess;
- private static JavaUtilZipFileAccess javaUtilZipFileAccess;
- private static JavaUtilResourceBundleAccess javaUtilResourceBundleAccess;
private static JavaAWTAccess javaAWTAccess;
private static JavaAWTFontAccess javaAWTFontAccess;
private static JavaBeansAccess javaBeansAccess;
+ private static JavaLangAccess javaLangAccess;
+ private static JavaLangInvokeAccess javaLangInvokeAccess;
+ private static JavaLangModuleAccess javaLangModuleAccess;
+ private static JavaLangRefAccess javaLangRefAccess;
+ private static JavaLangReflectAccess javaLangReflectAccess;
+ private static JavaIOAccess javaIOAccess;
+ private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess;
+ private static JavaIOFilePermissionAccess javaIOFilePermissionAccess;
+ private static JavaIORandomAccessFileAccess javaIORandomAccessFileAccess;
private static JavaObjectInputStreamAccess javaObjectInputStreamAccess;
private static JavaObjectInputFilterAccess javaObjectInputFilterAccess;
- private static JavaIORandomAccessFileAccess javaIORandomAccessFileAccess;
+ private static JavaNetInetAddressAccess javaNetInetAddressAccess;
+ private static JavaNetHttpCookieAccess javaNetHttpCookieAccess;
+ private static JavaNetUriAccess javaNetUriAccess;
+ private static JavaNetURLAccess javaNetURLAccess;
+ private static JavaNioAccess javaNioAccess;
+ private static JavaUtilJarAccess javaUtilJarAccess;
+ private static JavaUtilZipFileAccess javaUtilZipFileAccess;
+ private static JavaUtilResourceBundleAccess javaUtilResourceBundleAccess;
+ private static JavaSecurityAccess javaSecurityAccess;
private static JavaSecuritySignatureAccess javaSecuritySignatureAccess;
private static JavaxCryptoSealedObjectAccess javaxCryptoSealedObjectAccess;
@@ -131,6 +130,14 @@
return javaLangRefAccess;
}
+ public static void setJavaLangReflectAccess(JavaLangReflectAccess jlra) {
+ javaLangReflectAccess = jlra;
+ }
+
+ public static JavaLangReflectAccess getJavaLangReflectAccess() {
+ return javaLangReflectAccess;
+ }
+
public static void setJavaNetUriAccess(JavaNetUriAccess jnua) {
javaNetUriAccess = jnua;
}
@@ -151,16 +158,6 @@
return javaNetURLAccess;
}
- public static void setJavaNetURLClassLoaderAccess(JavaNetURLClassLoaderAccess jnua) {
- javaNetURLClassLoaderAccess = jnua;
- }
-
- public static JavaNetURLClassLoaderAccess getJavaNetURLClassLoaderAccess() {
- if (javaNetURLClassLoaderAccess == null)
- unsafe.ensureClassInitialized(java.net.URLClassLoader.class);
- return javaNetURLClassLoaderAccess;
- }
-
public static void setJavaNetInetAddressAccess(JavaNetInetAddressAccess jna) {
javaNetInetAddressAccess = jna;
}
@@ -181,16 +178,6 @@
return javaNetHttpCookieAccess;
}
- public static void setJavaNetSocketAccess(JavaNetSocketAccess jnsa) {
- javaNetSocketAccess = jnsa;
- }
-
- public static JavaNetSocketAccess getJavaNetSocketAccess() {
- if (javaNetSocketAccess == null)
- unsafe.ensureClassInitialized(java.net.ServerSocket.class);
- return javaNetSocketAccess;
- }
-
public static void setJavaNioAccess(JavaNioAccess jna) {
javaNioAccess = jna;
}
--- a/src/java.base/share/classes/jdk/internal/loader/BootLoader.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/jdk/internal/loader/BootLoader.java Thu Jul 25 08:24:01 2019 -0400
@@ -129,6 +129,23 @@
}
/**
+ * Loads a library from the system path.
+ */
+ public static void loadLibrary(String library) {
+ if (System.getSecurityManager() == null) {
+ SharedSecrets.getJavaLangAccess().loadLibrary(BootLoader.class, library);
+ } else {
+ AccessController.doPrivileged(
+ new java.security.PrivilegedAction<>() {
+ public Void run() {
+ SharedSecrets.getJavaLangAccess().loadLibrary(BootLoader.class, library);
+ return null;
+ }
+ });
+ }
+ }
+
+ /**
* Returns a URL to a resource in a module defined to the boot loader.
*/
public static URL findResource(String mn, String name) throws IOException {
--- a/src/java.base/share/classes/jdk/internal/reflect/LangReflectAccess.java Thu Jul 25 08:13:44 2019 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +0,0 @@
-/*
- * Copyright (c) 2001, 2013, 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 jdk.internal.reflect;
-
-import java.lang.reflect.*;
-
-/** An interface which gives privileged packages Java-level access to
- internals of java.lang.reflect. */
-
-public interface LangReflectAccess {
- /** Creates a new java.lang.reflect.Field. Access checks as per
- java.lang.reflect.AccessibleObject are not overridden. */
- public Field newField(Class<?> declaringClass,
- String name,
- Class<?> type,
- int modifiers,
- int slot,
- String signature,
- byte[] annotations);
-
- /** Creates a new java.lang.reflect.Method. Access checks as per
- java.lang.reflect.AccessibleObject are not overridden. */
- public Method newMethod(Class<?> declaringClass,
- String name,
- Class<?>[] parameterTypes,
- Class<?> returnType,
- Class<?>[] checkedExceptions,
- int modifiers,
- int slot,
- String signature,
- byte[] annotations,
- byte[] parameterAnnotations,
- byte[] annotationDefault);
-
- /** Creates a new java.lang.reflect.Constructor. Access checks as
- per java.lang.reflect.AccessibleObject are not overridden. */
- public <T> Constructor<T> newConstructor(Class<T> declaringClass,
- Class<?>[] parameterTypes,
- Class<?>[] checkedExceptions,
- int modifiers,
- int slot,
- String signature,
- byte[] annotations,
- byte[] parameterAnnotations);
-
- /** Gets the MethodAccessor object for a java.lang.reflect.Method */
- public MethodAccessor getMethodAccessor(Method m);
-
- /** Sets the MethodAccessor object for a java.lang.reflect.Method */
- public void setMethodAccessor(Method m, MethodAccessor accessor);
-
- /** Gets the ConstructorAccessor object for a
- java.lang.reflect.Constructor */
- public ConstructorAccessor getConstructorAccessor(Constructor<?> c);
-
- /** Sets the ConstructorAccessor object for a
- java.lang.reflect.Constructor */
- public void setConstructorAccessor(Constructor<?> c,
- ConstructorAccessor accessor);
-
- /** Gets the byte[] that encodes TypeAnnotations on an Executable. */
- public byte[] getExecutableTypeAnnotationBytes(Executable ex);
-
- /** Gets the "slot" field from a Constructor (used for serialization) */
- public int getConstructorSlot(Constructor<?> c);
-
- /** Gets the "signature" field from a Constructor (used for serialization) */
- public String getConstructorSignature(Constructor<?> c);
-
- /** Gets the "annotations" field from a Constructor (used for serialization) */
- public byte[] getConstructorAnnotations(Constructor<?> c);
-
- /** Gets the "parameterAnnotations" field from a Constructor (used for serialization) */
- public byte[] getConstructorParameterAnnotations(Constructor<?> c);
-
- /** Gets the shared array of parameter types of an Executable. */
- public Class<?>[] getExecutableSharedParameterTypes(Executable ex);
-
- //
- // Copying routines, needed to quickly fabricate new Field,
- // Method, and Constructor objects from templates
- //
-
- /** Makes a "child" copy of a Method */
- public Method copyMethod(Method arg);
-
- /** Makes a copy of this non-root a Method */
- public Method leafCopyMethod(Method arg);
-
- /** Makes a "child" copy of a Field */
- public Field copyField(Field arg);
-
- /** Makes a "child" copy of a Constructor */
- public <T> Constructor<T> copyConstructor(Constructor<T> arg);
-
- /** Gets the root of the given AccessibleObject object; null if arg is the root */
- public <T extends AccessibleObject> T getRoot(T obj);
-
- /** Returns a new instance created by the given constructor with access check */
- public <T> T newInstance(Constructor<T> ctor, Object[] args, Class<?> caller)
- throws IllegalAccessException, InstantiationException, InvocationTargetException;
-}
--- a/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java Thu Jul 25 08:24:01 2019 -0400
@@ -43,6 +43,8 @@
import java.util.Objects;
import java.util.Properties;
+import jdk.internal.access.JavaLangReflectAccess;
+import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.VM;
import sun.reflect.misc.ReflectUtil;
import sun.security.action.GetPropertyAction;
@@ -64,8 +66,7 @@
private static boolean initted = false;
private static final ReflectionFactory soleInstance = new ReflectionFactory();
- // Provides access to package-private mechanisms in java.lang.reflect
- private static volatile LangReflectAccess langReflectAccess;
+
/* Method for static class initializer <clinit>, or null */
private static volatile Method hasStaticInitializerMethod;
@@ -90,7 +91,9 @@
// true if deserialization constructor checking is disabled
private static boolean disableSerialConstructorChecks = false;
+ private final JavaLangReflectAccess langReflectAccess;
private ReflectionFactory() {
+ this.langReflectAccess = SharedSecrets.getJavaLangReflectAccess();
}
/**
@@ -160,12 +163,7 @@
//
//
- /** Called only by java.lang.reflect.Modifier's static initializer */
- public void setLangReflectAccess(LangReflectAccess access) {
- langReflectAccess = access;
- }
-
- /**
+ /*
* Note: this routine can cause the declaring class for the field
* be initialized and therefore must not be called until the
* first get/set of this field.
@@ -175,7 +173,7 @@
public FieldAccessor newFieldAccessor(Field field, boolean override) {
checkInitted();
- Field root = langReflectAccess().getRoot(field);
+ Field root = langReflectAccess.getRoot(field);
if (root != null) {
// FieldAccessor will use the root unless the modifiers have
// been overrridden
@@ -197,7 +195,7 @@
}
// use the root Method that will not cache caller class
- Method root = langReflectAccess().getRoot(method);
+ Method root = langReflectAccess.getRoot(method);
if (root != null) {
method = root;
}
@@ -233,7 +231,7 @@
}
// use the root Constructor that will not cache caller class
- Constructor<?> root = langReflectAccess().getRoot(c);
+ Constructor<?> root = langReflectAccess.getRoot(c);
if (root != null) {
c = root;
}
@@ -268,52 +266,6 @@
//
//
- /** Creates a new java.lang.reflect.Field. Access checks as per
- java.lang.reflect.AccessibleObject are not overridden. */
- public Field newField(Class<?> declaringClass,
- String name,
- Class<?> type,
- int modifiers,
- int slot,
- String signature,
- byte[] annotations)
- {
- return langReflectAccess().newField(declaringClass,
- name,
- type,
- modifiers,
- slot,
- signature,
- annotations);
- }
-
- /** Creates a new java.lang.reflect.Method. Access checks as per
- java.lang.reflect.AccessibleObject are not overridden. */
- public Method newMethod(Class<?> declaringClass,
- String name,
- Class<?>[] parameterTypes,
- Class<?> returnType,
- Class<?>[] checkedExceptions,
- int modifiers,
- int slot,
- String signature,
- byte[] annotations,
- byte[] parameterAnnotations,
- byte[] annotationDefault)
- {
- return langReflectAccess().newMethod(declaringClass,
- name,
- parameterTypes,
- returnType,
- checkedExceptions,
- modifiers,
- slot,
- signature,
- annotations,
- parameterAnnotations,
- annotationDefault);
- }
-
/** Creates a new java.lang.reflect.Constructor. Access checks as
per java.lang.reflect.AccessibleObject are not overridden. */
public Constructor<?> newConstructor(Class<?> declaringClass,
@@ -325,30 +277,20 @@
byte[] annotations,
byte[] parameterAnnotations)
{
- return langReflectAccess().newConstructor(declaringClass,
- parameterTypes,
- checkedExceptions,
- modifiers,
- slot,
- signature,
- annotations,
- parameterAnnotations);
- }
-
- /** Gets the MethodAccessor object for a java.lang.reflect.Method */
- public MethodAccessor getMethodAccessor(Method m) {
- return langReflectAccess().getMethodAccessor(m);
- }
-
- /** Sets the MethodAccessor object for a java.lang.reflect.Method */
- public void setMethodAccessor(Method m, MethodAccessor accessor) {
- langReflectAccess().setMethodAccessor(m, accessor);
+ return langReflectAccess.newConstructor(declaringClass,
+ parameterTypes,
+ checkedExceptions,
+ modifiers,
+ slot,
+ signature,
+ annotations,
+ parameterAnnotations);
}
/** Gets the ConstructorAccessor object for a
java.lang.reflect.Constructor */
public ConstructorAccessor getConstructorAccessor(Constructor<?> c) {
- return langReflectAccess().getConstructorAccessor(c);
+ return langReflectAccess.getConstructorAccessor(c);
}
/** Sets the ConstructorAccessor object for a
@@ -356,21 +298,21 @@
public void setConstructorAccessor(Constructor<?> c,
ConstructorAccessor accessor)
{
- langReflectAccess().setConstructorAccessor(c, accessor);
+ langReflectAccess.setConstructorAccessor(c, accessor);
}
/** Makes a copy of the passed method. The returned method is a
"child" of the passed one; see the comments in Method.java for
details. */
public Method copyMethod(Method arg) {
- return langReflectAccess().copyMethod(arg);
+ return langReflectAccess.copyMethod(arg);
}
/** Makes a copy of the passed method. The returned method is NOT
* a "child" but a "sibling" of the Method in arg. Should only be
* used on non-root methods. */
public Method leafCopyMethod(Method arg) {
- return langReflectAccess().leafCopyMethod(arg);
+ return langReflectAccess.leafCopyMethod(arg);
}
@@ -378,30 +320,30 @@
"child" of the passed one; see the comments in Field.java for
details. */
public Field copyField(Field arg) {
- return langReflectAccess().copyField(arg);
+ return langReflectAccess.copyField(arg);
}
/** Makes a copy of the passed constructor. The returned
constructor is a "child" of the passed one; see the comments
in Constructor.java for details. */
public <T> Constructor<T> copyConstructor(Constructor<T> arg) {
- return langReflectAccess().copyConstructor(arg);
+ return langReflectAccess.copyConstructor(arg);
}
/** Gets the byte[] that encodes TypeAnnotations on an executable.
*/
public byte[] getExecutableTypeAnnotationBytes(Executable ex) {
- return langReflectAccess().getExecutableTypeAnnotationBytes(ex);
+ return langReflectAccess.getExecutableTypeAnnotationBytes(ex);
}
public Class<?>[] getExecutableSharedParameterTypes(Executable ex) {
- return langReflectAccess().getExecutableSharedParameterTypes(ex);
+ return langReflectAccess.getExecutableSharedParameterTypes(ex);
}
public <T> T newInstance(Constructor<T> ctor, Object[] args, Class<?> caller)
throws IllegalAccessException, InstantiationException, InvocationTargetException
{
- return langReflectAccess().newInstance(ctor, args, caller);
+ return langReflectAccess.newInstance(ctor, args, caller);
}
//--------------------------------------------------------------------------
@@ -526,13 +468,13 @@
constructorToCall.getParameterTypes(),
constructorToCall.getExceptionTypes(),
constructorToCall.getModifiers(),
- langReflectAccess().
+ langReflectAccess.
getConstructorSlot(constructorToCall),
- langReflectAccess().
+ langReflectAccess.
getConstructorSignature(constructorToCall),
- langReflectAccess().
+ langReflectAccess.
getConstructorAnnotations(constructorToCall),
- langReflectAccess().
+ langReflectAccess.
getConstructorParameterAnnotations(constructorToCall));
setConstructorAccessor(c, acc);
c.setAccessible(true);
@@ -725,17 +667,6 @@
initted = true;
}
- private static LangReflectAccess langReflectAccess() {
- if (langReflectAccess == null) {
- // Call a static method to get class java.lang.reflect.Modifier
- // initialized. Its static initializer will cause
- // setLangReflectAccess() to be called from the context of the
- // java.lang.reflect package.
- Modifier.isPublic(Modifier.PUBLIC);
- }
- return langReflectAccess;
- }
-
/**
* Returns true if classes are defined in the classloader and same package, false
* otherwise.
--- a/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java Thu Jul 25 08:24:01 2019 -0400
@@ -88,18 +88,20 @@
* @param defc the class in which the proposed member is actually defined
* @param mods modifier flags for the proposed member
* @param lookupClass the class for which the access check is being made
+ * @param prevLookupClass the class for which the access check is being made
+ * @param allowedModes allowed modes
* @return true iff the accessing class can access such a member
*/
public static boolean isMemberAccessible(Class<?> refc, // symbolic ref class
Class<?> defc, // actual def class
int mods, // actual member mods
Class<?> lookupClass,
+ Class<?> prevLookupClass,
int allowedModes) {
if (allowedModes == 0) return false;
- assert((allowedModes & PUBLIC) != 0 &&
- (allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
+ assert((allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
// The symbolic reference class (refc) must always be fully verified.
- if (!isClassAccessible(refc, lookupClass, allowedModes)) {
+ if (!isClassAccessible(refc, lookupClass, prevLookupClass, allowedModes)) {
return false;
}
// Usually refc and defc are the same, but verify defc also in case they differ.
@@ -109,6 +111,7 @@
switch (mods & ALL_ACCESS_MODES) {
case PUBLIC:
+ assert (allowedModes & PUBLIC) != 0 || (allowedModes & UNCONDITIONAL_ALLOWED) != 0;
return true; // already checked above
case PROTECTED:
assert !defc.isInterface(); // protected members aren't allowed in interfaces
@@ -175,14 +178,23 @@
* package that is exported to the module that contains D.
* <li>C and D are members of the same runtime package.
* </ul>
+ *
* @param refc the symbolic reference class to which access is being checked (C)
* @param lookupClass the class performing the lookup (D)
+ * @param prevLookupClass the class from which the lookup was teleported or null
+ * @param allowedModes allowed modes
*/
- public static boolean isClassAccessible(Class<?> refc, Class<?> lookupClass,
+ public static boolean isClassAccessible(Class<?> refc,
+ Class<?> lookupClass,
+ Class<?> prevLookupClass,
int allowedModes) {
if (allowedModes == 0) return false;
- assert((allowedModes & PUBLIC) != 0 &&
- (allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
+ assert((allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
+
+ if ((allowedModes & PACKAGE_ALLOWED) != 0 &&
+ isSamePackage(lookupClass, refc))
+ return true;
+
int mods = getClassModifiers(refc);
if (isPublic(mods)) {
@@ -195,37 +207,62 @@
return true;
}
- // trivially allow
- if ((allowedModes & MODULE_ALLOWED) != 0 &&
- (lookupModule == refModule))
+ // allow access to public types in all unconditionally exported packages
+ if ((allowedModes & UNCONDITIONAL_ALLOWED) != 0) {
+ return refModule.isExported(refc.getPackageName());
+ }
+
+ if (lookupModule == refModule && prevLookupClass == null) {
+ // allow access to all public types in lookupModule
+ if ((allowedModes & MODULE_ALLOWED) != 0)
+ return true;
+
+ assert (allowedModes & PUBLIC) != 0;
+ return refModule.isExported(refc.getPackageName());
+ }
+
+ // cross-module access
+ // 1. refc is in different module from lookupModule, or
+ // 2. refc is in lookupModule and a different module from prevLookupModule
+ Module prevLookupModule = prevLookupClass != null ? prevLookupClass.getModule()
+ : null;
+ assert refModule != lookupModule || refModule != prevLookupModule;
+ if (isModuleAccessible(refc, lookupModule, prevLookupModule))
return true;
- // check readability when UNCONDITIONAL not allowed
- if (((allowedModes & UNCONDITIONAL_ALLOWED) != 0)
- || lookupModule.canRead(refModule)) {
-
- // check that refc is in an exported package
- if ((allowedModes & MODULE_ALLOWED) != 0) {
- if (refModule.isExported(refc.getPackageName(), lookupModule))
- return true;
- } else {
- // exported unconditionally
- if (refModule.isExported(refc.getPackageName()))
- return true;
- }
-
- // not exported but allow access during VM initialization
- // because java.base does not have its exports setup
- if (!jdk.internal.misc.VM.isModuleSystemInited())
- return true;
- }
+ // not exported but allow access during VM initialization
+ // because java.base does not have its exports setup
+ if (!jdk.internal.misc.VM.isModuleSystemInited())
+ return true;
// public class not accessible to lookupClass
return false;
}
- if ((allowedModes & PACKAGE_ALLOWED) != 0 &&
- isSamePackage(lookupClass, refc))
- return true;
+
+ return false;
+ }
+
+ /*
+ * Tests if a class or interface REFC is accessible to m1 and m2 where m2
+ * may be null.
+ *
+ * A class or interface REFC in m is accessible to m1 and m2 if and only if
+ * both m1 and m2 read m and m exports the package of REFC at least to
+ * both m1 and m2.
+ */
+ public static boolean isModuleAccessible(Class<?> refc, Module m1, Module m2) {
+ Module refModule = refc.getModule();
+ assert refModule != m1 || refModule != m2;
+ int mods = getClassModifiers(refc);
+ if (isPublic(mods)) {
+ if (m1.canRead(refModule) && (m2 == null || m2.canRead(refModule))) {
+ String pn = refc.getPackageName();
+
+ // refc is exported package to at least both m1 and m2
+ if (refModule.isExported(pn, m1) && (m2 == null || refModule.isExported(pn, m2)))
+ return true;
+ }
+ }
return false;
}
--- a/src/java.base/share/classes/sun/net/ext/ExtendedSocketOptions.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/net/ext/ExtendedSocketOptions.java Thu Jul 25 08:24:01 2019 -0400
@@ -29,8 +29,8 @@
import java.net.SocketException;
import java.net.SocketOption;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Set;
-import java.util.stream.Collectors;
/**
* Defines the infrastructure to support extended socket options, beyond those
@@ -45,6 +45,9 @@
public static final short SOCK_DGRAM = 2;
private final Set<SocketOption<?>> options;
+ private final Set<SocketOption<?>> datagramOptions;
+ private final Set<SocketOption<?>> clientStreamOptions;
+ private final Set<SocketOption<?>> serverStreamOptions;
/** Tells whether or not the option is supported. */
public final boolean isOptionSupported(SocketOption<?> option) {
@@ -78,11 +81,11 @@
return getInstance().options0(SOCK_DGRAM, false);
}
- private boolean isDatagramOption(SocketOption<?> option) {
+ private static boolean isDatagramOption(SocketOption<?> option) {
return !option.name().startsWith("TCP_");
}
- private boolean isStreamOption(SocketOption<?> option, boolean server) {
+ private static boolean isStreamOption(SocketOption<?> option, boolean server) {
if (server && "SO_FLOW_SLA".equals(option.name())) {
return false;
} else {
@@ -91,23 +94,19 @@
}
private Set<SocketOption<?>> options0(short type, boolean server) {
- Set<SocketOption<?>> extOptions;
switch (type) {
case SOCK_DGRAM:
- extOptions = options.stream()
- .filter(option -> isDatagramOption(option))
- .collect(Collectors.toUnmodifiableSet());
- break;
+ return datagramOptions;
case SOCK_STREAM:
- extOptions = options.stream()
- .filter(option -> isStreamOption(option, server))
- .collect(Collectors.toUnmodifiableSet());
- break;
+ if (server) {
+ return serverStreamOptions;
+ } else {
+ return clientStreamOptions;
+ }
default:
//this will never happen
throw new IllegalArgumentException("Invalid socket option type");
}
- return extOptions;
}
/** Sets the value of a socket option, for the given socket. */
@@ -120,6 +119,23 @@
protected ExtendedSocketOptions(Set<SocketOption<?>> options) {
this.options = options;
+ var datagramOptions = new HashSet<SocketOption<?>>();
+ var serverStreamOptions = new HashSet<SocketOption<?>>();
+ var clientStreamOptions = new HashSet<SocketOption<?>>();
+ for (var option : options) {
+ if (isDatagramOption(option)) {
+ datagramOptions.add(option);
+ }
+ if (isStreamOption(option, true)) {
+ serverStreamOptions.add(option);
+ }
+ if (isStreamOption(option, false)) {
+ clientStreamOptions.add(option);
+ }
+ }
+ this.datagramOptions = Set.copyOf(datagramOptions);
+ this.serverStreamOptions = Set.copyOf(serverStreamOptions);
+ this.clientStreamOptions = Set.copyOf(clientStreamOptions);
}
private static volatile ExtendedSocketOptions instance;
--- a/src/java.base/share/classes/sun/net/sdp/SdpSupport.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/net/sdp/SdpSupport.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2019, 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
@@ -27,7 +27,6 @@
import java.io.IOException;
import java.io.FileDescriptor;
-import java.security.AccessController;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.JavaIOFileDescriptorAccess;
@@ -75,12 +74,6 @@
private static native void convert0(int fd) throws IOException;
static {
- AccessController.doPrivileged(
- new java.security.PrivilegedAction<Void>() {
- public Void run() {
- System.loadLibrary("net");
- return null;
- }
- });
+ jdk.internal.loader.BootLoader.loadLibrary("net");
}
}
--- a/src/java.base/share/classes/sun/net/spi/DefaultProxySelector.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/net/spi/DefaultProxySelector.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -98,13 +98,7 @@
return NetProperties.getBoolean(key);
}});
if (b != null && b.booleanValue()) {
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction<Void>() {
- public Void run() {
- System.loadLibrary("net");
- return null;
- }
- });
+ jdk.internal.loader.BootLoader.loadLibrary("net");
hasSystemProxies = init();
}
}
--- a/src/java.base/share/classes/sun/net/util/IPAddressUtil.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/net/util/IPAddressUtil.java Thu Jul 25 08:24:01 2019 -0400
@@ -32,9 +32,11 @@
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
+import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@@ -372,4 +374,181 @@
return null;
}
}
+
+ // See java.net.URI for more details on how to generate these
+ // masks.
+ //
+ // square brackets
+ private static final long L_IPV6_DELIMS = 0x0L; // "[]"
+ private static final long H_IPV6_DELIMS = 0x28000000L; // "[]"
+ // RFC 3986 gen-delims
+ private static final long L_GEN_DELIMS = 0x8400800800000000L; // ":/?#[]@"
+ private static final long H_GEN_DELIMS = 0x28000001L; // ":/?#[]@"
+ // These gen-delims can appear in authority
+ private static final long L_AUTH_DELIMS = 0x400000000000000L; // "@[]:"
+ private static final long H_AUTH_DELIMS = 0x28000001L; // "@[]:"
+ // colon is allowed in userinfo
+ private static final long L_COLON = 0x400000000000000L; // ":"
+ private static final long H_COLON = 0x0L; // ":"
+ // slash should be encoded in authority
+ private static final long L_SLASH = 0x800000000000L; // "/"
+ private static final long H_SLASH = 0x0L; // "/"
+ // backslash should always be encoded
+ private static final long L_BACKSLASH = 0x0L; // "\"
+ private static final long H_BACKSLASH = 0x10000000L; // "\"
+ // ASCII chars 0-31 + 127 - various controls + CRLF + TAB
+ private static final long L_NON_PRINTABLE = 0xffffffffL;
+ private static final long H_NON_PRINTABLE = 0x8000000000000000L;
+ // All of the above
+ private static final long L_EXCLUDE = 0x84008008ffffffffL;
+ private static final long H_EXCLUDE = 0x8000000038000001L;
+
+ private static final char[] OTHERS = {
+ 8263,8264,8265,8448,8449,8453,8454,10868,
+ 65109,65110,65119,65131,65283,65295,65306,65311,65312
+ };
+
+ // Tell whether the given character is found by the given mask pair
+ public static boolean match(char c, long lowMask, long highMask) {
+ if (c < 64)
+ return ((1L << c) & lowMask) != 0;
+ if (c < 128)
+ return ((1L << (c - 64)) & highMask) != 0;
+ return false; // other non ASCII characters are not filtered
+ }
+
+ // returns -1 if the string doesn't contain any characters
+ // from the mask, the index of the first such character found
+ // otherwise.
+ public static int scan(String s, long lowMask, long highMask) {
+ int i = -1, len;
+ if (s == null || (len = s.length()) == 0) return -1;
+ boolean match = false;
+ while (++i < len && !(match = match(s.charAt(i), lowMask, highMask)));
+ if (match) return i;
+ return -1;
+ }
+
+ public static int scan(String s, long lowMask, long highMask, char[] others) {
+ int i = -1, len;
+ if (s == null || (len = s.length()) == 0) return -1;
+ boolean match = false;
+ char c, c0 = others[0];
+ while (++i < len && !(match = match((c=s.charAt(i)), lowMask, highMask))) {
+ if (c >= c0 && (Arrays.binarySearch(others, c) > -1)) {
+ match = true; break;
+ }
+ }
+ if (match) return i;
+
+ return -1;
+ }
+
+ private static String describeChar(char c) {
+ if (c < 32 || c == 127) {
+ if (c == '\n') return "LF";
+ if (c == '\r') return "CR";
+ return "control char (code=" + (int)c + ")";
+ }
+ if (c == '\\') return "'\\'";
+ return "'" + c + "'";
+ }
+
+ private static String checkUserInfo(String str) {
+ // colon is permitted in user info
+ int index = scan(str, L_EXCLUDE & ~L_COLON,
+ H_EXCLUDE & ~H_COLON);
+ if (index >= 0) {
+ return "Illegal character found in user-info: "
+ + describeChar(str.charAt(index));
+ }
+ return null;
+ }
+
+ private static String checkHost(String str) {
+ int index;
+ if (str.startsWith("[") && str.endsWith("]")) {
+ str = str.substring(1, str.length() - 1);
+ if (isIPv6LiteralAddress(str)) {
+ index = str.indexOf('%');
+ if (index >= 0) {
+ index = scan(str = str.substring(index),
+ L_NON_PRINTABLE | L_IPV6_DELIMS,
+ H_NON_PRINTABLE | H_IPV6_DELIMS);
+ if (index >= 0) {
+ return "Illegal character found in IPv6 scoped address: "
+ + describeChar(str.charAt(index));
+ }
+ }
+ return null;
+ }
+ return "Unrecognized IPv6 address format";
+ } else {
+ index = scan(str, L_EXCLUDE, H_EXCLUDE);
+ if (index >= 0) {
+ return "Illegal character found in host: "
+ + describeChar(str.charAt(index));
+ }
+ }
+ return null;
+ }
+
+ private static String checkAuth(String str) {
+ int index = scan(str,
+ L_EXCLUDE & ~L_AUTH_DELIMS,
+ H_EXCLUDE & ~H_AUTH_DELIMS);
+ if (index >= 0) {
+ return "Illegal character found in authority: "
+ + describeChar(str.charAt(index));
+ }
+ return null;
+ }
+
+ // check authority of hierarchical URL. Appropriate for
+ // HTTP-like protocol handlers
+ public static String checkAuthority(URL url) {
+ String s, u, h;
+ if (url == null) return null;
+ if ((s = checkUserInfo(u = url.getUserInfo())) != null) {
+ return s;
+ }
+ if ((s = checkHost(h = url.getHost())) != null) {
+ return s;
+ }
+ if (h == null && u == null) {
+ return checkAuth(url.getAuthority());
+ }
+ return null;
+ }
+
+ // minimal syntax checks - deeper check may be performed
+ // by the appropriate protocol handler
+ public static String checkExternalForm(URL url) {
+ String s;
+ if (url == null) return null;
+ int index = scan(s = url.getUserInfo(),
+ L_NON_PRINTABLE | L_SLASH,
+ H_NON_PRINTABLE | H_SLASH);
+ if (index >= 0) {
+ return "Illegal character found in authority: "
+ + describeChar(s.charAt(index));
+ }
+ if ((s = checkHostString(url.getHost())) != null) {
+ return s;
+ }
+ return null;
+ }
+
+ public static String checkHostString(String host) {
+ if (host == null) return null;
+ int index = scan(host,
+ L_NON_PRINTABLE | L_SLASH,
+ H_NON_PRINTABLE | H_SLASH,
+ OTHERS);
+ if (index >= 0) {
+ return "Illegal character found in host: "
+ + describeChar(host.charAt(index));
+ }
+ return null;
+ }
}
--- a/src/java.base/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2019, 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
@@ -36,6 +36,7 @@
import java.io.FilterInputStream;
import java.io.FilterOutputStream;
import java.io.FileNotFoundException;
+import java.net.MalformedURLException;
import java.net.URL;
import java.net.SocketPermission;
import java.net.UnknownHostException;
@@ -48,6 +49,7 @@
import java.security.Permission;
import java.util.Properties;
import sun.net.NetworkClient;
+import sun.net.util.IPAddressUtil;
import sun.net.www.MessageHeader;
import sun.net.www.MeteredStream;
import sun.net.www.URLConnection;
@@ -157,6 +159,21 @@
}
}
+ static URL checkURL(URL u) throws IllegalArgumentException {
+ if (u != null) {
+ if (u.toExternalForm().indexOf('\n') > -1) {
+ Exception mfue = new MalformedURLException("Illegal character in URL");
+ throw new IllegalArgumentException(mfue.getMessage(), mfue);
+ }
+ }
+ String s = IPAddressUtil.checkAuthority(u);
+ if (s != null) {
+ Exception mfue = new MalformedURLException(s);
+ throw new IllegalArgumentException(mfue.getMessage(), mfue);
+ }
+ return u;
+ }
+
/**
* Creates an FtpURLConnection from a URL.
*
@@ -170,7 +187,7 @@
* Same as FtpURLconnection(URL) with a per connection proxy specified
*/
FtpURLConnection(URL url, Proxy p) {
- super(url);
+ super(checkURL(url));
instProxy = p;
host = url.getHost();
port = url.getPort();
--- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2019, 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
@@ -68,6 +68,7 @@
import jdk.internal.access.JavaNetHttpCookieAccess;
import jdk.internal.access.SharedSecrets;
import sun.net.*;
+import sun.net.util.IPAddressUtil;
import sun.net.www.*;
import sun.net.www.http.HttpClient;
import sun.net.www.http.PosterOutputStream;
@@ -868,8 +869,13 @@
throw new MalformedURLException("Illegal character in URL");
}
}
+ String s = IPAddressUtil.checkAuthority(u);
+ if (s != null) {
+ throw new MalformedURLException(s);
+ }
return u;
}
+
protected HttpURLConnection(URL u, Proxy p, Handler handler)
throws IOException {
super(checkURL(u));
--- a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsURLConnectionImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsURLConnectionImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -37,6 +37,7 @@
import java.util.Map;
import java.util.List;
import java.util.Optional;
+import sun.net.util.IPAddressUtil;
import sun.net.www.http.HttpClient;
/**
@@ -69,6 +70,10 @@
throw new MalformedURLException("Illegal character in URL");
}
}
+ String s = IPAddressUtil.checkAuthority(u);
+ if (s != null) {
+ throw new MalformedURLException(s);
+ }
return u;
}
@@ -289,7 +294,7 @@
* @param key the keyword by which the request is known
* (e.g., "<code>accept</code>").
* @param value the value associated with it.
- * @see #getRequestProperties(java.lang.String)
+ * @see #getRequestProperty(java.lang.String)
* @since 1.4
*/
public void addRequestProperty(String key, String value) {
--- a/src/java.base/share/classes/sun/nio/ch/IOUtil.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/nio/ch/IOUtil.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, 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
@@ -432,15 +432,8 @@
public static void load() { }
static {
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction<Void>() {
- public Void run() {
- System.loadLibrary("net");
- System.loadLibrary("nio");
- return null;
- }
- });
-
+ jdk.internal.loader.BootLoader.loadLibrary("net");
+ jdk.internal.loader.BootLoader.loadLibrary("nio");
initIDs();
IOV_MAX = iovMax();
--- a/src/java.base/share/classes/sun/security/ssl/CertStatusExtension.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/CertStatusExtension.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -724,12 +724,14 @@
// Update the context.
chc.handshakeExtensions.put(
SH_STATUS_REQUEST, CertStatusRequestSpec.DEFAULT);
- chc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_STATUS.id,
- SSLHandshake.CERTIFICATE_STATUS);
// Since we've received a legitimate status_request in the
// ServerHello, stapling is active if it's been enabled.
chc.staplingActive = chc.sslContext.isStaplingEnabled(true);
+ if (chc.staplingActive) {
+ chc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_STATUS.id,
+ SSLHandshake.CERTIFICATE_STATUS);
+ }
// No impact on session resumption.
}
@@ -1079,12 +1081,16 @@
// Update the context.
chc.handshakeExtensions.put(
SH_STATUS_REQUEST_V2, CertStatusRequestV2Spec.DEFAULT);
- chc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_STATUS.id,
- SSLHandshake.CERTIFICATE_STATUS);
// Since we've received a legitimate status_request in the
- // ServerHello, stapling is active if it's been enabled.
+ // ServerHello, stapling is active if it's been enabled. If it
+ // is active, make sure we add the CertificateStatus message
+ // consumer.
chc.staplingActive = chc.sslContext.isStaplingEnabled(true);
+ if (chc.staplingActive) {
+ chc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_STATUS.id,
+ SSLHandshake.CERTIFICATE_STATUS);
+ }
// No impact on session resumption.
}
--- a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -330,6 +330,15 @@
// clean up this consumer
chc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE_REQUEST.id);
+ SSLConsumer certStatCons = chc.handshakeConsumers.remove(
+ SSLHandshake.CERTIFICATE_STATUS.id);
+ if (certStatCons != null) {
+ // Stapling was active but no certificate status message
+ // was sent. We need to run the absence handler which will
+ // check the certificate chain.
+ CertificateStatus.handshakeAbsence.absent(context, null);
+ }
+
T10CertificateRequestMessage crm =
new T10CertificateRequestMessage(chc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
@@ -647,6 +656,15 @@
// clean up this consumer
chc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE_REQUEST.id);
+ SSLConsumer certStatCons = chc.handshakeConsumers.remove(
+ SSLHandshake.CERTIFICATE_STATUS.id);
+ if (certStatCons != null) {
+ // Stapling was active but no certificate status message
+ // was sent. We need to run the absence handler which will
+ // check the certificate chain.
+ CertificateStatus.handshakeAbsence.absent(context, null);
+ }
+
T12CertificateRequestMessage crm =
new T12CertificateRequestMessage(chc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
--- a/src/java.base/share/classes/sun/security/ssl/CertificateStatus.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/CertificateStatus.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -287,12 +287,16 @@
}
// Pin the received responses to the SSLSessionImpl. It will
- // be retrieved by the X509TrustManagerImpl during the certficicate
+ // be retrieved by the X509TrustManagerImpl during the certificate
// checking phase.
chc.handshakeSession.setStatusResponses(cst.encodedResponses);
// Now perform the check
T12CertificateConsumer.checkServerCerts(chc, chc.deferredCerts);
+
+ // Update the handshake consumers to remove this message, indicating
+ // that it has been processed.
+ chc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE_STATUS.id);
}
}
--- a/src/java.base/share/classes/sun/security/ssl/Finished.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/Finished.java Thu Jul 25 08:24:01 2019 -0400
@@ -482,7 +482,7 @@
shc.conContext.inputRecord.expectingFinishFlight();
} else {
if (shc.handshakeSession.isRejoinable() &&
- !shc.statelessResumption) {
+ !shc.handshakeSession.isStatelessable(shc)) {
((SSLSessionContextImpl)shc.sslContext.
engineGetServerSessionContext()).put(
shc.handshakeSession);
@@ -847,6 +847,8 @@
shc.conContext.serverVerifyData = fm.verifyData;
}
+ shc.conContext.conSession = shc.handshakeSession.finish();
+
// update the context
shc.handshakeConsumers.put(
SSLHandshake.FINISHED.id, SSLHandshake.FINISHED);
--- a/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java Thu Jul 25 08:24:01 2019 -0400
@@ -45,7 +45,6 @@
*/
final class NewSessionTicket {
static final int MAX_TICKET_LIFETIME = 604800; // seconds, 7 days
-
static final SSLConsumer handshakeConsumer =
new T13NewSessionTicketConsumer();
static final SSLConsumer handshake12Consumer =
@@ -60,7 +59,7 @@
*/
abstract static class NewSessionTicketMessage extends HandshakeMessage {
int ticketLifetime;
- byte[] ticket;
+ byte[] ticket = new byte[0];
NewSessionTicketMessage(HandshakeContext context) {
super(context);
@@ -83,6 +82,9 @@
"TicketNonce not part of RFC 5077.");
}
+ boolean isValid() {
+ return (ticket.length > 0);
+ }
}
/**
* NewSessionTicket for TLS 1.2 and below (RFC 5077)
@@ -102,13 +104,13 @@
// RFC5077 struct {
// uint32 ticket_lifetime;
- // opaque ticket<1..2^16-1>;
+ // opaque ticket<0..2^16-1>;
// } NewSessionTicket;
super(context);
- if (m.remaining() < 14) {
+ if (m.remaining() < 6) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
- "Invalid NewSessionTicket message: no sufficient data");
+ "Invalid NewSessionTicket message: insufficient data");
}
this.ticketLifetime = Record.getInt32(m);
@@ -186,7 +188,7 @@
if (m.remaining() < 14) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
- "Invalid NewSessionTicket message: no sufficient data");
+ "Invalid NewSessionTicket message: insufficient data");
}
this.ticketLifetime = Record.getInt32(m);
@@ -195,18 +197,21 @@
if (m.remaining() < 5) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
- "Invalid NewSessionTicket message: no sufficient data");
+ "Invalid NewSessionTicket message: insufficient ticket" +
+ " data");
}
this.ticket = Record.getBytes16(m);
if (ticket.length == 0) {
- throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+ SSLLogger.fine(
"No ticket in the NewSessionTicket handshake message");
+ }
}
if (m.remaining() < 2) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
- "Invalid NewSessionTicket message: no sufficient data");
+ "Invalid NewSessionTicket message: extra data");
}
SSLExtension[] supportedExtensions =
@@ -310,36 +315,45 @@
@Override
public byte[] produce(ConnectionContext context) throws IOException {
- // The producing happens in server side only.
- ServerHandshakeContext shc = (ServerHandshakeContext)context;
+ HandshakeContext hc = (HandshakeContext)context;
- // Is this session resumable?
- if (!shc.handshakeSession.isRejoinable()) {
- return null;
- }
+ // The producing happens in server side only.
+ if (hc instanceof ServerHandshakeContext) {
+ // Is this session resumable?
+ if (!hc.handshakeSession.isRejoinable()) {
+ return null;
+ }
- // What's the requested PSK key exchange modes?
- //
- // Note that currently, the NewSessionTicket post-handshake is
- // produced and delivered only in the current handshake context
- // if required.
- PskKeyExchangeModesSpec pkemSpec =
- (PskKeyExchangeModesSpec)shc.handshakeExtensions.get(
- SSLExtension.PSK_KEY_EXCHANGE_MODES);
- if (pkemSpec == null || !pkemSpec.contains(
- PskKeyExchangeModesExtension.PskKeyExchangeMode.PSK_DHE_KE)) {
- // Client doesn't support PSK with (EC)DHE key establishment.
- return null;
+ // What's the requested PSK key exchange modes?
+ //
+ // Note that currently, the NewSessionTicket post-handshake is
+ // produced and delivered only in the current handshake context
+ // if required.
+ PskKeyExchangeModesSpec pkemSpec =
+ (PskKeyExchangeModesSpec) hc.handshakeExtensions.get(
+ SSLExtension.PSK_KEY_EXCHANGE_MODES);
+ if (pkemSpec == null || !pkemSpec.contains(
+ PskKeyExchangeModesExtension.PskKeyExchangeMode.PSK_DHE_KE)) {
+ // Client doesn't support PSK with (EC)DHE key establishment.
+ return null;
+ }
+ } else { // PostHandshakeContext
+
+ // Check if we have sent a PSK already, then we know it is using a
+ // allowable PSK exchange key mode
+ if (!hc.handshakeSession.isPSKable()) {
+ return null;
+ }
}
// get a new session ID
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
- shc.sslContext.engineGetServerSessionContext();
+ hc.sslContext.engineGetServerSessionContext();
SessionId newId = new SessionId(true,
- shc.sslContext.getSecureRandom());
+ hc.sslContext.getSecureRandom());
SecretKey resumptionMasterSecret =
- shc.handshakeSession.getResumptionMasterSecret();
+ hc.handshakeSession.getResumptionMasterSecret();
if (resumptionMasterSecret == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
@@ -349,10 +363,10 @@
}
// construct the PSK and handshake message
- BigInteger nonce = shc.handshakeSession.incrTicketNonceCounter();
+ BigInteger nonce = hc.handshakeSession.incrTicketNonceCounter();
byte[] nonceArr = nonce.toByteArray();
SecretKey psk = derivePreSharedKey(
- shc.negotiatedCipherSuite.hashAlg,
+ hc.negotiatedCipherSuite.hashAlg,
resumptionMasterSecret, nonceArr);
int sessionTimeoutSeconds = sessionCache.getSessionTimeout();
@@ -364,31 +378,35 @@
return null;
}
- NewSessionTicketMessage nstm;
+ NewSessionTicketMessage nstm = null;
SSLSessionImpl sessionCopy =
- new SSLSessionImpl(shc.handshakeSession, newId);
+ new SSLSessionImpl(hc.handshakeSession, newId);
sessionCopy.setPreSharedKey(psk);
sessionCopy.setPskIdentity(newId.getId());
- if (shc.statelessResumption) {
- try {
- nstm = new T13NewSessionTicketMessage(shc,
- sessionTimeoutSeconds, shc.sslContext.getSecureRandom(),
- nonceArr, new SessionTicketSpec().encrypt(shc, sessionCopy));
+ // If a stateless ticket is allowed, attempt to make one
+ if (hc.handshakeSession.isStatelessable(hc)) {
+ nstm = new T13NewSessionTicketMessage(hc,
+ sessionTimeoutSeconds,
+ hc.sslContext.getSecureRandom(),
+ nonceArr,
+ new SessionTicketSpec().encrypt(hc, sessionCopy));
+ // If ticket construction failed, switch to session cache
+ if (!nstm.isValid()) {
+ hc.statelessResumption = false;
+ } else {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced NewSessionTicket stateless " +
"handshake message", nstm);
}
- } catch (Exception e) {
- // Error with NST ticket, abort NST
- shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, e);
- return null;
}
- } else {
- nstm = new T13NewSessionTicketMessage(shc, sessionTimeoutSeconds,
- shc.sslContext.getSecureRandom(), nonceArr,
+ }
+ // If a session cache ticket is being used, make one
+ if (!hc.handshakeSession.isStatelessable(hc)) {
+ nstm = new T13NewSessionTicketMessage(hc, sessionTimeoutSeconds,
+ hc.sslContext.getSecureRandom(), nonceArr,
newId.getId());
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
@@ -399,13 +417,21 @@
// create and cache the new session
// The new session must be a child of the existing session so
// they will be invalidated together, etc.
- shc.handshakeSession.addChild(sessionCopy);
+ hc.handshakeSession.addChild(sessionCopy);
sessionCopy.setTicketAgeAdd(nstm.getTicketAgeAdd());
sessionCache.put(sessionCopy);
}
+
// Output the handshake message.
- nstm.write(shc.handshakeOutput);
- shc.handshakeOutput.flush();
+ if (nstm != null) {
+ // should never be null
+ nstm.write(hc.handshakeOutput);
+ hc.handshakeOutput.flush();
+ }
+
+ if (hc instanceof PostHandshakeContext) {
+ ((PostHandshakeContext) hc).finish();
+ }
// The message has been delivered.
return null;
@@ -448,23 +474,16 @@
return null;
}
- NewSessionTicketMessage nstm;
-
SSLSessionImpl sessionCopy =
new SSLSessionImpl(shc.handshakeSession, newId);
sessionCopy.setPskIdentity(newId.getId());
- try {
- nstm = new T12NewSessionTicketMessage(shc, sessionTimeoutSeconds,
- new SessionTicketSpec().encrypt(shc, sessionCopy));
- if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
- SSLLogger.fine(
- "Produced NewSessionTicket stateless handshake message", nstm);
- }
- } catch (Exception e) {
- // Abort on error with NST ticket
- shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, e);
- return null;
+ NewSessionTicketMessage nstm = new T12NewSessionTicketMessage(shc,
+ sessionTimeoutSeconds,
+ new SessionTicketSpec().encrypt(shc, sessionCopy));
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+ SSLLogger.fine(
+ "Produced NewSessionTicket stateless handshake message", nstm);
}
// Output the handshake message.
@@ -505,6 +524,9 @@
"Consuming NewSessionTicket message", nstm);
}
+ SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
+ hc.sslContext.engineGetClientSessionContext();
+
// discard tickets with timeout 0
if (nstm.ticketLifetime <= 0 ||
nstm.ticketLifetime > MAX_TICKET_LIFETIME) {
@@ -513,12 +535,10 @@
"Discarding NewSessionTicket with lifetime "
+ nstm.ticketLifetime, nstm);
}
+ sessionCache.remove(hc.handshakeSession.getSessionId());
return;
}
- SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
- hc.sslContext.engineGetClientSessionContext();
-
if (sessionCache.getSessionTimeout() > MAX_TICKET_LIFETIME) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
--- a/src/java.base/share/classes/sun/security/ssl/PostHandshakeContext.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/PostHandshakeContext.java Thu Jul 25 08:24:01 2019 -0400
@@ -54,6 +54,7 @@
handshakeConsumers = new LinkedHashMap<>(consumers);
handshakeFinished = true;
+ handshakeSession = context.conSession;
}
@Override
@@ -82,4 +83,9 @@
SSLHandshake.nameOf(handshakeType), be);
}
}
+
+ // Finish this PostHandshake event
+ void finish() {
+ handshakeSession = null;
+ }
}
--- a/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -1480,8 +1480,9 @@
checkAdditionalTrust(chain, authType, engine, false);
}
- private void checkAdditionalTrust(X509Certificate[] chain, String authType,
- Socket socket, boolean isClient) throws CertificateException {
+ private void checkAdditionalTrust(X509Certificate[] chain,
+ String authType, Socket socket,
+ boolean checkClientTrusted) throws CertificateException {
if (socket != null && socket.isConnected() &&
socket instanceof SSLSocket) {
@@ -1495,9 +1496,8 @@
String identityAlg = sslSocket.getSSLParameters().
getEndpointIdentificationAlgorithm();
if (identityAlg != null && !identityAlg.isEmpty()) {
- String hostname = session.getPeerHost();
- X509TrustManagerImpl.checkIdentity(
- hostname, chain[0], identityAlg);
+ X509TrustManagerImpl.checkIdentity(session, chain,
+ identityAlg, checkClientTrusted);
}
// try the best to check the algorithm constraints
@@ -1519,12 +1519,13 @@
constraints = new SSLAlgorithmConstraints(sslSocket, true);
}
- checkAlgorithmConstraints(chain, constraints, isClient);
+ checkAlgorithmConstraints(chain, constraints, checkClientTrusted);
}
}
- private void checkAdditionalTrust(X509Certificate[] chain, String authType,
- SSLEngine engine, boolean isClient) throws CertificateException {
+ private void checkAdditionalTrust(X509Certificate[] chain,
+ String authType, SSLEngine engine,
+ boolean checkClientTrusted) throws CertificateException {
if (engine != null) {
SSLSession session = engine.getHandshakeSession();
if (session == null) {
@@ -1535,9 +1536,8 @@
String identityAlg = engine.getSSLParameters().
getEndpointIdentificationAlgorithm();
if (identityAlg != null && !identityAlg.isEmpty()) {
- String hostname = session.getPeerHost();
- X509TrustManagerImpl.checkIdentity(
- hostname, chain[0], identityAlg);
+ X509TrustManagerImpl.checkIdentity(session, chain,
+ identityAlg, checkClientTrusted);
}
// try the best to check the algorithm constraints
@@ -1559,13 +1559,13 @@
constraints = new SSLAlgorithmConstraints(engine, true);
}
- checkAlgorithmConstraints(chain, constraints, isClient);
+ checkAlgorithmConstraints(chain, constraints, checkClientTrusted);
}
}
private void checkAlgorithmConstraints(X509Certificate[] chain,
AlgorithmConstraints constraints,
- boolean isClient) throws CertificateException {
+ boolean checkClientTrusted) throws CertificateException {
try {
// Does the certificate chain end with a trusted certificate?
int checkedLength = chain.length - 1;
@@ -1584,7 +1584,7 @@
if (checkedLength >= 0) {
AlgorithmChecker checker =
new AlgorithmChecker(constraints, null,
- (isClient ? Validator.VAR_TLS_CLIENT :
+ (checkClientTrusted ? Validator.VAR_TLS_CLIENT :
Validator.VAR_TLS_SERVER));
checker.init(false);
for (int i = checkedLength; i >= 0; i--) {
--- a/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -344,6 +344,12 @@
hsStatus = tryKeyUpdate(hsStatus);
}
+ // Check if NewSessionTicket PostHandshake message needs to be sent
+ if (conContext.conSession.updateNST &&
+ !conContext.sslConfig.isClientMode) {
+ hsStatus = tryNewSessionTicket(hsStatus);
+ }
+
// update context status
ciphertext.handshakeStatus = hsStatus;
@@ -397,6 +403,29 @@
return currentHandshakeStatus;
}
+ // Try to generate a PostHandshake NewSessionTicket message. This is
+ // TLS 1.3 only.
+ private HandshakeStatus tryNewSessionTicket(
+ HandshakeStatus currentHandshakeStatus) throws IOException {
+ // Don't bother to kickstart if handshaking is in progress, or if the
+ // connection is not duplex-open.
+ if ((conContext.handshakeContext == null) &&
+ conContext.protocolVersion.useTLS13PlusSpec() &&
+ !conContext.isOutboundClosed() &&
+ !conContext.isInboundClosed() &&
+ !conContext.isBroken) {
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+ SSLLogger.finest("trigger NST");
+ }
+ conContext.conSession.updateNST = false;
+ NewSessionTicket.kickstartProducer.produce(
+ new PostHandshakeContext(conContext));
+ return conContext.getHandshakeStatus();
+ }
+
+ return currentHandshakeStatus;
+ }
+
private static void checkParams(
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
ByteBuffer[] dsts, int dstsOffset, int dstsLength) {
--- a/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -69,7 +69,7 @@
private int cacheLimit; // the max cache size
private int timeout; // timeout in seconds
- // Does this context support stateless session (RFC 5077)
+ // Default setting for stateless session resumption support (RFC 5077)
private boolean statelessSession = true;
// package private
@@ -239,6 +239,7 @@
st = GetPropertyAction.privilegedGetProperty(
"jdk.tls.client.enableSessionTicketExtension", "true");
}
+
if (st.compareToIgnoreCase("false") == 0) {
statelessSession = false;
}
--- a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -27,6 +27,7 @@
import sun.security.x509.X509CertImpl;
import java.io.IOException;
+import java.lang.reflect.Array;
import java.math.BigInteger;
import java.net.InetAddress;
import java.nio.ByteBuffer;
@@ -35,6 +36,7 @@
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Queue;
import java.util.Collection;
import java.util.Collections;
@@ -104,7 +106,7 @@
private X509Certificate[] localCerts;
private PrivateKey localPrivateKey;
private final Collection<SignatureScheme> localSupportedSignAlgs;
- private String[] peerSupportedSignAlgs; // for certificate
+ private Collection<SignatureScheme> peerSupportedSignAlgs; //for certificate
private boolean useDefaultPeerSignAlgs = false;
private List<byte[]> statusResponses;
private SecretKey resumptionMasterSecret;
@@ -236,7 +238,8 @@
baseSession.localSupportedSignAlgs == null ?
Collections.emptySet() : baseSession.localSupportedSignAlgs;
this.peerSupportedSignAlgs =
- baseSession.getPeerSupportedSignatureAlgorithms();
+ baseSession.peerSupportedSignAlgs == null ?
+ Collections.emptySet() : baseSession.peerSupportedSignAlgs;
this.serverNameIndication = baseSession.serverNameIndication;
this.requestedServerNames = baseSession.getRequestedServerNames();
this.masterSecret = baseSession.getMasterSecret();
@@ -261,8 +264,10 @@
/**
* < 2 bytes > protocolVersion
* < 2 bytes > cipherSuite
- * < 2 bytes > localSupportedSignAlgs entries
+ * < 1 byte > localSupportedSignAlgs entries
* < 2 bytes per entries > localSupportedSignAlgs
+ * < 1 bytes > peerSupportedSignAlgs entries
+ * < 2 bytes per entries > peerSupportedSignAlgs
* < 2 bytes > preSharedKey length
* < length in bytes > preSharedKey
* < 1 byte > pskIdentity length
@@ -281,6 +286,9 @@
* < 1 byte > ServerName length
* < length in bytes > ServerName
* < 4 bytes > creationTime
+ * < 2 byte > status response length
+ * < 2 byte > status response entry length
+ * < length in byte > status response entry
* < 1 byte > Length of peer host
* < length in bytes > peer host
* < 2 bytes> peer port
@@ -302,17 +310,17 @@
* < length in bytes> PSK identity
* Anonymous
* < 1 byte >
+ * < 4 bytes > maximumPacketSize
+ * < 4 bytes > negotiatedMaxFragSize
*/
SSLSessionImpl(HandshakeContext hc, ByteBuffer buf) throws IOException {
int i = 0;
byte[] b;
- this.localSupportedSignAlgs = new ArrayList<>();
-
- boundValues = null;
-
- this.protocolVersion = ProtocolVersion.valueOf(Short.toUnsignedInt(buf.getShort()));
+ boundValues = new ConcurrentHashMap<>();
+ this.protocolVersion =
+ ProtocolVersion.valueOf(Short.toUnsignedInt(buf.getShort()));
if (protocolVersion.useTLS13PlusSpec()) {
this.sessionId = new SessionId(false, null);
@@ -322,14 +330,26 @@
hc.sslContext.getSecureRandom());
}
- this.cipherSuite = CipherSuite.valueOf(Short.toUnsignedInt(buf.getShort()));
+ this.cipherSuite =
+ CipherSuite.valueOf(Short.toUnsignedInt(buf.getShort()));
// Local Supported signature algorithms
- i = Short.toUnsignedInt(buf.getShort());
+ ArrayList<SignatureScheme> list = new ArrayList<>();
+ i = Byte.toUnsignedInt(buf.get());
while (i-- > 0) {
- this.localSupportedSignAlgs.add(SignatureScheme.valueOf(
+ list.add(SignatureScheme.valueOf(
Short.toUnsignedInt(buf.getShort())));
}
+ this.localSupportedSignAlgs = Collections.unmodifiableCollection(list);
+
+ // Peer Supported signature algorithms
+ i = Byte.toUnsignedInt(buf.get());
+ list.clear();
+ while (i-- > 0) {
+ list.add(SignatureScheme.valueOf(
+ Short.toUnsignedInt(buf.getShort())));
+ }
+ this.peerSupportedSignAlgs = Collections.unmodifiableCollection(list);
// PSK
i = Short.toUnsignedInt(buf.getShort());
@@ -410,9 +430,27 @@
}
}
+ maximumPacketSize = buf.getInt();
+ negotiatedMaxFragLen = buf.getInt();
+
// Get creation time
this.creationTime = buf.getLong();
+ // Get Buffer sizes
+
+ // Status Response
+ len = Short.toUnsignedInt(buf.getShort());
+ if (len == 0) {
+ statusResponses = Collections.emptyList();
+ } else {
+ statusResponses = new ArrayList<>();
+ }
+ while (len-- > 0) {
+ b = new byte[Short.toUnsignedInt(buf.getShort())];
+ buf.get(b);
+ statusResponses.add(b);
+ }
+
// Get Peer host & port
i = Byte.toUnsignedInt(buf.get());
if (i == 0) {
@@ -484,6 +522,33 @@
context = (SSLSessionContextImpl)
hc.sslContext.engineGetServerSessionContext();
+ this.lastUsedTime = System.currentTimeMillis();
+ }
+
+ // Some situations we cannot provide a stateless ticket, but after it
+ // has been negotiated
+ boolean isStatelessable(HandshakeContext hc) {
+ if (!hc.statelessResumption) {
+ return false;
+ }
+
+ // If there is no getMasterSecret with TLS1.2 or under, do not resume.
+ if (!protocolVersion.useTLS13PlusSpec() &&
+ getMasterSecret().getEncoded() == null) {
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+ SSLLogger.finest("No MasterSecret, cannot make stateless" +
+ " ticket");
+ }
+ return false;
+ }
+ if (boundValues != null && boundValues.size() > 0) {
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+ SSLLogger.finest("There are boundValues, cannot make" +
+ " stateless ticket");
+ }
+ return false;
+ }
+ return true;
}
/**
@@ -497,11 +562,14 @@
hos.putInt16(cipherSuite.id);
// Local Supported signature algorithms
- int l = localSupportedSignAlgs.size();
- hos.putInt16(l);
- SignatureScheme[] sig = new SignatureScheme[l];
- localSupportedSignAlgs.toArray(sig);
- for (SignatureScheme s : sig) {
+ hos.putInt8(localSupportedSignAlgs.size());
+ for (SignatureScheme s : localSupportedSignAlgs) {
+ hos.putInt16(s.id);
+ }
+
+ // Peer Supported signature algorithms
+ hos.putInt8(peerSupportedSignAlgs.size());
+ for (SignatureScheme s : peerSupportedSignAlgs) {
hos.putInt16(s.id);
}
@@ -564,16 +632,30 @@
// List of SNIServerName
hos.putInt16(requestedServerNames.size());
if (requestedServerNames.size() > 0) {
- for (SNIServerName host: requestedServerNames) {
+ for (SNIServerName host : requestedServerNames) {
b = host.getEncoded();
hos.putInt8(b.length);
hos.write(b, 0, b.length);
}
}
+ // Buffer sizes
+ hos.putInt32(maximumPacketSize);
+ hos.putInt32(negotiatedMaxFragLen);
+
+ // creation time
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
hos.writeBytes(buffer.putLong(creationTime).array());
+ // Status Responses
+ List<byte[]> list = getStatusResponses();
+ int l = list.size();
+ hos.putInt16(l);
+ for (byte[] e : list) {
+ hos.putInt16(e.length);
+ hos.write(e);
+ }
+
// peer Host & Port
if (host == null || host.length() == 0) {
hos.putInt8(0);
@@ -649,10 +731,14 @@
BigInteger incrTicketNonceCounter() {
BigInteger result = ticketNonceCounter;
- ticketNonceCounter = ticketNonceCounter.add(BigInteger.valueOf(1));
+ ticketNonceCounter = ticketNonceCounter.add(BigInteger.ONE);
return result;
}
+ boolean isPSKable() {
+ return (ticketNonceCounter.compareTo(BigInteger.ZERO) > 0);
+ }
+
/**
* Returns the master secret ... treat with extreme caution!
*/
@@ -725,8 +811,7 @@
void setPeerSupportedSignatureAlgorithms(
Collection<SignatureScheme> signatureSchemes) {
- peerSupportedSignAlgs =
- SignatureScheme.getAlgorithmNames(signatureSchemes);
+ peerSupportedSignAlgs = signatureSchemes;
}
// TLS 1.2 only
@@ -740,16 +825,20 @@
// certificates and server key exchange), it MUST send the
// signature_algorithms extension, listing the algorithms it
// is willing to accept.
+ private static final ArrayList<SignatureScheme> defaultPeerSupportedSignAlgs =
+ new ArrayList<>(Arrays.asList(SignatureScheme.RSA_PKCS1_SHA1,
+ SignatureScheme.DSA_SHA1,
+ SignatureScheme.ECDSA_SHA1));
+
void setUseDefaultPeerSignAlgs() {
useDefaultPeerSignAlgs = true;
- peerSupportedSignAlgs = new String[] {
- "SHA1withRSA", "SHA1withDSA", "SHA1withECDSA"};
+ peerSupportedSignAlgs = defaultPeerSupportedSignAlgs;
}
// Returns the connection session.
SSLSessionImpl finish() {
if (useDefaultPeerSignAlgs) {
- this.peerSupportedSignAlgs = new String[0];
+ peerSupportedSignAlgs = Collections.emptySet();
}
return this;
@@ -1212,6 +1301,7 @@
* sessions can be shared across different protection domains.
*/
private final ConcurrentHashMap<SecureKey, Object> boundValues;
+ boolean updateNST;
/**
* Assigns a session value. Session change events are given if
@@ -1238,6 +1328,9 @@
e = new SSLSessionBindingEvent(this, key);
((SSLSessionBindingListener)value).valueBound(e);
}
+ if (protocolVersion.useTLS13PlusSpec()) {
+ updateNST = true;
+ }
}
/**
@@ -1273,6 +1366,9 @@
e = new SSLSessionBindingEvent(this, key);
((SSLSessionBindingListener)value).valueUnbound(e);
}
+ if (protocolVersion.useTLS13PlusSpec()) {
+ updateNST = true;
+ }
}
@@ -1474,11 +1570,7 @@
*/
@Override
public String[] getPeerSupportedSignatureAlgorithms() {
- if (peerSupportedSignAlgs != null) {
- return peerSupportedSignAlgs.clone();
- }
-
- return new String[0];
+ return SignatureScheme.getAlgorithmNames(peerSupportedSignAlgs);
}
/**
--- a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -1264,6 +1264,11 @@
conContext.outputRecord.writeCipher.atKeyLimit()) {
tryKeyUpdate();
}
+ // Check if NewSessionTicket PostHandshake message needs to be sent
+ if (conContext.conSession.updateNST) {
+ conContext.conSession.updateNST = false;
+ tryNewSessionTicket();
+ }
}
@Override
@@ -1499,6 +1504,25 @@
}
}
+ // Try to generate a PostHandshake NewSessionTicket message. This is
+ // TLS 1.3 only.
+ private void tryNewSessionTicket() throws IOException {
+ // Don't bother to kickstart if handshaking is in progress, or if the
+ // connection is not duplex-open.
+ if (!conContext.sslConfig.isClientMode &&
+ conContext.protocolVersion.useTLS13PlusSpec() &&
+ conContext.handshakeContext == null &&
+ !conContext.isOutboundClosed() &&
+ !conContext.isInboundClosed() &&
+ !conContext.isBroken) {
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+ SSLLogger.finest("trigger new session ticket");
+ }
+ NewSessionTicket.kickstartProducer.produce(
+ new PostHandshakeContext(conContext));
+ }
+ }
+
/**
* Initialize the handshaker and socket streams.
*
--- a/src/java.base/share/classes/sun/security/ssl/ServerHello.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java Thu Jul 25 08:24:01 2019 -0400
@@ -1155,14 +1155,14 @@
chc, chc.resumingSession.getMasterSecret());
}
- chc.conContext.consumers.putIfAbsent(
- ContentType.CHANGE_CIPHER_SPEC.id,
- ChangeCipherSpec.t10Consumer);
if (chc.statelessResumption) {
chc.handshakeConsumers.putIfAbsent(
SSLHandshake.NEW_SESSION_TICKET.id,
SSLHandshake.NEW_SESSION_TICKET);
}
+ chc.conContext.consumers.putIfAbsent(
+ ContentType.CHANGE_CIPHER_SPEC.id,
+ ChangeCipherSpec.t10Consumer);
chc.handshakeConsumers.put(
SSLHandshake.FINISHED.id,
SSLHandshake.FINISHED);
--- a/src/java.base/share/classes/sun/security/ssl/ServerHelloDone.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/ServerHelloDone.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -133,6 +133,15 @@
// The consuming happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
+ SSLConsumer certStatCons = chc.handshakeConsumers.remove(
+ SSLHandshake.CERTIFICATE_STATUS.id);
+ if (certStatCons != null) {
+ // Stapling was active but no certificate status message
+ // was sent. We need to run the absence handler which will
+ // check the certificate chain.
+ CertificateStatus.handshakeAbsence.absent(context, null);
+ }
+
// clean up this consumer
chc.handshakeConsumers.clear();
--- a/src/java.base/share/classes/sun/security/ssl/ServerKeyExchange.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/ServerKeyExchange.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -92,6 +92,15 @@
// clean up this consumer
chc.handshakeConsumers.remove(SSLHandshake.SERVER_KEY_EXCHANGE.id);
+ SSLConsumer certStatCons = chc.handshakeConsumers.remove(
+ SSLHandshake.CERTIFICATE_STATUS.id);
+ if (certStatCons != null) {
+ // Stapling was active but no certificate status message
+ // was sent. We need to run the absence handler which will
+ // check the certificate chain.
+ CertificateStatus.handshakeAbsence.absent(context, null);
+ }
+
SSLKeyExchange ke = SSLKeyExchange.valueOf(
chc.negotiatedCipherSuite.keyExchange,
chc.negotiatedProtocol);
--- a/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java Thu Jul 25 08:24:01 2019 -0400
@@ -46,7 +46,6 @@
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.MessageFormat;
-import java.util.Collection;
import java.util.Locale;
/**
@@ -255,13 +254,17 @@
data = buf;
}
- public byte[] encrypt(HandshakeContext hc, SSLSessionImpl session)
- throws IOException {
+ public byte[] encrypt(HandshakeContext hc, SSLSessionImpl session) {
byte[] encrypted;
- StatelessKey key = KeyState.getCurrentKey(hc);
- byte[] iv = new byte[16];
+
+ if (!hc.handshakeSession.isStatelessable(hc)) {
+ return new byte[0];
+ }
try {
+ StatelessKey key = KeyState.getCurrentKey(hc);
+ byte[] iv = new byte[16];
+
SecureRandom random = hc.sslContext.getSecureRandom();
random.nextBytes(iv);
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
@@ -273,8 +276,11 @@
(byte)(key.num >>> 8),
(byte)(key.num)}
);
- encrypted = c.doFinal(session.write());
-
+ byte[] data = session.write();
+ if (data.length == 0) {
+ return data;
+ }
+ encrypted = c.doFinal(data);
byte[] result = new byte[encrypted.length + Integer.BYTES +
iv.length];
result[0] = (byte)(key.num >>> 24);
@@ -286,7 +292,10 @@
Integer.BYTES + iv.length, encrypted.length);
return result;
} catch (Exception e) {
- throw hc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, e);
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+ SSLLogger.fine("Encryption failed." + e);
+ }
+ return new byte[0];
}
}
@@ -311,11 +320,7 @@
(byte)(keyID >>> 8),
(byte)(keyID)}
);
- /*
- return ByteBuffer.wrap(c.doFinal(data,
- Integer.BYTES + iv.length,
- data.length - (Integer.BYTES + iv.length)));
- */
+
ByteBuffer out;
out = ByteBuffer.allocate(data.remaining() - GCM_TAG_LEN / 8);
c.doFinal(data, out);
--- a/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -145,7 +145,7 @@
}
private Validator checkTrustedInit(X509Certificate[] chain,
- String authType, boolean isClient) {
+ String authType, boolean checkClientTrusted) {
if (chain == null || chain.length == 0) {
throw new IllegalArgumentException(
"null or zero-length certificate chain");
@@ -157,7 +157,7 @@
}
Validator v = null;
- if (isClient) {
+ if (checkClientTrusted) {
v = clientValidator;
if (v == null) {
validatorLock.lock();
@@ -192,9 +192,10 @@
return v;
}
- private void checkTrusted(X509Certificate[] chain, String authType,
- Socket socket, boolean isClient) throws CertificateException {
- Validator v = checkTrustedInit(chain, authType, isClient);
+ private void checkTrusted(X509Certificate[] chain,
+ String authType, Socket socket,
+ boolean checkClientTrusted) throws CertificateException {
+ Validator v = checkTrustedInit(chain, authType, checkClientTrusted);
X509Certificate[] trustedChain = null;
if ((socket != null) && socket.isConnected() &&
@@ -223,28 +224,23 @@
// Grab any stapled OCSP responses for use in validation
List<byte[]> responseList = Collections.emptyList();
- if (!isClient && isExtSession) {
+ if (!checkClientTrusted && isExtSession) {
responseList =
((ExtendedSSLSession)session).getStatusResponses();
}
trustedChain = v.validate(chain, null, responseList,
- constraints, isClient ? null : authType);
-
- // check if EE certificate chains to a public root CA (as
- // pre-installed in cacerts)
- boolean chainsToPublicCA = AnchorCertificates.contains(
- trustedChain[trustedChain.length-1]);
+ constraints, checkClientTrusted ? null : authType);
// check endpoint identity
String identityAlg = sslSocket.getSSLParameters().
getEndpointIdentificationAlgorithm();
if (identityAlg != null && !identityAlg.isEmpty()) {
- checkIdentity(session, trustedChain[0], identityAlg, isClient,
- getRequestedServerNames(socket), chainsToPublicCA);
+ checkIdentity(session,
+ trustedChain, identityAlg, checkClientTrusted);
}
} else {
trustedChain = v.validate(chain, null, Collections.emptyList(),
- null, isClient ? null : authType);
+ null, checkClientTrusted ? null : authType);
}
if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) {
@@ -253,9 +249,10 @@
}
}
- private void checkTrusted(X509Certificate[] chain, String authType,
- SSLEngine engine, boolean isClient) throws CertificateException {
- Validator v = checkTrustedInit(chain, authType, isClient);
+ private void checkTrusted(X509Certificate[] chain,
+ String authType, SSLEngine engine,
+ boolean checkClientTrusted) throws CertificateException {
+ Validator v = checkTrustedInit(chain, authType, checkClientTrusted);
X509Certificate[] trustedChain = null;
if (engine != null) {
@@ -281,28 +278,23 @@
// Grab any stapled OCSP responses for use in validation
List<byte[]> responseList = Collections.emptyList();
- if (!isClient && isExtSession) {
+ if (!checkClientTrusted && isExtSession) {
responseList =
((ExtendedSSLSession)session).getStatusResponses();
}
trustedChain = v.validate(chain, null, responseList,
- constraints, isClient ? null : authType);
-
- // check if EE certificate chains to a public root CA (as
- // pre-installed in cacerts)
- boolean chainsToPublicCA = AnchorCertificates.contains(
- trustedChain[trustedChain.length-1]);
+ constraints, checkClientTrusted ? null : authType);
// check endpoint identity
String identityAlg = engine.getSSLParameters().
getEndpointIdentificationAlgorithm();
if (identityAlg != null && !identityAlg.isEmpty()) {
- checkIdentity(session, trustedChain[0], identityAlg, isClient,
- getRequestedServerNames(engine), chainsToPublicCA);
+ checkIdentity(session, trustedChain,
+ identityAlg, checkClientTrusted);
}
} else {
trustedChain = v.validate(chain, null, Collections.emptyList(),
- null, isClient ? null : authType);
+ null, checkClientTrusted ? null : authType);
}
if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) {
@@ -360,14 +352,8 @@
static List<SNIServerName> getRequestedServerNames(Socket socket) {
if (socket != null && socket.isConnected() &&
socket instanceof SSLSocket) {
-
- SSLSocket sslSocket = (SSLSocket)socket;
- SSLSession session = sslSocket.getHandshakeSession();
-
- if (session != null && (session instanceof ExtendedSSLSession)) {
- ExtendedSSLSession extSession = (ExtendedSSLSession)session;
- return extSession.getRequestedServerNames();
- }
+ return getRequestedServerNames(
+ ((SSLSocket)socket).getHandshakeSession());
}
return Collections.<SNIServerName>emptyList();
@@ -376,12 +362,16 @@
// Also used by X509KeyManagerImpl
static List<SNIServerName> getRequestedServerNames(SSLEngine engine) {
if (engine != null) {
- SSLSession session = engine.getHandshakeSession();
+ return getRequestedServerNames(engine.getHandshakeSession());
+ }
- if (session != null && (session instanceof ExtendedSSLSession)) {
- ExtendedSSLSession extSession = (ExtendedSSLSession)session;
- return extSession.getRequestedServerNames();
- }
+ return Collections.<SNIServerName>emptyList();
+ }
+
+ private static List<SNIServerName> getRequestedServerNames(
+ SSLSession session) {
+ if (session != null && (session instanceof ExtendedSSLSession)) {
+ return ((ExtendedSSLSession)session).getRequestedServerNames();
}
return Collections.<SNIServerName>emptyList();
@@ -402,23 +392,28 @@
* the identity checking aginst the server_name extension if present, and
* may failove to peer host checking.
*/
- private static void checkIdentity(SSLSession session,
- X509Certificate cert,
+ static void checkIdentity(SSLSession session,
+ X509Certificate[] trustedChain,
String algorithm,
- boolean isClient,
- List<SNIServerName> sniNames,
- boolean chainsToPublicCA) throws CertificateException {
+ boolean checkClientTrusted) throws CertificateException {
+
+ // check if EE certificate chains to a public root CA (as
+ // pre-installed in cacerts)
+ boolean chainsToPublicCA = AnchorCertificates.contains(
+ trustedChain[trustedChain.length - 1]);
boolean identifiable = false;
String peerHost = session.getPeerHost();
- if (isClient) {
- String hostname = getHostNameInSNI(sniNames);
- if (hostname != null) {
+ if (!checkClientTrusted) {
+ List<SNIServerName> sniNames = getRequestedServerNames(session);
+ String sniHostName = getHostNameInSNI(sniNames);
+ if (sniHostName != null) {
try {
- checkIdentity(hostname, cert, algorithm, chainsToPublicCA);
+ checkIdentity(sniHostName,
+ trustedChain[0], algorithm, chainsToPublicCA);
identifiable = true;
} catch (CertificateException ce) {
- if (hostname.equalsIgnoreCase(peerHost)) {
+ if (sniHostName.equalsIgnoreCase(peerHost)) {
throw ce;
}
@@ -428,7 +423,8 @@
}
if (!identifiable) {
- checkIdentity(peerHost, cert, algorithm, chainsToPublicCA);
+ checkIdentity(peerHost,
+ trustedChain[0], algorithm, chainsToPublicCA);
}
}
--- a/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java Thu Jul 25 08:24:01 2019 -0400
@@ -92,8 +92,6 @@
* add the current position to the <code>eocList</code> vector.
*/
private void parseTag() throws IOException {
- if (dataPos == dataSize)
- return;
if (isEOC(data[dataPos]) && (data[dataPos + 1] == 0)) {
int numOfEncapsulatedLenBytes = 0;
Object elem = null;
@@ -332,6 +330,10 @@
// parse and set up the vectors of all the indefinite-lengths
while (dataPos < dataSize) {
+ if (dataPos + 2 > dataSize) {
+ // There should be at least one tag and one length
+ return null;
+ }
parseTag();
len = parseLength();
if (len < 0) {
--- a/src/java.base/share/classes/sun/security/util/HostnameChecker.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/util/HostnameChecker.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2019, 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
@@ -260,28 +260,35 @@
* The matching is performed as per RFC 2818 rules for TLS and
* RFC 2830 rules for LDAP.<p>
*
- * The <code>name</code> parameter should represent a DNS name.
- * The <code>template</code> parameter
- * may contain the wildcard character *
+ * The <code>name</code> parameter should represent a DNS name. The
+ * <code>template</code> parameter may contain the wildcard character '*'.
*/
private boolean isMatched(String name, String template,
boolean chainsToPublicCA) {
// Normalize to Unicode, because PSL is in Unicode.
- name = IDN.toUnicode(IDN.toASCII(name));
- template = IDN.toUnicode(IDN.toASCII(template));
+ try {
+ name = IDN.toUnicode(IDN.toASCII(name));
+ template = IDN.toUnicode(IDN.toASCII(template));
+ } catch (RuntimeException re) {
+ if (SSLLogger.isOn) {
+ SSLLogger.fine("Failed to normalize to Unicode: " + re);
+ }
- if (hasIllegalWildcard(name, template, chainsToPublicCA)) {
+ return false;
+ }
+
+ if (hasIllegalWildcard(template, chainsToPublicCA)) {
return false;
}
// check the validity of the domain name template.
try {
- // Replacing wildcard character '*' with 'x' so as to check
+ // Replacing wildcard character '*' with 'z' so as to check
// the domain name template validity.
//
// Using the checking implemented in SNIHostName
- new SNIHostName(template.replace('*', 'x'));
+ new SNIHostName(template.replace('*', 'z'));
} catch (IllegalArgumentException iae) {
// It would be nice to add debug log if not matching.
return false;
@@ -299,8 +306,8 @@
/**
* Returns true if the template contains an illegal wildcard character.
*/
- private static boolean hasIllegalWildcard(String domain, String template,
- boolean chainsToPublicCA) {
+ private static boolean hasIllegalWildcard(
+ String template, boolean chainsToPublicCA) {
// not ok if it is a single wildcard character or "*."
if (template.equals("*") || template.equals("*.")) {
if (SSLLogger.isOn) {
@@ -331,25 +338,29 @@
return true;
}
- // If the wildcarded domain is a top-level domain under which names
- // can be registered, then a wildcard is not allowed.
-
if (!chainsToPublicCA) {
return false; // skip check for non-public certificates
}
- Optional<RegisteredDomain> rd = RegisteredDomain.from(domain)
- .filter(d -> d.type() == RegisteredDomain.Type.ICANN);
- if (rd.isPresent()) {
- String wDomain = afterWildcard.substring(firstDotIndex + 1);
- if (rd.get().publicSuffix().equalsIgnoreCase(wDomain)) {
- if (SSLLogger.isOn) {
- SSLLogger.fine(
- "Certificate domain name has illegal " +
- "wildcard for public suffix: " + template);
- }
- return true;
+ // If the wildcarded domain is a top-level domain under which names
+ // can be registered, then a wildcard is not allowed.
+ String wildcardedDomain = afterWildcard.substring(firstDotIndex + 1);
+ String templateDomainSuffix =
+ RegisteredDomain.from("z." + wildcardedDomain)
+ .filter(d -> d.type() == RegisteredDomain.Type.ICANN)
+ .map(RegisteredDomain::publicSuffix).orElse(null);
+ if (templateDomainSuffix == null) {
+ return false; // skip check if not known public suffix
+ }
+
+ // Is it a top-level domain?
+ if (wildcardedDomain.equalsIgnoreCase(templateDomainSuffix)) {
+ if (SSLLogger.isOn) {
+ SSLLogger.fine(
+ "Certificate domain name has illegal " +
+ "wildcard for top-level public suffix: " + template);
}
+ return true;
}
return false;
--- a/src/java.base/share/classes/sun/security/util/KeyStoreDelegator.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/util/KeyStoreDelegator.java Thu Jul 25 08:24:01 2019 -0400
@@ -269,6 +269,8 @@
throw (CertificateException)e;
} else if (e instanceof NoSuchAlgorithmException) {
throw (NoSuchAlgorithmException)e;
+ } else if (e instanceof RuntimeException){
+ throw (RuntimeException)e;
}
}
}
--- a/src/java.base/share/classes/sun/security/util/ManifestDigester.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/util/ManifestDigester.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -25,10 +25,12 @@
package sun.security.util;
-import java.security.*;
+import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.HashMap;
import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.io.IOException;
import java.util.List;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -40,13 +42,27 @@
*/
public class ManifestDigester {
+ /**
+ * The part "{@code Manifest-Main-Attributes}" of the main attributes
+ * digest header name in a signature file as described in the jar
+ * specification:
+ * <blockquote>{@code x-Digest-Manifest-Main-Attributes}
+ * (where x is the standard name of a {@link MessageDigest} algorithm):
+ * The value of this attribute is the digest value of the main attributes
+ * of the manifest.</blockquote>
+ * @see <a href="{@docRoot}/../specs/jar/jar.html#signature-file">
+ * JAR File Specification, section Signature File</a>
+ * @see #getMainAttsEntry
+ */
public static final String MF_MAIN_ATTRS = "Manifest-Main-Attributes";
/** the raw bytes of the manifest */
- private byte[] rawBytes;
+ private final byte[] rawBytes;
- /** the entries grouped by names */
- private HashMap<String, Entry> entries; // key is a UTF-8 string
+ private final Entry mainAttsEntry;
+
+ /** individual sections by their names */
+ private final HashMap<String, Entry> entries = new HashMap<>();
/** state returned by findSection */
static class Position {
@@ -72,29 +88,31 @@
private boolean findSection(int offset, Position pos)
{
int i = offset, len = rawBytes.length;
- int last = offset;
+ int last = offset - 1;
int next;
boolean allBlank = true;
- pos.endOfFirstLine = -1;
+ /* denotes that a position is not yet assigned.
+ * As a primitive type int it cannot be null
+ * and -1 would be confused with (i - 1) when i == 0 */
+ final int UNASSIGNED = Integer.MIN_VALUE;
+
+ pos.endOfFirstLine = UNASSIGNED;
while (i < len) {
byte b = rawBytes[i];
switch(b) {
case '\r':
- if (pos.endOfFirstLine == -1)
+ if (pos.endOfFirstLine == UNASSIGNED)
pos.endOfFirstLine = i-1;
- if ((i < len) && (rawBytes[i+1] == '\n'))
+ if (i < len - 1 && rawBytes[i + 1] == '\n')
i++;
/* fall through */
case '\n':
- if (pos.endOfFirstLine == -1)
+ if (pos.endOfFirstLine == UNASSIGNED)
pos.endOfFirstLine = i-1;
if (allBlank || (i == len-1)) {
- if (i == len-1)
- pos.endOfSection = i;
- else
- pos.endOfSection = last;
+ pos.endOfSection = allBlank ? last : i;
pos.startOfNext = i+1;
return true;
}
@@ -116,16 +134,17 @@
public ManifestDigester(byte[] bytes)
{
rawBytes = bytes;
- entries = new HashMap<>();
Position pos = new Position();
- if (!findSection(0, pos))
+ if (!findSection(0, pos)) {
+ mainAttsEntry = null;
return; // XXX: exception?
+ }
// create an entry for main attributes
- entries.put(MF_MAIN_ATTRS, new Entry().addSection(
- new Section(0, pos.endOfSection + 1, pos.startOfNext, rawBytes)));
+ mainAttsEntry = new Entry().addSection(new Section(
+ 0, pos.endOfSection + 1, pos.startOfNext, rawBytes));
int start = pos.startOfNext;
while(findSection(start, pos)) {
@@ -133,14 +152,16 @@
int sectionLen = pos.endOfSection-start+1;
int sectionLenWithBlank = pos.startOfNext-start;
- if (len > 6) {
+ if (len >= 6) { // 6 == "Name: ".length()
if (isNameAttr(bytes, start)) {
ByteArrayOutputStream nameBuf = new ByteArrayOutputStream();
nameBuf.write(bytes, start+6, len-6);
int i = start + len;
if ((i-start) < sectionLen) {
- if (bytes[i] == '\r') {
+ if (bytes[i] == '\r'
+ && i + 1 - start < sectionLen
+ && bytes[i + 1] == '\n') {
i += 2;
} else {
i += 1;
@@ -152,14 +173,16 @@
// name is wrapped
int wrapStart = i;
while (((i-start) < sectionLen)
- && (bytes[i++] != '\n'));
- if (bytes[i-1] != '\n')
- return; // XXX: exception?
- int wrapLen;
- if (bytes[i-2] == '\r')
- wrapLen = i-wrapStart-2;
- else
- wrapLen = i-wrapStart-1;
+ && (bytes[i] != '\r')
+ && (bytes[i] != '\n')) i++;
+ int wrapLen = i - wrapStart;
+ if (i - start < sectionLen) {
+ i++;
+ if (bytes[i - 1] == '\r'
+ && i - start < sectionLen
+ && bytes[i] == '\n')
+ i++;
+ }
nameBuf.write(bytes, wrapStart, wrapLen);
} else {
@@ -167,7 +190,7 @@
}
}
- entries.computeIfAbsent(new String(nameBuf.toByteArray(), UTF_8),
+ entries.computeIfAbsent(nameBuf.toString(UTF_8),
dummy -> new Entry())
.addSection(new Section(start, sectionLen,
sectionLenWithBlank, rawBytes));
@@ -202,6 +225,26 @@
return this;
}
+ /**
+ * Check if the sections (particularly the last one of usually only one)
+ * are properly delimited with a trailing blank line so that another
+ * section can be correctly appended and return {@code true} or return
+ * {@code false} to indicate that reproduction is not advised and should
+ * be carried out with a clean "normalized" newly-written manifest.
+ *
+ * @see #reproduceRaw
+ */
+ public boolean isProperlyDelimited() {
+ return sections.stream().allMatch(
+ Section::isProperlySectionDelimited);
+ }
+
+ public void reproduceRaw(OutputStream out) throws IOException {
+ for (Section sec : sections) {
+ out.write(sec.rawBytes, sec.offset, sec.lengthWithBlankLine);
+ }
+ }
+
public byte[] digest(MessageDigest md)
{
md.reset();
@@ -242,6 +285,15 @@
this.rawBytes = rawBytes;
}
+ /**
+ * Returns {@code true} if the raw section is terminated with a blank
+ * line so that another section can possibly be appended resulting in a
+ * valid manifest and {@code false} otherwise.
+ */
+ private boolean isProperlySectionDelimited() {
+ return lengthWithBlankLine > length;
+ }
+
private static void doOldStyle(MessageDigest md,
byte[] bytes,
int offset,
@@ -268,10 +320,33 @@
}
}
+ /**
+ * @see #MF_MAIN_ATTRS
+ */
+ public Entry getMainAttsEntry() {
+ return mainAttsEntry;
+ }
+
+ /**
+ * @see #MF_MAIN_ATTRS
+ */
+ public Entry getMainAttsEntry(boolean oldStyle) {
+ mainAttsEntry.oldStyle = oldStyle;
+ return mainAttsEntry;
+ }
+
+ public Entry get(String name) {
+ return entries.get(name);
+ }
+
public Entry get(String name, boolean oldStyle) {
- Entry e = entries.get(name);
- if (e != null)
+ Entry e = get(name);
+ if (e == null && MF_MAIN_ATTRS.equals(name)) {
+ e = getMainAttsEntry();
+ }
+ if (e != null) {
e.oldStyle = oldStyle;
+ }
return e;
}
--- a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -72,8 +72,7 @@
private ArrayList<CodeSigner[]> signerCache;
private static final String ATTR_DIGEST =
- ("-DIGEST-" + ManifestDigester.MF_MAIN_ATTRS).toUpperCase
- (Locale.ENGLISH);
+ "-DIGEST-" + ManifestDigester.MF_MAIN_ATTRS.toUpperCase(Locale.ENGLISH);
/** the PKCS7 block for this .DSA/.RSA/.EC file */
private PKCS7 block;
@@ -537,8 +536,7 @@
MessageDigest digest = getDigest(algorithm);
if (digest != null) {
- ManifestDigester.Entry mde =
- md.get(ManifestDigester.MF_MAIN_ATTRS, false);
+ ManifestDigester.Entry mde = md.getMainAttsEntry(false);
byte[] computedHash = mde.digest(digest);
byte[] expectedHash =
Base64.getMimeDecoder().decode((String)se.getValue());
--- a/src/java.base/share/legal/icu.md Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/legal/icu.md Thu Jul 25 08:24:01 2019 -0400
@@ -1,6 +1,7 @@
## International Components for Unicode (ICU4J) v64.2
### ICU4J License
+```
COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later)
@@ -387,3 +388,6 @@
# by ICANN or the IETF Trust on the database or the code. Any person
# making a contribution to the database or code waives all rights to
# future claims in that contribution or in the TZ Database.
+
+```
+
--- a/src/java.base/share/legal/unicode.md Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/share/legal/unicode.md Thu Jul 25 08:24:01 2019 -0400
@@ -1,6 +1,7 @@
## The Unicode Standard, Unicode Character Database, Version 12.1.0
### Unicode Character Database
+```
UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
@@ -48,3 +49,6 @@
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in these Data Files or Software without prior
written authorization of the copyright holder.
+
+```
+
--- a/src/java.base/solaris/classes/sun/nio/fs/SolarisNativeDispatcher.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/solaris/classes/sun/nio/fs/SolarisNativeDispatcher.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2019, 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
@@ -25,9 +25,6 @@
package sun.nio.fs;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-
/**
* Solaris specific system calls.
*/
@@ -52,11 +49,7 @@
private static native void init();
static {
- AccessController.doPrivileged(new PrivilegedAction<Void>() {
- public Void run() {
- System.loadLibrary("nio");
- return null;
- }});
+ jdk.internal.loader.BootLoader.loadLibrary("nio");
init();
}
}
--- a/src/java.base/solaris/classes/sun/nio/fs/SolarisWatchService.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/solaris/classes/sun/nio/fs/SolarisWatchService.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2019, 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
@@ -26,8 +26,6 @@
package sun.nio.fs;
import java.nio.file.*;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.util.*;
import java.io.IOException;
import jdk.internal.misc.Unsafe;
@@ -817,11 +815,7 @@
throws UnixException;
static {
- AccessController.doPrivileged(new PrivilegedAction<Void>() {
- public Void run() {
- System.loadLibrary("nio");
- return null;
- }});
+ jdk.internal.loader.BootLoader.loadLibrary("nio");
init();
}
}
--- a/src/java.base/unix/classes/sun/net/PortConfig.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/unix/classes/sun/net/PortConfig.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -25,7 +25,7 @@
package sun.net;
-import java.security.AccessController;
+import sun.security.action.GetPropertyAction;
/**
* Determines the ephemeral port range in use on this system.
@@ -41,35 +41,29 @@
private PortConfig() {}
static {
- AccessController.doPrivileged(
- new java.security.PrivilegedAction<>() {
- public Void run() {
- System.loadLibrary("net");
- String os = System.getProperty("os.name");
- if (os.startsWith("Linux")) {
- defaultLower = 32768;
- defaultUpper = 61000;
- } else if (os.startsWith("SunOS")) {
- defaultLower = 32768;
- defaultUpper = 65535;
- } else if (os.contains("OS X")) {
- defaultLower = 49152;
- defaultUpper = 65535;
- } else if (os.startsWith("AIX")) {
- // The ephemeral port is OS version dependent on AIX:
- // http://publib.boulder.ibm.com/infocenter/aix/v7r1/topic/com.ibm.aix.rsct315.admin/bl503_ephport.htm
- // However, on AIX 5.3 / 6.1 / 7.1 we always see the
- // settings below by using:
- // /usr/sbin/no -a | fgrep ephemeral
- defaultLower = 32768;
- defaultUpper = 65535;
- } else {
- throw new InternalError(
- "sun.net.PortConfig: unknown OS");
- }
- return null;
- }
- });
+ jdk.internal.loader.BootLoader.loadLibrary("net");
+ String os = GetPropertyAction.privilegedGetProperty("os.name");
+ if (os.startsWith("Linux")) {
+ defaultLower = 32768;
+ defaultUpper = 61000;
+ } else if (os.startsWith("SunOS")) {
+ defaultLower = 32768;
+ defaultUpper = 65535;
+ } else if (os.contains("OS X")) {
+ defaultLower = 49152;
+ defaultUpper = 65535;
+ } else if (os.startsWith("AIX")) {
+ // The ephemeral port is OS version dependent on AIX:
+ // http://publib.boulder.ibm.com/infocenter/aix/v7r1/topic/com.ibm.aix.rsct315.admin/bl503_ephport.htm
+ // However, on AIX 5.3 / 6.1 / 7.1 we always see the
+ // settings below by using:
+ // /usr/sbin/no -a | fgrep ephemeral
+ defaultLower = 32768;
+ defaultUpper = 65535;
+ } else {
+ throw new InternalError(
+ "sun.net.PortConfig: unknown OS");
+ }
int v = getLower0();
if (v == -1) {
--- a/src/java.base/unix/classes/sun/net/dns/ResolverConfigurationImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/unix/classes/sun/net/dns/ResolverConfigurationImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2019, 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
@@ -259,13 +259,7 @@
static native String fallbackDomain0();
static {
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction<>() {
- public Void run() {
- System.loadLibrary("net");
- return null;
- }
- });
+ jdk.internal.loader.BootLoader.loadLibrary("net");
}
}
--- a/src/java.base/unix/classes/sun/nio/fs/UnixCopyFile.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/unix/classes/sun/nio/fs/UnixCopyFile.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2019, 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
@@ -33,8 +33,6 @@
import java.nio.file.LinkOption;
import java.nio.file.LinkPermission;
import java.nio.file.StandardCopyOption;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -628,12 +626,7 @@
throws UnixException;
static {
- AccessController.doPrivileged(new PrivilegedAction<>() {
- @Override
- public Void run() {
- System.loadLibrary("nio");
- return null;
- }});
+ jdk.internal.loader.BootLoader.loadLibrary("nio");
}
}
--- a/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java Thu Jul 25 08:24:01 2019 -0400
@@ -25,9 +25,6 @@
package sun.nio.fs;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-
/**
* Unix system and library calls.
*/
@@ -630,11 +627,7 @@
private static native int init();
static {
- AccessController.doPrivileged(new PrivilegedAction<>() {
- public Void run() {
- System.loadLibrary("nio");
- return null;
- }});
+ jdk.internal.loader.BootLoader.loadLibrary("nio");
capabilities = init();
}
}
--- a/src/java.base/unix/native/libjava/childproc.c Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/unix/native/libjava/childproc.c Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -26,6 +26,7 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
--- a/src/java.base/unix/native/libnet/NetworkInterface.c Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/unix/native/libnet/NetworkInterface.c Thu Jul 25 08:24:01 2019 -0400
@@ -34,6 +34,7 @@
#include <netinet/in6_var.h>
#include <sys/ndd_var.h>
#include <sys/kinfo.h>
+#include <strings.h>
#endif
#if defined(__solaris__)
--- a/src/java.base/unix/native/libnio/ch/FileChannelImpl.c Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/unix/native/libnio/ch/FileChannelImpl.c Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, 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
@@ -32,6 +32,7 @@
#if defined(__linux__) || defined(__solaris__)
#include <sys/sendfile.h>
#elif defined(_AIX)
+#include <string.h>
#include <sys/socket.h>
#elif defined(_ALLBSD_SOURCE)
#include <sys/socket.h>
--- a/src/java.base/unix/native/libnio/ch/Net.c Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/unix/native/libnio/ch/Net.c Thu Jul 25 08:24:01 2019 -0400
@@ -42,6 +42,7 @@
#include "nio.h"
#ifdef _AIX
+#include <stdlib.h>
#include <sys/utsname.h>
#endif
--- a/src/java.base/windows/classes/sun/net/dns/ResolverConfigurationImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/windows/classes/sun/net/dns/ResolverConfigurationImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2019, 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
@@ -160,13 +160,7 @@
static native int notifyAddrChange0();
static {
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction<Void>() {
- public Void run() {
- System.loadLibrary("net");
- return null;
- }
- });
+ jdk.internal.loader.BootLoader.loadLibrary("net");
init0();
// start the address listener thread
--- a/src/java.base/windows/classes/sun/net/www/protocol/file/Handler.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/windows/classes/sun/net/www/protocol/file/Handler.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2019, 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
@@ -95,7 +95,7 @@
path = "\\\\" + host + path;
File f = new File(path);
if (f.exists()) {
- return createFileURLConnection(url, f);
+ return new UNCFileURLConnection(url, f, path);
}
/*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/windows/classes/sun/net/www/protocol/file/UNCFileURLConnection.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019, 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 sun.net.www.protocol.file;
+
+import java.io.File;
+import java.io.FilePermission;
+import java.net.URL;
+import java.security.Permission;
+
+final class UNCFileURLConnection extends FileURLConnection {
+
+ private final String effectivePath;
+ private volatile Permission permission;
+
+ UNCFileURLConnection(URL u, File file, String effectivePath) {
+ super(u, file);
+ this.effectivePath = effectivePath;
+ }
+
+ @Override
+ public Permission getPermission() {
+ Permission perm = permission;
+ if (perm == null) {
+ permission = perm = new FilePermission(effectivePath, "read");
+ }
+ return perm;
+ }
+}
+
--- a/src/java.base/windows/classes/sun/nio/fs/RegistryFileTypeDetector.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/windows/classes/sun/nio/fs/RegistryFileTypeDetector.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2019, 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
@@ -27,8 +27,6 @@
import java.nio.file.*;
import java.io.IOException;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
/**
* File type detector that does lookup of file extension using Windows Registry.
@@ -70,13 +68,8 @@
private static native String queryStringValue(long subKey, long name);
static {
- AccessController.doPrivileged(new PrivilegedAction<Void>() {
- @Override
- public Void run() {
- // nio.dll has dependency on net.dll
- System.loadLibrary("net");
- System.loadLibrary("nio");
- return null;
- }});
+ // nio.dll has dependency on net.dll
+ jdk.internal.loader.BootLoader.loadLibrary("net");
+ jdk.internal.loader.BootLoader.loadLibrary("nio");
}
}
--- a/src/java.base/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.base/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java Thu Jul 25 08:24:01 2019 -0400
@@ -25,8 +25,6 @@
package sun.nio.fs;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import jdk.internal.misc.Unsafe;
import static sun.nio.fs.WindowsConstants.*;
@@ -1153,13 +1151,9 @@
private static native void initIDs();
static {
- AccessController.doPrivileged(new PrivilegedAction<Void>() {
- public Void run() {
- // nio.dll has dependency on net.dll
- System.loadLibrary("net");
- System.loadLibrary("nio");
- return null;
- }});
+ // nio.dll has dependency on net.dll
+ jdk.internal.loader.BootLoader.loadLibrary("net");
+ jdk.internal.loader.BootLoader.loadLibrary("nio");
initIDs();
}
--- a/src/java.desktop/aix/native/libawt/porting_aix.c Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.desktop/aix/native/libawt/porting_aix.c Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2018 SAP SE. All rights reserved.
+ * Copyright (c) 2012, 2019 SAP SE. 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
@@ -25,6 +25,7 @@
*/
#include <stdio.h>
+#include <string.h>
#include <sys/ldr.h>
#include <errno.h>
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -34,7 +34,6 @@
import javax.swing.JProgressBar;
import javax.swing.JTabbedPane;
import javax.swing.JSlider;
-import javax.swing.JCheckBox;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
@@ -42,6 +41,7 @@
import static javax.accessibility.AccessibleContext.ACCESSIBLE_CARET_PROPERTY;
import static javax.accessibility.AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY;
import static javax.accessibility.AccessibleContext.ACCESSIBLE_STATE_PROPERTY;
+import static javax.accessibility.AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED;
import static javax.accessibility.AccessibleContext.ACCESSIBLE_TEXT_PROPERTY;
import static javax.accessibility.AccessibleContext.ACCESSIBLE_NAME_PROPERTY;
@@ -129,6 +129,8 @@
valueChanged(ptr);
} else if (name.compareTo(ACCESSIBLE_SELECTION_PROPERTY) == 0) {
selectionChanged(ptr);
+ } else if (name.compareTo(ACCESSIBLE_TABLE_MODEL_CHANGED) == 0) {
+ valueChanged(ptr);
} else if (name.compareTo(ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0 ) {
if (newValue instanceof AccessibleContext) {
activeDescendant = (AccessibleContext)newValue;
--- a/src/java.desktop/macosx/native/libsplashscreen/splashscreen_sys.m Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.desktop/macosx/native/libsplashscreen/splashscreen_sys.m Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -214,7 +214,7 @@
splash->maskRequired = 0;
-
+
//TODO: the following is too much of a hack but should work in 90% cases.
// besides we don't use device-dependent drawing, so probably
// that's very fine indeed
@@ -282,9 +282,11 @@
SplashRedrawWindow(Splash * splash) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- SplashUpdateScreenData(splash);
+ [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
+ // drop the reference to the old view and image
+ [splash->window setContentView: nil];
+ SplashUpdateScreenData(splash);
- [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
// NSDeviceRGBColorSpace vs. NSCalibratedRGBColorSpace ?
NSBitmapImageRep * rep = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes: (unsigned char**)&splash->screenData
@@ -311,7 +313,7 @@
size.height /= scaleFactor;
[image setSize: size];
}
-
+
NSImageView * view = [[NSImageView alloc] init];
[view setImage: image];
--- a/src/java.desktop/share/classes/javax/swing/JTable.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.desktop/share/classes/javax/swing/JTable.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -5278,7 +5278,8 @@
// Don't start when just a modifier is pressed
int code = e.getKeyCode();
if (code == KeyEvent.VK_SHIFT || code == KeyEvent.VK_CONTROL ||
- code == KeyEvent.VK_ALT) {
+ code == KeyEvent.VK_ALT || code == KeyEvent.VK_META ||
+ code == KeyEvent.VK_ALT_GRAPH) {
return false;
}
// Try to install the editor
@@ -5302,7 +5303,9 @@
// If we have started an editor as a result of the user
// pressing a key and the surrendersFocusOnKeystroke property
// is true, give the focus to the new editor.
- if (getSurrendersFocusOnKeystroke()) {
+ Object prop = getClientProperty("JTable.forceAutoStartsEdit");
+ if (getSurrendersFocusOnKeystroke()
+ || Boolean.TRUE.equals(prop)) {
editorComponent.requestFocus();
}
}
@@ -6668,6 +6671,7 @@
*/
protected AccessibleJTable() {
super();
+ JTable.this.putClientProperty("JTable.forceAutoStartsEdit", true);
JTable.this.addPropertyChangeListener(this);
JTable.this.getSelectionModel().addListSelectionListener(this);
TableColumnModel tcm = JTable.this.getColumnModel();
@@ -7104,15 +7108,12 @@
int row = rowAtPoint(p);
if ((column != -1) && (row != -1)) {
- TableColumn aColumn = getColumnModel().getColumn(column);
- TableCellRenderer renderer = aColumn.getCellRenderer();
- if (renderer == null) {
- Class<?> columnClass = getColumnClass(column);
- renderer = getDefaultRenderer(columnClass);
- }
- Component component = renderer.getTableCellRendererComponent(
- JTable.this, null, false, false,
- row, column);
+ if (row == getEditingRow() && column == getEditingColumn()) {
+ Component editor = getEditorComponent();
+ if (editor instanceof Accessible) {
+ return (Accessible) editor;
+ }
+ }
return new AccessibleJTableCell(JTable.this, row, column,
getAccessibleIndexAt(row, column));
}
@@ -7145,15 +7146,12 @@
int column = getAccessibleColumnAtIndex(i);
int row = getAccessibleRowAtIndex(i);
- TableColumn aColumn = getColumnModel().getColumn(column);
- TableCellRenderer renderer = aColumn.getCellRenderer();
- if (renderer == null) {
- Class<?> columnClass = getColumnClass(column);
- renderer = getDefaultRenderer(columnClass);
- }
- Component component = renderer.getTableCellRendererComponent(
- JTable.this, null, false, false,
- row, column);
+ if (row == getEditingRow() && column == getEditingColumn()) {
+ Component editor = getEditorComponent();
+ if (editor instanceof Accessible) {
+ return (Accessible) editor;
+ }
+ }
return new AccessibleJTableCell(JTable.this, row, column,
getAccessibleIndexAt(row, column));
}
--- a/src/java.desktop/share/native/libsplashscreen/libpng/png.c Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.c Thu Jul 25 08:24:01 2019 -0400
@@ -4622,8 +4622,7 @@
if (image != NULL && image->opaque != NULL &&
image->opaque->error_buf == NULL)
{
- /* Ignore errors here: */
- (void)png_safe_execute(image, png_image_free_function, image);
+ png_image_free_function(image);
image->opaque = NULL;
}
}
--- a/src/java.desktop/unix/classes/sun/awt/X11/XFontPeer.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.desktop/unix/classes/sun/awt/X11/XFontPeer.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,31 +22,14 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package sun.awt.X11;
import sun.awt.PlatformFont;
-import java.awt.GraphicsEnvironment;
-
-public class XFontPeer extends PlatformFont {
-
- /*
- * XLFD name for XFontSet.
- */
- private String xfsname;
- static {
- if (!GraphicsEnvironment.isHeadless()) {
- initIDs();
- }
- }
+final class XFontPeer extends PlatformFont {
- /**
- * Initialize JNI field and method IDs for fields that may be
- accessed from C.
- */
- private static native void initIDs();
-
- public XFontPeer(String name, int style){
+ XFontPeer(final String name, final int style) {
super(name, style);
}
--- a/src/java.desktop/unix/classes/sun/java2d/xr/XRSurfaceData.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.desktop/unix/classes/sun/java2d/xr/XRSurfaceData.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2013, 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
@@ -54,7 +54,6 @@
import sun.java2d.pipe.ShapeDrawPipe;
import sun.java2d.pipe.TextPipe;
import sun.java2d.pipe.ValidatePipe;
-import sun.java2d.x11.X11SurfaceData;
import sun.java2d.x11.XSurfaceData;
import sun.font.FontManagerNativeLibrary;
@@ -244,7 +243,7 @@
*/
public static XRWindowSurfaceData createData(X11ComponentPeer peer) {
XRGraphicsConfig gc = getGC(peer);
- return new XRWindowSurfaceData(peer, gc, X11SurfaceData.getSurfaceType(gc, Transparency.OPAQUE));
+ return new XRWindowSurfaceData(peer, gc, gc.getSurfaceType());
}
/**
--- a/src/java.desktop/unix/native/common/awt/awt_Font.c Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.desktop/unix/native/common/awt/awt_Font.c Thu Jul 25 08:24:01 2019 -0400
@@ -35,7 +35,6 @@
#include "awt_Font.h"
#include "java_awt_Dimension.h"
-#include "multi_font.h"
#include "Disposer.h"
#endif /* !HEADLESS */
#include <jni.h>
@@ -436,269 +435,6 @@
return 1;
}
-struct FontData *
-awtJNI_GetFontData(JNIEnv * env, jobject font, char **errmsg)
-{
- /* We are going to create at most 4 outstanding local refs in this
- * function. */
- if ((*env)->EnsureLocalCapacity(env, 4) < 0) {
- return NULL;
- }
-
- if (!JNU_IsNull(env, font) && awtJNI_IsMultiFont(env, font)) {
- JNU_CHECK_EXCEPTION_RETURN(env, NULL);
-
- struct FontData *fdata = NULL;
- int32_t i, size;
- char *fontsetname = NULL;
- char *nativename = NULL;
- Boolean doFree = FALSE;
- jobjectArray componentFonts = NULL;
- jobject peer = NULL;
- jobject fontDescriptor = NULL;
- jstring fontDescriptorName = NULL;
- jstring charsetName = NULL;
-
- fdata = (struct FontData *) JNU_GetLongFieldAsPtr(env,font,
- fontIDs.pData);
-
- if (fdata != NULL && fdata->flist != NULL) {
- return fdata;
- }
- size = (*env)->GetIntField(env, font, fontIDs.size);
- fdata = (struct FontData *) malloc(sizeof(struct FontData));
-
- peer = (*env)->CallObjectMethod(env, font, fontIDs.getPeer);
-
- componentFonts =
- (*env)->GetObjectField(env, peer, platformFontIDs.componentFonts);
- /* We no longer need peer */
- (*env)->DeleteLocalRef(env, peer);
-
- fdata->charset_num = (*env)->GetArrayLength(env, componentFonts);
-
- fdata->flist = (awtFontList *) malloc(sizeof(awtFontList)
- * fdata->charset_num);
- fdata->xfont = NULL;
- for (i = 0; i < fdata->charset_num; i++) {
- /*
- * set xlfd name
- */
-
- fontDescriptor = (*env)->GetObjectArrayElement(env, componentFonts, i);
- fontDescriptorName =
- (*env)->GetObjectField(env, fontDescriptor,
- fontDescriptorIDs.nativeName);
-
- if (!JNU_IsNull(env, fontDescriptorName)) {
- nativename = (char *) JNU_GetStringPlatformChars(env, fontDescriptorName, NULL);
- if (nativename == NULL) {
- nativename = "";
- doFree = FALSE;
- } else {
- doFree = TRUE;
- }
- } else {
- nativename = "";
- doFree = FALSE;
- }
-
- fdata->flist[i].xlfd = malloc(strlen(nativename)
- + strlen(defaultXLFD));
- jio_snprintf(fdata->flist[i].xlfd, strlen(nativename) + 10,
- nativename, size * 10);
-
- if (nativename != NULL && doFree)
- JNU_ReleaseStringPlatformChars(env, fontDescriptorName, (const char *) nativename);
-
- /*
- * set charset_name
- */
-
- charsetName =
- (*env)->GetObjectField(env, fontDescriptor,
- fontDescriptorIDs.charsetName);
-
- fdata->flist[i].charset_name = (char *)
- JNU_GetStringPlatformChars(env, charsetName, NULL);
- if (fdata->flist[i].charset_name == NULL) {
- (*env)->ExceptionClear(env);
- JNU_ThrowOutOfMemoryError(env, "Could not create charset name");
- return NULL;
- }
-
- /* We are done with the objects. */
- (*env)->DeleteLocalRef(env, fontDescriptor);
- (*env)->DeleteLocalRef(env, fontDescriptorName);
- (*env)->DeleteLocalRef(env, charsetName);
-
- /*
- * set load & XFontStruct
- */
- fdata->flist[i].load = 0;
-
- /*
- * This appears to be a bogus check. The actual intent appears
- * to be to find out whether this is the "base" font in a set,
- * rather than iso8859_1 explicitly. Note that iso8859_15 will
- * and must also pass this test.
- */
-
- if (fdata->xfont == NULL &&
- strstr(fdata->flist[i].charset_name, "8859_1")) {
- fdata->flist[i].xfont =
- loadFont(awt_display, fdata->flist[i].xlfd, size * 10);
- if (fdata->flist[i].xfont != NULL) {
- fdata->flist[i].load = 1;
- fdata->xfont = fdata->flist[i].xfont;
- fdata->flist[i].index_length = 1;
- } else {
- /* Free any already allocated storage and fonts */
- int j = i;
- for (j = 0; j <= i; j++) {
- free((void *)fdata->flist[j].xlfd);
- JNU_ReleaseStringPlatformChars(env, NULL,
- fdata->flist[j].charset_name);
- if (fdata->flist[j].load) {
- XFreeFont(awt_display, fdata->flist[j].xfont);
- }
- }
- free((void *)fdata->flist);
- free((void *)fdata);
-
- if (errmsg != NULL) {
- *errmsg = "java/lang" "NullPointerException";
- }
- (*env)->DeleteLocalRef(env, componentFonts);
- return NULL;
- }
- }
- }
- (*env)->DeleteLocalRef(env, componentFonts);
- /*
- * XFontSet will create if the peer of TextField/TextArea
- * are used.
- */
- fdata->xfs = NULL;
-
- JNU_SetLongFieldFromPtr(env,font,fontIDs.pData,fdata);
- Disposer_AddRecord(env, font, pDataDisposeMethod, ptr_to_jlong(fdata));
- return fdata;
- } else {
- JNU_CHECK_EXCEPTION_RETURN(env, NULL);
- Display *display = NULL;
- struct FontData *fdata = NULL;
- char fontSpec[1024];
- int32_t height;
- int32_t oheight;
- int32_t above = 0; /* tries above height */
- int32_t below = 0; /* tries below height */
- char *foundry = NULL;
- char *name = NULL;
- char *encoding = NULL;
- char *style = NULL;
- XFontStruct *xfont = NULL;
- jstring family = NULL;
-
- if (JNU_IsNull(env, font)) {
- if (errmsg != NULL) {
- *errmsg = "java/lang" "NullPointerException";
- }
- return (struct FontData *) NULL;
- }
- display = XDISPLAY;
-
- fdata = (struct FontData *) JNU_GetLongFieldAsPtr(env,font,fontIDs.pData);
- if (fdata != NULL && fdata->xfont != NULL) {
- return fdata;
- }
-
- family = (*env)->CallObjectMethod(env, font, fontIDs.getFamily);
-
- if (!awtJNI_FontName(env, family, &foundry, &name, &encoding)) {
- if (errmsg != NULL) {
- *errmsg = "java/lang" "NullPointerException";
- }
- (*env)->DeleteLocalRef(env, family);
- return (struct FontData *) NULL;
- }
- style = Style((*env)->GetIntField(env, font, fontIDs.style));
- oheight = height = (*env)->GetIntField(env, font, fontIDs.size);
-
- while (1) {
- jio_snprintf(fontSpec, sizeof(fontSpec), "-%s-%s-%s-*-*-%d-*-*-*-*-*-%s",
- foundry,
- name,
- style,
- height,
- encoding);
-
- /*fprintf(stderr,"LoadFont: %s\n", fontSpec); */
- xfont = XLoadQueryFont(display, fontSpec);
-
- /* XXX: sometimes XLoadQueryFont returns a bogus font structure */
- /* with negative ascent. */
- if (xfont == NULL || xfont->ascent < 0) {
- if (xfont != NULL) {
- XFreeFont(display, xfont);
- }
- if (foundry != anyfoundry) { /* Use ptr comparison here, not strcmp */
- /* Try any other foundry before messing with the sizes */
- foundry = anyfoundry;
- continue;
- }
- /* We couldn't find the font. We'll try to find an */
- /* alternate by searching for heights above and below our */
- /* preferred height. We try for 4 heights above and below. */
- /* If we still can't find a font we repeat the algorithm */
- /* using misc-fixed as the font. If we then fail, then we */
- /* give up and signal an error. */
- if (above == below) {
- above++;
- height = oheight + above;
- } else {
- below++;
- if (below > 4) {
- if (name != defaultfontname || style != anystyle) {
- name = defaultfontname;
- foundry = defaultfoundry;
- height = oheight;
- style = anystyle;
- encoding = isolatin1;
- above = below = 0;
- continue;
- } else {
- if (errmsg != NULL) {
- *errmsg = "java/io/" "FileNotFoundException";
- }
- (*env)->DeleteLocalRef(env, family);
- return (struct FontData *) NULL;
- }
- }
- height = oheight - below;
- }
- continue;
- } else {
- fdata = ZALLOC(FontData);
-
- if (fdata == NULL) {
- if (errmsg != NULL) {
- *errmsg = "java/lang" "OutOfMemoryError";
- }
- } else {
- fdata->xfont = xfont;
- JNU_SetLongFieldFromPtr(env,font,fontIDs.pData,fdata);
- Disposer_AddRecord(env, font, pDataDisposeMethod,
- ptr_to_jlong(fdata));
- }
- (*env)->DeleteLocalRef(env, family);
- return fdata;
- }
- }
- /* not reached */
- }
-}
-
/*
* Registered with the 2D disposer to be called after the Font is GC'd.
*/
--- a/src/java.desktop/unix/native/common/awt/awt_Font.h Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.desktop/unix/native/common/awt/awt_Font.h Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, 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
@@ -34,11 +34,6 @@
jmethodID getFamily;
};
-/* fieldIDs for XFontPeer fields that may be accessed from C */
-struct XFontPeerIDs {
- jfieldID xfsname;
-};
-
/* fieldIDs for PlatformFont fields that may be accessed from C */
struct PlatformFontIDs {
jfieldID componentFonts;
--- a/src/java.desktop/unix/native/common/awt/awt_p.h Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.desktop/unix/native/common/awt/awt_p.h Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2019, 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
@@ -119,8 +119,6 @@
XFontStruct *xfont; /* Latin1 font */
};
-extern struct FontData *awtJNI_GetFontData(JNIEnv *env,jobject font, char **errmsg);
-
extern AwtGraphicsConfigDataPtr getDefaultConfig(int screen);
extern AwtScreenDataPtr getScreenData(int screen);
#endif /* !HEADLESS */
--- a/src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2019, 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
@@ -150,7 +150,7 @@
static jboolean isXCompositeDisplay(Display *display, int screenNumber) {
char NET_WM_CM_Sn[25];
- snprintf(NET_WM_CM_Sn, sizeof(NET_WM_CM_Sn), "_NET_WM_CM_S%d\0", screenNumber);
+ snprintf(NET_WM_CM_Sn, sizeof(NET_WM_CM_Sn), "_NET_WM_CM_S%d", screenNumber);
Atom managerSelection = XInternAtom(display, NET_WM_CM_Sn, 0);
Window owner = XGetSelectionOwner(display, managerSelection);
--- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -2451,14 +2451,14 @@
static jobject get_integer_property(JNIEnv *env, GtkSettings* settings, const gchar* key)
{
- gint intval = NULL;
+ gint intval = 0;
(*fp_g_object_get)(settings, key, &intval, NULL);
return create_Integer(env, intval);
}
static jobject get_boolean_property(JNIEnv *env, GtkSettings* settings, const gchar* key)
{
- gint intval = NULL;
+ gint intval = 0;
(*fp_g_object_get)(settings, key, &intval, NULL);
return create_Boolean(env, intval);
}
--- a/src/java.desktop/unix/native/libawt_xawt/awt/multi_font.c Thu Jul 25 08:13:44 2019 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,393 +0,0 @@
-/*
- * Copyright (c) 1996, 2014, 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.
- */
-
-/*
- * These routines are used for display string with multi font.
- */
-
-#ifdef HEADLESS
- #error This file should not be included in headless library
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <ctype.h>
-#include <jni.h>
-#include <jni_util.h>
-#include <jvm.h>
-#include "awt_Font.h"
-#include "awt_p.h"
-#include "multi_font.h"
-
-extern XFontStruct *loadFont(Display *, char *, int32_t);
-
-extern struct FontIDs fontIDs;
-extern struct PlatformFontIDs platformFontIDs;
-extern struct XFontPeerIDs xFontPeerIDs;
-
-/*
- * make string with str + string representation of num
- * This string is used as tag string of Motif Compound String and FontList.
- */
-static void
-makeTag(char *str, int32_t num, char *buf)
-{
- int32_t len = strlen(str);
-
- strcpy(buf, str);
- buf[len] = '0' + num % 100;
- buf[len + 1] = '\0';
-}
-
-static int32_t
-awtJNI_GetFontDescriptorNumber(JNIEnv * env
- ,jobject font
- ,jobject fd)
-{
- int32_t i = 0, num;
- /* initialize to NULL so that DeleteLocalRef will work. */
- jobjectArray componentFonts = NULL;
- jobject peer = NULL;
- jobject temp = NULL;
- jboolean validRet = JNI_FALSE;
-
- if ((*env)->EnsureLocalCapacity(env, 2) < 0 || (*env)->ExceptionCheck(env))
- goto done;
-
- peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);
- if (peer == NULL)
- goto done;
-
- componentFonts = (jobjectArray)
- (*env)->GetObjectField(env,peer,platformFontIDs.componentFonts);
-
- if (componentFonts == NULL)
- goto done;
-
- num = (*env)->GetArrayLength(env, componentFonts);
-
- for (i = 0; i < num; i++) {
- temp = (*env)->GetObjectArrayElement(env, componentFonts, i);
-
- if ((*env)->IsSameObject(env, fd, temp)) {
- validRet = JNI_TRUE;
- break;
- }
- (*env)->DeleteLocalRef(env, temp);
- }
-
- done:
- (*env)->DeleteLocalRef(env, peer);
- (*env)->DeleteLocalRef(env, componentFonts);
-
- if (validRet)
- return i;
-
- return 0;
-}
-
-jobject
-awtJNI_GetFMFont(JNIEnv * env, jobject this)
-{
- return JNU_CallMethodByName(env, NULL, this, "getFont_NoClientCode",
- "()Ljava/awt/Font;").l;
-}
-
-jboolean
-awtJNI_IsMultiFont(JNIEnv * env, jobject this)
-{
- jobject peer = NULL;
- jobject fontConfig = NULL;
-
- if (this == NULL) {
- return JNI_FALSE;
- }
-
- if ((*env)->EnsureLocalCapacity(env, 2) < 0) {
- return JNI_FALSE;
- }
-
- peer = (*env)->CallObjectMethod(env,this,fontIDs.getPeer);
- if (peer == NULL) {
- return JNI_FALSE;
- }
-
- fontConfig = (*env)->GetObjectField(env,peer,platformFontIDs.fontConfig);
- (*env)->DeleteLocalRef(env, peer);
-
- if (fontConfig == NULL) {
- return JNI_FALSE;
- }
- (*env)->DeleteLocalRef(env, fontConfig);
-
- return JNI_TRUE;
-}
-
-jboolean
-awtJNI_IsMultiFontMetrics(JNIEnv * env, jobject this)
-{
- jobject peer = NULL;
- jobject fontConfig = NULL;
- jobject font = NULL;
-
- if (JNU_IsNull(env, this)) {
- return JNI_FALSE;
- }
- if ((*env)->EnsureLocalCapacity(env, 3) < 0) {
- return JNI_FALSE;
- }
-
- font = JNU_CallMethodByName(env, NULL, this, "getFont_NoClientCode",
- "()Ljava/awt/Font;").l;
- if (JNU_IsNull(env, font) || (*env)->ExceptionCheck(env)) {
- return JNI_FALSE;
- }
-
- peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);
- (*env)->DeleteLocalRef(env, font);
-
- if (peer == NULL) {
- return JNI_FALSE;
- }
-
- fontConfig = (*env)->GetObjectField(env,peer,platformFontIDs.fontConfig);
- (*env)->DeleteLocalRef(env, peer);
- if (fontConfig == NULL) {
- return JNI_FALSE;
- }
- (*env)->DeleteLocalRef(env, fontConfig);
-
- return JNI_TRUE;
-}
-
-/* #define FONT_DEBUG 2 */
-
-XFontSet
-awtJNI_MakeFontSet(JNIEnv * env, jobject font)
-{
- jstring xlfd = NULL;
- char *xfontset = NULL;
- int32_t size;
- int32_t length = 0;
- char *realxlfd = NULL, *ptr = NULL, *prev = NULL;
- char **missing_list = NULL;
- int32_t missing_count;
- char *def_string = NULL;
- XFontSet xfs;
- jobject peer = NULL;
- jstring xfsname = NULL;
-#ifdef FONT_DEBUG
- char xx[1024];
-#endif
-
- if ((*env)->EnsureLocalCapacity(env, 2) < 0)
- return 0;
-
- size = (*env)->GetIntField(env, font, fontIDs.size) * 10;
-
- peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);
- xfsname = (*env)->GetObjectField(env, peer, xFontPeerIDs.xfsname);
-
- if (JNU_IsNull(env, xfsname))
- xfontset = "";
- else
- xfontset = (char *)JNU_GetStringPlatformChars(env, xfsname, NULL);
-
- realxlfd = malloc(strlen(xfontset) + 50);
-
- prev = ptr = xfontset;
- while ((ptr = strstr(ptr, "%d"))) {
- char save = *(ptr + 2);
-
- *(ptr + 2) = '\0';
- jio_snprintf(realxlfd + length, strlen(xfontset) + 50 - length,
- prev, size);
- length = strlen(realxlfd);
- *(ptr + 2) = save;
-
- prev = ptr + 2;
- ptr += 2;
- }
- strcpy(realxlfd + length, prev);
-
-#ifdef FONT_DEBUG
- strcpy(xx, realxlfd);
-#endif
- xfs = XCreateFontSet(awt_display, realxlfd, &missing_list,
- &missing_count, &def_string);
-#if FONT_DEBUG >= 2
- fprintf(stderr, "XCreateFontSet(%s)->0x%x\n", xx, xfs);
-#endif
-
-#if FONT_DEBUG
- if (missing_count != 0) {
- int32_t i;
- fprintf(stderr, "XCreateFontSet missing %d fonts:\n", missing_count);
- for (i = 0; i < missing_count; ++i) {
- fprintf(stderr, "\t\"%s\"\n", missing_list[i]);
- }
- fprintf(stderr, " requested \"%s\"\n", xx);
-#if FONT_DEBUG >= 3
- exit(-1);
-#endif
- }
-#endif
-
- free((void *)realxlfd);
-
- if (xfontset && !JNU_IsNull(env, xfsname))
- JNU_ReleaseStringPlatformChars(env, xfsname, (const char *) xfontset);
-
- (*env)->DeleteLocalRef(env, peer);
- (*env)->DeleteLocalRef(env, xfsname);
- return xfs;
-}
-
-/*
- * get multi font string width with multiple X11 font
- *
- * ASSUMES: We are not running on a privileged thread
- */
-int32_t
-awtJNI_GetMFStringWidth(JNIEnv * env, jcharArray s, int offset, int sLength, jobject font)
-{
- char *err = NULL;
- unsigned char *stringData = NULL;
- char *offsetStringData = NULL;
- int32_t stringCount, i;
- int32_t size;
- struct FontData *fdata = NULL;
- jobject fontDescriptor = NULL;
- jbyteArray data = NULL;
- int32_t j;
- int32_t width = 0;
- int32_t length;
- XFontStruct *xf = NULL;
- jobjectArray dataArray = NULL;
- if ((*env)->EnsureLocalCapacity(env, 3) < 0)
- return 0;
-
- if (!JNU_IsNull(env, s) && !JNU_IsNull(env, font))
- {
- jobject peer;
- peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);
-
- dataArray = (*env)->CallObjectMethod(
- env,
- peer,
- platformFontIDs.makeConvertedMultiFontChars,
- s, offset, sLength);
-
- if ((*env)->ExceptionOccurred(env))
- {
- (*env)->ExceptionDescribe(env);
- (*env)->ExceptionClear(env);
- }
-
- (*env)->DeleteLocalRef(env, peer);
-
- if(dataArray == NULL)
- {
- return 0;
- }
- } else {
- return 0;
- }
-
- fdata = awtJNI_GetFontData(env, font, &err);
- if ((*env)->ExceptionCheck(env)) {
- (*env)->DeleteLocalRef(env, dataArray);
- return 0;
- }
-
- stringCount = (*env)->GetArrayLength(env, dataArray);
-
- size = (*env)->GetIntField(env, font, fontIDs.size);
-
- for (i = 0; i < stringCount; i+=2)
- {
- fontDescriptor = (*env)->GetObjectArrayElement(env, dataArray, i);
- data = (*env)->GetObjectArrayElement(env, dataArray, i + 1);
-
- /* Bail if we've finished */
- if (fontDescriptor == NULL || data == NULL) {
- (*env)->DeleteLocalRef(env, fontDescriptor);
- (*env)->DeleteLocalRef(env, data);
- break;
- }
-
- j = awtJNI_GetFontDescriptorNumber(env, font, fontDescriptor);
- if ((*env)->ExceptionCheck(env)) {
- (*env)->DeleteLocalRef(env, fontDescriptor);
- (*env)->DeleteLocalRef(env, data);
- break;
- }
-
- if (fdata->flist[j].load == 0) {
- xf = loadFont(awt_display,
- fdata->flist[j].xlfd, size * 10);
- if (xf == NULL) {
- (*env)->DeleteLocalRef(env, fontDescriptor);
- (*env)->DeleteLocalRef(env, data);
- continue;
- }
- fdata->flist[j].load = 1;
- fdata->flist[j].xfont = xf;
- if (xf->min_byte1 == 0 && xf->max_byte1 == 0)
- fdata->flist[j].index_length = 1;
- else
- fdata->flist[j].index_length = 2;
- }
- xf = fdata->flist[j].xfont;
-
- stringData =
- (unsigned char *)(*env)->GetPrimitiveArrayCritical(env, data,NULL);
- if (stringData == NULL) {
- (*env)->DeleteLocalRef(env, fontDescriptor);
- (*env)->DeleteLocalRef(env, data);
- (*env)->ExceptionClear(env);
- JNU_ThrowOutOfMemoryError(env, "Could not get string data");
- break;
- }
-
- length = (stringData[0] << 24) | (stringData[1] << 16) |
- (stringData[2] << 8) | stringData[3];
- offsetStringData = (char *)(stringData + (4 * sizeof(char)));
-
- if (fdata->flist[j].index_length == 2) {
- width += XTextWidth16(xf, (XChar2b *)offsetStringData, length/2);
- } else {
- width += XTextWidth(xf, offsetStringData, length);
- }
-
- (*env)->ReleasePrimitiveArrayCritical(env, data, stringData, JNI_ABORT);
- (*env)->DeleteLocalRef(env, fontDescriptor);
- (*env)->DeleteLocalRef(env, data);
- }
- (*env)->DeleteLocalRef(env, dataArray);
-
- return width;
-}
--- a/src/java.desktop/unix/native/libawt_xawt/awt/multi_font.h Thu Jul 25 08:13:44 2019 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 1996, 2011, 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.
- */
-/*
- * header for Multi Font String
- */
-#ifndef _MULTI_FONT_H_
-#define _MULTI_FONT_H_
-
-#ifndef HEADLESS
-jboolean awtJNI_IsMultiFont(JNIEnv *env,jobject this);
-jboolean awtJNI_IsMultiFontMetrics(JNIEnv *env,jobject this);
-XFontSet awtJNI_MakeFontSet(JNIEnv *env,jobject font);
-struct FontData *awtJNI_GetFontData(JNIEnv *env,jobject font, char **errmsg);
-int32_t awtJNI_GetMFStringWidth(JNIEnv * env, jcharArray s, int32_t offset,
- int32_t length, jobject font);
-#endif /* !HEADLESS */
-
-#endif /* _MULTI_FONT_H_ */
--- a/src/java.desktop/unix/native/libawt_xawt/xawt/XToolkit.c Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.desktop/unix/native/libawt_xawt/xawt/XToolkit.c Thu Jul 25 08:24:01 2019 -0400
@@ -79,16 +79,6 @@
extern Display* awt_init_Display(JNIEnv *env, jobject this);
extern void freeNativeStringArray(char **array, jsize length);
extern char** stringArrayToNative(JNIEnv *env, jobjectArray array, jsize * ret_length);
-
-struct XFontPeerIDs xFontPeerIDs;
-
-JNIEXPORT void JNICALL
-Java_sun_awt_X11_XFontPeer_initIDs
- (JNIEnv *env, jclass cls)
-{
- xFontPeerIDs.xfsname =
- (*env)->GetFieldID(env, cls, "xfsname", "Ljava/lang/String;");
-}
#endif /* !HEADLESS */
/* This function gets called from the static initializer for FileDialog.java
--- a/src/java.security.jgss/macosx/native/libosxkrb5/SCDynamicStoreConfig.m Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/macosx/native/libosxkrb5/SCDynamicStoreConfig.m Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -102,7 +102,8 @@
for (NSString *realm in realms) {
CFTypeRef realmInfo = SCDynamicStoreCopyValue(store, (CFStringRef) [NSString stringWithFormat:@"Kerberos:%@", realm]);
- if (CFGetTypeID(realmInfo) != CFDictionaryGetTypeID()) {
+ if (realmInfo == NULL || CFGetTypeID(realmInfo) != CFDictionaryGetTypeID()) {
+ if (realmInfo) CFRelease(realmInfo);
return nil;
}
--- a/src/java.security.jgss/macosx/native/libosxkrb5/nativeccache.c Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/macosx/native/libosxkrb5/nativeccache.c Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -345,7 +345,7 @@
if (krbcredsConstructor == 0) {
krbcredsConstructor = (*env)->GetMethodID(env, krbcredsClass, "<init>",
- "(Lsun/security/krb5/internal/Ticket;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/EncryptionKey;Lsun/security/krb5/internal/TicketFlags;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/HostAddresses;)V");
+ "(Lsun/security/krb5/internal/Ticket;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/EncryptionKey;Lsun/security/krb5/internal/TicketFlags;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/HostAddresses;)V");
if (krbcredsConstructor == 0) {
printf("Couldn't find sun.security.krb5.internal.Ticket constructor\n");
break;
@@ -359,7 +359,9 @@
krbcredsConstructor,
ticket,
clientPrincipal,
+ NULL,
targetPrincipal,
+ NULL,
encryptionKey,
ticketFlags,
authTime,
--- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -26,8 +26,6 @@
package javax.security.auth.kerberos;
import sun.security.krb5.JavaxSecurityAuthKerberosAccess;
-import sun.security.krb5.EncryptionKey;
-import sun.security.krb5.PrincipalName;
class JavaxSecurityAuthKerberosAccessImpl
implements JavaxSecurityAuthKerberosAccess {
@@ -35,4 +33,20 @@
KeyTab ktab) {
return ktab.takeSnapshot();
}
+
+ public KerberosPrincipal kerberosTicketGetClientAlias(KerberosTicket t) {
+ return t.clientAlias;
+ }
+
+ public void kerberosTicketSetClientAlias(KerberosTicket t, KerberosPrincipal a) {
+ t.clientAlias = a;
+ }
+
+ public KerberosPrincipal kerberosTicketGetServerAlias(KerberosTicket t) {
+ return t.serverAlias;
+ }
+
+ public void kerberosTicketSetServerAlias(KerberosTicket t, KerberosPrincipal a) {
+ t.serverAlias = a;
+ }
}
--- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosTicket.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosTicket.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, 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
@@ -195,6 +195,10 @@
private transient boolean destroyed = false;
+ transient KerberosPrincipal clientAlias = null;
+
+ transient KerberosPrincipal serverAlias = null;
+
/**
* Constructs a {@code KerberosTicket} using credentials information that a
* client either receives from a KDC or reads from a cache.
@@ -591,7 +595,11 @@
try {
krb5Creds = new sun.security.krb5.Credentials(asn1Encoding,
client.getName(),
+ (clientAlias != null ?
+ clientAlias.getName() : null),
server.getName(),
+ (serverAlias != null ?
+ serverAlias.getName() : null),
sessionKey.getEncoded(),
sessionKey.getKeyType(),
flags,
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, 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
@@ -713,14 +713,14 @@
if (subject != null &&
!subject.isReadOnly()) {
/*
- * Store the service credentials as
- * javax.security.auth.kerberos.KerberosTicket in
- * the Subject. We could wait till the context is
- * succesfully established; however it is easier
- * to do here and there is no harm indoing it here.
- */
+ * Store the service credentials as
+ * javax.security.auth.kerberos.KerberosTicket in
+ * the Subject. We could wait until the context is
+ * successfully established; however it is easier
+ * to do it here and there is no harm.
+ */
final KerberosTicket kt =
- Krb5Util.credsToTicket(serviceCreds);
+ Krb5Util.credsToTicket(serviceCreds);
AccessController.doPrivileged (
new java.security.PrivilegedAction<Void>() {
public Void run() {
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, 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
@@ -59,7 +59,9 @@
private Krb5InitCredential(Krb5NameElement name,
byte[] asn1Encoding,
KerberosPrincipal client,
+ KerberosPrincipal clientAlias,
KerberosPrincipal server,
+ KerberosPrincipal serverAlias,
byte[] sessionKey,
int keyType,
boolean[] flags,
@@ -80,14 +82,21 @@
endTime,
renewTill,
clientAddresses);
-
+ KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
+ .kerberosTicketSetClientAlias(this, clientAlias);
+ KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
+ .kerberosTicketSetServerAlias(this, serverAlias);
this.name = name;
try {
// Cache this for later use by the sun.security.krb5 package.
krb5Credentials = new Credentials(asn1Encoding,
client.getName(),
+ (clientAlias != null ?
+ clientAlias.getName() : null),
server.getName(),
+ (serverAlias != null ?
+ serverAlias.getName() : null),
sessionKey,
keyType,
flags,
@@ -110,7 +119,9 @@
Credentials delegatedCred,
byte[] asn1Encoding,
KerberosPrincipal client,
+ KerberosPrincipal clientAlias,
KerberosPrincipal server,
+ KerberosPrincipal serverAlias,
byte[] sessionKey,
int keyType,
boolean[] flags,
@@ -131,7 +142,10 @@
endTime,
renewTill,
clientAddresses);
-
+ KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
+ .kerberosTicketSetClientAlias(this, clientAlias);
+ KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
+ .kerberosTicketSetServerAlias(this, serverAlias);
this.name = name;
// A delegated cred does not have all fields set. So do not try to
// creat new Credentials out of the delegatedCred.
@@ -153,10 +167,18 @@
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
}
+ KerberosPrincipal clientAlias = KerberosSecrets
+ .getJavaxSecurityAuthKerberosAccess()
+ .kerberosTicketGetClientAlias(tgt);
+ KerberosPrincipal serverAlias = KerberosSecrets
+ .getJavaxSecurityAuthKerberosAccess()
+ .kerberosTicketGetServerAlias(tgt);
return new Krb5InitCredential(name,
tgt.getEncoded(),
tgt.getClient(),
+ clientAlias,
tgt.getServer(),
+ serverAlias,
tgt.getSessionKey().getEncoded(),
tgt.getSessionKeyType(),
tgt.getFlags(),
@@ -179,10 +201,14 @@
*/
PrincipalName cPrinc = delegatedCred.getClient();
+ PrincipalName cAPrinc = delegatedCred.getClientAlias();
PrincipalName sPrinc = delegatedCred.getServer();
+ PrincipalName sAPrinc = delegatedCred.getServerAlias();
KerberosPrincipal client = null;
+ KerberosPrincipal clientAlias = null;
KerberosPrincipal server = null;
+ KerberosPrincipal serverAlias = null;
Krb5NameElement credName = null;
@@ -193,6 +219,10 @@
client = new KerberosPrincipal(fullName);
}
+ if (cAPrinc != null) {
+ clientAlias = new KerberosPrincipal(cAPrinc.getName());
+ }
+
// XXX Compare name to credName
if (sPrinc != null) {
@@ -201,11 +231,17 @@
KerberosPrincipal.KRB_NT_SRV_INST);
}
+ if (sAPrinc != null) {
+ serverAlias = new KerberosPrincipal(sAPrinc.getName());
+ }
+
return new Krb5InitCredential(credName,
delegatedCred,
delegatedCred.getEncoded(),
client,
+ clientAlias,
server,
+ serverAlias,
sessionKey.getBytes(),
sessionKey.getEType(),
delegatedCred.getFlags(),
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java Thu Jul 25 08:24:01 2019 -0400
@@ -132,7 +132,7 @@
public static KerberosTicket credsToTicket(Credentials serviceCreds) {
EncryptionKey sessionKey = serviceCreds.getSessionKey();
- return new KerberosTicket(
+ KerberosTicket kt = new KerberosTicket(
serviceCreds.getEncoded(),
new KerberosPrincipal(serviceCreds.getClient().getName()),
new KerberosPrincipal(serviceCreds.getServer().getName(),
@@ -145,14 +145,35 @@
serviceCreds.getEndTime(),
serviceCreds.getRenewTill(),
serviceCreds.getClientAddresses());
+ PrincipalName clientAlias = serviceCreds.getClientAlias();
+ PrincipalName serverAlias = serviceCreds.getServerAlias();
+ if (clientAlias != null) {
+ KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
+ .kerberosTicketSetClientAlias(kt, new KerberosPrincipal(
+ clientAlias.getName(), clientAlias.getNameType()));
+ }
+ if (serverAlias != null) {
+ KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
+ .kerberosTicketSetServerAlias(kt, new KerberosPrincipal(
+ serverAlias.getName(), serverAlias.getNameType()));
+ }
+ return kt;
};
public static Credentials ticketToCreds(KerberosTicket kerbTicket)
throws KrbException, IOException {
+ KerberosPrincipal clientAlias = KerberosSecrets
+ .getJavaxSecurityAuthKerberosAccess()
+ .kerberosTicketGetClientAlias(kerbTicket);
+ KerberosPrincipal serverAlias = KerberosSecrets
+ .getJavaxSecurityAuthKerberosAccess()
+ .kerberosTicketGetServerAlias(kerbTicket);
return new Credentials(
kerbTicket.getEncoded(),
kerbTicket.getClient().getName(),
+ (clientAlias != null ? clientAlias.getName() : null),
kerbTicket.getServer().getName(),
+ (serverAlias != null ? serverAlias.getName() : null),
kerbTicket.getSessionKey().getEncoded(),
kerbTicket.getSessionKeyType(),
kerbTicket.getFlags(),
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/SubjectComber.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/SubjectComber.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2019, 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
@@ -25,6 +25,8 @@
package sun.security.jgss.krb5;
+import sun.security.krb5.KerberosSecrets;
+
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KerberosKey;
import javax.security.auth.Subject;
@@ -182,24 +184,45 @@
}
} else {
+ KerberosPrincipal serverAlias = KerberosSecrets
+ .getJavaxSecurityAuthKerberosAccess()
+ .kerberosTicketGetServerAlias(ticket);
if (serverPrincipal == null ||
- ticket.getServer().getName().equals(serverPrincipal)) {
-
+ ticket.getServer().getName().equals(serverPrincipal) ||
+ (serverAlias != null &&
+ serverPrincipal.equals(
+ serverAlias.getName()))) {
+ KerberosPrincipal clientAlias = KerberosSecrets
+ .getJavaxSecurityAuthKerberosAccess()
+ .kerberosTicketGetClientAlias(ticket);
if (clientPrincipal == null ||
clientPrincipal.equals(
- ticket.getClient().getName())) {
+ ticket.getClient().getName()) ||
+ (clientAlias != null &&
+ clientPrincipal.equals(
+ clientAlias.getName()))) {
if (oneOnly) {
return ticket;
} else {
// Record names so that tickets will
// all belong to same principals
if (clientPrincipal == null) {
- clientPrincipal =
- ticket.getClient().getName();
+ if (clientAlias == null) {
+ clientPrincipal =
+ ticket.getClient().getName();
+ } else {
+ clientPrincipal =
+ clientAlias.getName();
+ }
}
if (serverPrincipal == null) {
- serverPrincipal =
- ticket.getServer().getName();
+ if (serverAlias == null) {
+ serverPrincipal =
+ ticket.getServer().getName();
+ } else {
+ serverPrincipal =
+ serverAlias.getName();
+ }
}
answer.add(credClass.cast(ticket));
}
--- a/src/java.security.jgss/share/classes/sun/security/krb5/Credentials.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/Credentials.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, 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
@@ -49,7 +49,9 @@
Ticket ticket;
PrincipalName client;
+ PrincipalName clientAlias;
PrincipalName server;
+ PrincipalName serverAlias;
EncryptionKey key;
TicketFlags flags;
KerberosTime authTime;
@@ -69,7 +71,9 @@
public Credentials(Ticket new_ticket,
PrincipalName new_client,
+ PrincipalName new_client_alias,
PrincipalName new_server,
+ PrincipalName new_server_alias,
EncryptionKey new_key,
TicketFlags new_flags,
KerberosTime authTime,
@@ -78,14 +82,17 @@
KerberosTime renewTill,
HostAddresses cAddr,
AuthorizationData authzData) {
- this(new_ticket, new_client, new_server, new_key, new_flags,
- authTime, new_startTime, new_endTime, renewTill, cAddr);
+ this(new_ticket, new_client, new_client_alias, new_server,
+ new_server_alias, new_key, new_flags, authTime,
+ new_startTime, new_endTime, renewTill, cAddr);
this.authzData = authzData;
}
public Credentials(Ticket new_ticket,
PrincipalName new_client,
+ PrincipalName new_client_alias,
PrincipalName new_server,
+ PrincipalName new_server_alias,
EncryptionKey new_key,
TicketFlags new_flags,
KerberosTime authTime,
@@ -95,7 +102,9 @@
HostAddresses cAddr) {
ticket = new_ticket;
client = new_client;
+ clientAlias = new_client_alias;
server = new_server;
+ serverAlias = new_server_alias;
key = new_key;
flags = new_flags;
this.authTime = authTime;
@@ -107,7 +116,9 @@
public Credentials(byte[] encoding,
String client,
+ String clientAlias,
String server,
+ String serverAlias,
byte[] keyBytes,
int keyType,
boolean[] flags,
@@ -118,7 +129,11 @@
InetAddress[] cAddrs) throws KrbException, IOException {
this(new Ticket(encoding),
new PrincipalName(client, PrincipalName.KRB_NT_PRINCIPAL),
+ (clientAlias == null? null : new PrincipalName(clientAlias,
+ PrincipalName.KRB_NT_PRINCIPAL)),
new PrincipalName(server, PrincipalName.KRB_NT_SRV_INST),
+ (serverAlias == null? null : new PrincipalName(serverAlias,
+ PrincipalName.KRB_NT_SRV_INST)),
new EncryptionKey(keyType, keyBytes),
(flags == null? null: new TicketFlags(flags)),
(authTime == null? null: new KerberosTime(authTime)),
@@ -143,10 +158,18 @@
return client;
}
+ public final PrincipalName getClientAlias() {
+ return clientAlias;
+ }
+
public final PrincipalName getServer() {
return server;
}
+ public final PrincipalName getServerAlias() {
+ return serverAlias;
+ }
+
public final EncryptionKey getSessionKey() {
return key;
}
@@ -262,6 +285,7 @@
return new KrbTgsReq(options,
this,
server,
+ serverAlias,
null, // from
null, // till
null, // rtime
@@ -484,7 +508,11 @@
public static void printDebug(Credentials c) {
System.out.println(">>> DEBUG: ----Credentials----");
System.out.println("\tclient: " + c.client.toString());
+ if (c.clientAlias != null)
+ System.out.println("\tclient alias: " + c.clientAlias.toString());
System.out.println("\tserver: " + c.server.toString());
+ if (c.serverAlias != null)
+ System.out.println("\tserver alias: " + c.serverAlias.toString());
System.out.println("\tticket: sname: " + c.ticket.sname.toString());
if (c.startTime != null) {
System.out.println("\tstartTime: " + c.startTime.getTime());
@@ -512,7 +540,11 @@
public String toString() {
StringBuilder sb = new StringBuilder("Credentials:");
sb.append( "\n client=").append(client);
+ if (clientAlias != null)
+ sb.append( "\n clientAlias=").append(clientAlias);
sb.append( "\n server=").append(server);
+ if (serverAlias != null)
+ sb.append( "\n serverAlias=").append(serverAlias);
if (authTime != null) {
sb.append("\n authTime=").append(authTime);
}
--- a/src/java.security.jgss/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -25,6 +25,8 @@
package sun.security.krb5;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KeyTab;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.PrincipalName;
@@ -39,4 +41,12 @@
*/
public sun.security.krb5.internal.ktab.KeyTab keyTabTakeSnapshot(
KeyTab ktab);
+
+ public KerberosPrincipal kerberosTicketGetClientAlias(KerberosTicket t);
+
+ public void kerberosTicketSetClientAlias(KerberosTicket t, KerberosPrincipal a);
+
+ public KerberosPrincipal kerberosTicketGetServerAlias(KerberosTicket t);
+
+ public void kerberosTicketSetServerAlias(KerberosTicket t, KerberosPrincipal a);
}
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbApReq.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbApReq.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, 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
@@ -363,7 +363,9 @@
creds = new Credentials(
apReqMessg.ticket,
authenticator.cname,
+ null,
apReqMessg.ticket.sname,
+ null,
enc_ticketPart.key,
enc_ticketPart.flags,
enc_ticketPart.authtime,
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsRep.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsRep.java Thu Jul 25 08:24:01 2019 -0400
@@ -118,7 +118,7 @@
"Cannot find key for type/kvno to decrypt AS REP - " +
EType.toString(encPartKeyType) + "/" + encPartKvno);
}
- decrypt(dkey, asReq);
+ decrypt(dkey, asReq, cname);
}
/**
@@ -136,7 +136,7 @@
password,
encPartKeyType,
PAData.getSaltAndParams(encPartKeyType, rep.pAData));
- decrypt(dkey, asReq);
+ decrypt(dkey, asReq, cname);
}
/**
@@ -144,7 +144,8 @@
* @param dkey the decryption key to use
* @param asReq the original AS-REQ sent, used to validate AS-REP
*/
- private void decrypt(EncryptionKey dkey, KrbAsReq asReq)
+ private void decrypt(EncryptionKey dkey, KrbAsReq asReq,
+ PrincipalName cname)
throws KrbException, Asn1Exception, IOException {
byte[] enc_as_rep_bytes = rep.encPart.decrypt(dkey,
KeyUsage.KU_ENC_AS_REP_PART);
@@ -157,10 +158,16 @@
ASReq req = asReq.getMessage();
check(true, req, rep, dkey);
+ PrincipalName clientAlias = cname;
+ if (clientAlias.equals(rep.cname))
+ clientAlias = null;
+
creds = new Credentials(
rep.ticket,
rep.cname,
+ clientAlias,
enc_part.sname,
+ null, // No server alias expected in a TGT
enc_part.key,
enc_part.flags,
enc_part.authtime,
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java Thu Jul 25 08:24:01 2019 -0400
@@ -68,6 +68,7 @@
// Common data for AS-REQ fields
private KDCOptions options;
private PrincipalName cname;
+ private PrincipalName refCname; // May be changed by referrals
private PrincipalName sname;
private KerberosTime from;
private KerberosTime till;
@@ -100,6 +101,7 @@
private void init(PrincipalName cname)
throws KrbException {
this.cname = cname;
+ this.refCname = cname;
state = State.INIT;
}
@@ -284,7 +286,7 @@
}
return new KrbAsReq(key,
options,
- cname,
+ refCname,
sname,
from,
till,
@@ -334,7 +336,7 @@
ReferralsState referralsState = new ReferralsState();
while (true) {
if (referralsState.refreshComm()) {
- comm = new KdcComm(cname.getRealmAsString());
+ comm = new KdcComm(refCname.getRealmAsString());
}
try {
req = build(pakey, referralsState);
@@ -384,7 +386,7 @@
ReferralsState() throws KrbException {
if (Config.DISABLE_REFERRALS) {
- if (cname.getNameType() == PrincipalName.KRB_NT_ENTERPRISE) {
+ if (refCname.getNameType() == PrincipalName.KRB_NT_ENTERPRISE) {
throw new KrbException("NT-ENTERPRISE principals only allowed" +
" when referrals are enabled.");
}
@@ -402,15 +404,15 @@
if (req.getMessage().reqBody.kdcOptions.get(KDCOptions.CANONICALIZE) &&
referredRealm != null && referredRealm.toString().length() > 0 &&
count < Config.MAX_REFERRALS) {
- cname = new PrincipalName(cname.getNameType(),
- cname.getNameStrings(), referredRealm);
+ refCname = new PrincipalName(refCname.getNameType(),
+ refCname.getNameStrings(), referredRealm);
refreshComm = true;
count++;
return true;
}
}
if (count < Config.MAX_REFERRALS &&
- cname.getNameType() != PrincipalName.KRB_NT_ENTERPRISE) {
+ refCname.getNameType() != PrincipalName.KRB_NT_ENTERPRISE) {
// Server may raise an error if CANONICALIZE is true.
// Try CANONICALIZE false.
enabled = false;
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbCred.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbCred.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, 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
@@ -76,7 +76,7 @@
options.set(KDCOptions.FORWARDABLE, true);
KrbTgsReq tgsReq = new KrbTgsReq(options, tgt, tgService,
- null, null, null, null,
+ null, null, null, null, null,
null, // No easy way to get addresses right
null, null, null);
credMessg = createMessage(tgsReq.sendAndGetCreds(), key);
@@ -152,7 +152,7 @@
+ " endtime=" + endtime
+ "renewTill=" + renewTill);
}
- creds = new Credentials(ticket, pname, sname, credInfoKey,
+ creds = new Credentials(ticket, pname, null, sname, null, credInfoKey,
flags, authtime, starttime, endtime, renewTill, caddr);
}
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsRep.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsRep.java Thu Jul 25 08:24:01 2019 -0400
@@ -86,9 +86,20 @@
check(false, req, rep, tgsReq.tgsReqKey);
+ PrincipalName serverAlias = tgsReq.getServerAlias();
+ if (serverAlias != null) {
+ PrincipalName repSname = enc_part.sname;
+ if (serverAlias.equals(repSname) ||
+ isReferralSname(repSname)) {
+ serverAlias = null;
+ }
+ }
+
this.creds = new Credentials(rep.ticket,
rep.cname,
+ tgsReq.getClientAlias(),
enc_part.sname,
+ serverAlias,
enc_part.key,
enc_part.flags,
enc_part.authtime,
@@ -111,4 +122,16 @@
sun.security.krb5.internal.ccache.Credentials setCredentials() {
return new sun.security.krb5.internal.ccache.Credentials(rep, secondTicket);
}
+
+ private static boolean isReferralSname(PrincipalName sname) {
+ if (sname != null) {
+ String[] snameStrings = sname.getNameStrings();
+ if (snameStrings.length == 2 &&
+ snameStrings[0].equals(
+ PrincipalName.TGS_DEFAULT_SRV_NAME)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsReq.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsReq.java Thu Jul 25 08:24:01 2019 -0400
@@ -45,7 +45,9 @@
public class KrbTgsReq {
private PrincipalName princName;
+ private PrincipalName clientAlias;
private PrincipalName servName;
+ private PrincipalName serverAlias;
private TGSReq tgsReqMessg;
private KerberosTime ctime;
private Ticket secondTicket = null;
@@ -59,13 +61,16 @@
// Used in CredentialsUtil
public KrbTgsReq(KDCOptions options, Credentials asCreds,
- PrincipalName cname, PrincipalName sname,
+ PrincipalName cname, PrincipalName clientAlias,
+ PrincipalName sname, PrincipalName serverAlias,
Ticket[] additionalTickets, PAData[] extraPAs)
throws KrbException, IOException {
this(options,
asCreds,
cname,
+ clientAlias,
sname,
+ serverAlias,
null, // KerberosTime from
null, // KerberosTime till
null, // KerberosTime rtime
@@ -82,6 +87,7 @@
KDCOptions options,
Credentials asCreds,
PrincipalName sname,
+ PrincipalName serverAlias,
KerberosTime from,
KerberosTime till,
KerberosTime rtime,
@@ -90,16 +96,18 @@
AuthorizationData authorizationData,
Ticket[] additionalTickets,
EncryptionKey subKey) throws KrbException, IOException {
- this(options, asCreds, asCreds.getClient(), sname,
- from, till, rtime, eTypes, addresses,
- authorizationData, additionalTickets, subKey, null);
+ this(options, asCreds, asCreds.getClient(), asCreds.getClientAlias(),
+ sname, serverAlias, from, till, rtime, eTypes,
+ addresses, authorizationData, additionalTickets, subKey, null);
}
private KrbTgsReq(
KDCOptions options,
Credentials asCreds,
PrincipalName cname,
+ PrincipalName clientAlias,
PrincipalName sname,
+ PrincipalName serverAlias,
KerberosTime from,
KerberosTime till,
KerberosTime rtime,
@@ -111,7 +119,9 @@
PAData[] extraPAs) throws KrbException, IOException {
princName = cname;
+ this.clientAlias = clientAlias;
servName = sname;
+ this.serverAlias = serverAlias;
ctime = KerberosTime.now();
// check if they are valid arguments. The optional fields
@@ -365,6 +375,14 @@
return secondTicket;
}
+ PrincipalName getClientAlias() {
+ return clientAlias;
+ }
+
+ PrincipalName getServerAlias() {
+ return serverAlias;
+ }
+
private static void debug(String message) {
// System.err.println(">>> KrbTgsReq: " + message);
}
--- a/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java Thu Jul 25 08:24:01 2019 -0400
@@ -564,7 +564,9 @@
for (int i = 0; i < nameStrings.length; i++) {
if (i > 0)
str.append("/");
- str.append(nameStrings[i]);
+ String n = nameStrings[i];
+ n = n.replace("@", "\\@");
+ str.append(n);
}
str.append("@");
str.append(nameRealm.toString());
--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/CredentialsUtil.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/CredentialsUtil.java Thu Jul 25 08:24:01 2019 -0400
@@ -284,8 +284,9 @@
// Try CANONICALIZE false.
}
}
- return serviceCredsSingle(options, asCreds,
- cname, sname, additionalTickets, extraPAs);
+ return serviceCredsSingle(options, asCreds, cname,
+ asCreds.getClientAlias(), sname, sname, additionalTickets,
+ extraPAs);
}
/*
@@ -300,26 +301,29 @@
options = new KDCOptions(options.toBooleanArray());
options.set(KDCOptions.CANONICALIZE, true);
PrincipalName cSname = sname;
+ PrincipalName refSname = sname; // May change with referrals
Credentials creds = null;
boolean isReferral = false;
List<String> referrals = new LinkedList<>();
+ PrincipalName clientAlias = asCreds.getClientAlias();
while (referrals.size() <= Config.MAX_REFERRALS) {
ReferralsCache.ReferralCacheEntry ref =
- ReferralsCache.get(sname, cSname.getRealmString());
+ ReferralsCache.get(cname, sname, refSname.getRealmString());
String toRealm = null;
if (ref == null) {
- creds = serviceCredsSingle(options, asCreds,
- cname, cSname, additionalTickets, extraPAs);
+ creds = serviceCredsSingle(options, asCreds, cname,
+ clientAlias, refSname, cSname, additionalTickets,
+ extraPAs);
PrincipalName server = creds.getServer();
- if (!cSname.equals(server)) {
+ if (!refSname.equals(server)) {
String[] serverNameStrings = server.getNameStrings();
if (serverNameStrings.length == 2 &&
serverNameStrings[0].equals(
PrincipalName.TGS_DEFAULT_SRV_NAME) &&
- !cSname.getRealmAsString().equals(serverNameStrings[1])) {
+ !refSname.getRealmAsString().equals(serverNameStrings[1])) {
// Server Name (sname) has the following format:
// krbtgt/TO-REALM.COM@FROM-REALM.COM
- ReferralsCache.put(sname, server.getRealmString(),
+ ReferralsCache.put(cname, sname, server.getRealmString(),
serverNameStrings[1], creds);
toRealm = serverNameStrings[1];
isReferral = true;
@@ -336,8 +340,8 @@
// Referrals loop detected
return null;
}
- cSname = new PrincipalName(cSname.getNameString(),
- cSname.getNameType(), toRealm);
+ refSname = new PrincipalName(refSname.getNameString(),
+ refSname.getNameType(), toRealm);
referrals.add(toRealm);
isReferral = false;
continue;
@@ -356,14 +360,15 @@
*/
private static Credentials serviceCredsSingle(
KDCOptions options, Credentials asCreds,
- PrincipalName cname, PrincipalName sname,
+ PrincipalName cname, PrincipalName clientAlias,
+ PrincipalName refSname, PrincipalName sname,
Ticket[] additionalTickets, PAData[] extraPAs)
throws KrbException, IOException {
Credentials theCreds = null;
boolean[] okAsDelegate = new boolean[]{true};
String[] serverAsCredsNames = asCreds.getServer().getNameStrings();
String tgtRealm = serverAsCredsNames[1];
- String serviceRealm = sname.getRealmString();
+ String serviceRealm = refSname.getRealmString();
if (!serviceRealm.equals(tgtRealm)) {
// This is a cross-realm service request
if (DEBUG) {
@@ -390,8 +395,8 @@
System.out.println(">>> Credentials serviceCredsSingle:" +
" same realm");
}
- KrbTgsReq req = new KrbTgsReq(options, asCreds,
- cname, sname, additionalTickets, extraPAs);
+ KrbTgsReq req = new KrbTgsReq(options, asCreds, cname, clientAlias,
+ refSname, sname, additionalTickets, extraPAs);
theCreds = req.sendAndGetCreds();
if (theCreds != null) {
if (DEBUG) {
--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java Thu Jul 25 08:24:01 2019 -0400
@@ -139,7 +139,7 @@
sTime = new_sTime;
suSec = new_suSec;
errorCode = new_errorCode;
- crealm = new_cname.getRealm();
+ crealm = new_cname != null ? new_cname.getRealm() : null;
cname = new_cname;
sname = new_sname;
eText = new_eText;
@@ -168,7 +168,7 @@
sTime = new_sTime;
suSec = new_suSec;
errorCode = new_errorCode;
- crealm = new_cname.getRealm();
+ crealm = new_cname != null ? new_cname.getRealm() : null;
cname = new_cname;
sname = new_sname;
eText = new_eText;
--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ReferralsCache.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/ReferralsCache.java Thu Jul 25 08:24:01 2019 -0400
@@ -45,8 +45,27 @@
*/
final class ReferralsCache {
- private static Map<PrincipalName, Map<String, ReferralCacheEntry>> referralsMap =
- new HashMap<>();
+ private static Map<ReferralCacheKey, Map<String, ReferralCacheEntry>>
+ referralsMap = new HashMap<>();
+
+ static private final class ReferralCacheKey {
+ private PrincipalName cname;
+ private PrincipalName sname;
+ ReferralCacheKey (PrincipalName cname, PrincipalName sname) {
+ this.cname = cname;
+ this.sname = sname;
+ }
+ public boolean equals(Object other) {
+ if (!(other instanceof ReferralCacheKey))
+ return false;
+ ReferralCacheKey that = (ReferralCacheKey)other;
+ return cname.equals(that.cname) &&
+ sname.equals(that.sname);
+ }
+ public int hashCode() {
+ return cname.hashCode() + sname.hashCode();
+ }
+ }
static final class ReferralCacheEntry {
private final Credentials creds;
@@ -64,8 +83,9 @@
}
/*
- * Add a new referral entry to the cache, including: service principal,
- * source KDC realm, destination KDC realm and referral TGT.
+ * Add a new referral entry to the cache, including: client principal,
+ * service principal, source KDC realm, destination KDC realm and
+ * referral TGT.
*
* If a loop is generated when adding the new referral, the first hop is
* automatically removed. For example, let's assume that adding a
@@ -73,16 +93,17 @@
* REALM-1.COM -> REALM-2.COM -> REALM-3.COM -> REALM-1.COM. Then,
* REALM-1.COM -> REALM-2.COM referral entry is removed from the cache.
*/
- static synchronized void put(PrincipalName service,
+ static synchronized void put(PrincipalName cname, PrincipalName service,
String fromRealm, String toRealm, Credentials creds) {
- pruneExpired(service);
+ ReferralCacheKey k = new ReferralCacheKey(cname, service);
+ pruneExpired(k);
if (creds.getEndTime().before(new Date())) {
return;
}
- Map<String, ReferralCacheEntry> entries = referralsMap.get(service);
+ Map<String, ReferralCacheEntry> entries = referralsMap.get(k);
if (entries == null) {
entries = new HashMap<String, ReferralCacheEntry>();
- referralsMap.put(service, entries);
+ referralsMap.put(k, entries);
}
entries.remove(fromRealm);
ReferralCacheEntry newEntry = new ReferralCacheEntry(creds, toRealm);
@@ -103,13 +124,14 @@
}
/*
- * Obtain a referral entry from the cache given a service principal and a
- * source KDC realm.
+ * Obtain a referral entry from the cache given a client principal,
+ * service principal and a source KDC realm.
*/
- static synchronized ReferralCacheEntry get(PrincipalName service,
- String fromRealm) {
- pruneExpired(service);
- Map<String, ReferralCacheEntry> entries = referralsMap.get(service);
+ static synchronized ReferralCacheEntry get(PrincipalName cname,
+ PrincipalName service, String fromRealm) {
+ ReferralCacheKey k = new ReferralCacheKey(cname, service);
+ pruneExpired(k);
+ Map<String, ReferralCacheEntry> entries = referralsMap.get(k);
if (entries != null) {
ReferralCacheEntry toRef = entries.get(fromRealm);
if (toRef != null) {
@@ -122,9 +144,9 @@
/*
* Remove referral entries from the cache when referral TGTs expire.
*/
- private static void pruneExpired(PrincipalName service) {
+ private static void pruneExpired(ReferralCacheKey k) {
Date now = new Date();
- Map<String, ReferralCacheEntry> entries = referralsMap.get(service);
+ Map<String, ReferralCacheEntry> entries = referralsMap.get(k);
if (entries != null) {
for (Entry<String, ReferralCacheEntry> mapEntry :
entries.entrySet()) {
--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/Credentials.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/Credentials.java Thu Jul 25 08:24:01 2019 -0400
@@ -180,8 +180,9 @@
// is most likely to be the one in Authenticator in PA-TGS-REQ encoded
// in TGS-REQ, therefore only stored with a service ticket. Currently
// in Java, we only reads TGTs.
- return new sun.security.krb5.Credentials(ticket,
- cname, sname, key, flags, authtime, starttime, endtime, renewTill, caddr);
+ return new sun.security.krb5.Credentials(ticket, cname, null, sname,
+ null, key, flags, authtime, starttime, endtime, renewTill,
+ caddr);
}
public KerberosTime getStartTime() {
--- a/src/java.security.jgss/windows/native/libw2k_lsa_auth/NativeCreds.c Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.security.jgss/windows/native/libw2k_lsa_auth/NativeCreds.c Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, 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
@@ -406,6 +406,8 @@
"(Lsun/security/krb5/internal/Ticket;"
"Lsun/security/krb5/PrincipalName;"
"Lsun/security/krb5/PrincipalName;"
+ "Lsun/security/krb5/PrincipalName;"
+ "Lsun/security/krb5/PrincipalName;"
"Lsun/security/krb5/EncryptionKey;"
"Lsun/security/krb5/internal/TicketFlags;"
"Lsun/security/krb5/internal/KerberosTime;"
@@ -667,7 +669,9 @@
krbcredsConstructor,
ticket,
clientPrincipal,
+ NULL,
targetPrincipal,
+ NULL,
encryptionKey,
ticketFlags,
authTime, // mdu
--- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
@@ -42,6 +42,7 @@
import java.util.StringTokenizer;
import javax.xml.XMLConstants;
import javax.xml.catalog.CatalogFeatures;
+import jdk.xml.internal.ErrorHandlerProxy;
import jdk.xml.internal.JdkXmlFeatures;
import jdk.xml.internal.JdkXmlUtils;
import jdk.xml.internal.SecuritySupport;
@@ -61,7 +62,7 @@
* @author G. Todd Miller
* @author Morten Jorgensen
* @author Erwin Bolwidt <ejb@klomp.org>
- * @LastModified: Nov 2017
+ * @LastModified: July 2019
*/
public class Parser implements Constants, ContentHandler {
@@ -98,9 +99,13 @@
private boolean _overrideDefaultParser;
- public Parser(XSLTC xsltc, boolean useOverrideDefaultParser) {
+ // flag indicates whether there's an user's ErrorListener
+ private boolean _hasUserErrListener;
+
+ public Parser(XSLTC xsltc, boolean useOverrideDefaultParser, boolean hasUserErrListener) {
_xsltc = xsltc;
_overrideDefaultParser = useOverrideDefaultParser;
+ _hasUserErrListener = hasUserErrListener;
}
public void init() {
@@ -426,6 +431,11 @@
try {
// Parse the input document and build the abstract syntax tree
reader.setContentHandler(this);
+ if (_hasUserErrListener) {
+ // Set a ErrorHandler proxy to pass any parsing error on to be handled
+ // by the user's ErrorListener
+ reader.setErrorHandler(new ErrorHandlerProxy());
+ }
reader.parse(input);
// Find the start of the stylesheet within the tree
return getStylesheet(_root);
--- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
@@ -58,7 +58,7 @@
* @author G. Todd Miller
* @author Morten Jorgensen
* @author John Howard (johnh@schemasoft.com)
- * @LastModified: Oct 2017
+ * @LastModified: July 2019
*/
public final class XSLTC {
@@ -175,10 +175,10 @@
/**
* XSLTC compiler constructor
*/
- public XSLTC(JdkXmlFeatures featureManager) {
+ public XSLTC(JdkXmlFeatures featureManager, boolean hasListener) {
_overrideDefaultParser = featureManager.getFeature(
JdkXmlFeatures.XmlFeature.JDK_OVERRIDE_PARSER);
- _parser = new Parser(this, _overrideDefaultParser);
+ _parser = new Parser(this, _overrideDefaultParser, hasListener);
_xmlFeatures = featureManager;
_extensionClassLoader = null;
_externalExtensionFunctions = new HashMap<>();
--- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesHandlerImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesHandlerImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
@@ -47,6 +47,7 @@
* Implementation of a JAXP1.1 TemplatesHandler
* @author Morten Jorgensen
* @author Santiago Pericas-Geertsen
+ * @LastModified: July 2019
*/
public class TemplatesHandlerImpl
implements ContentHandler, TemplatesHandler, SourceLoader
@@ -91,14 +92,14 @@
/**
* Default constructor
*/
- protected TemplatesHandlerImpl(int indentNumber,
- TransformerFactoryImpl tfactory)
+ protected TemplatesHandlerImpl(int indentNumber, TransformerFactoryImpl tfactory,
+ boolean hasUserErrListener)
{
_indentNumber = indentNumber;
_tfactory = tfactory;
// Instantiate XSLTC and get reference to parser object
- XSLTC xsltc = new XSLTC(tfactory.getJdkXmlFeatures());
+ XSLTC xsltc = new XSLTC(tfactory.getJdkXmlFeatures(), hasUserErrListener);
if (tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING))
xsltc.setSecureProcessing(true);
--- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
@@ -84,7 +84,7 @@
* @author G. Todd Miller
* @author Morten Jorgensen
* @author Santiago Pericas-Geertsen
- * @LastModified: July 2018
+ * @LastModified: July 2019
*/
public class TransformerFactoryImpl
extends SAXTransformerFactory implements SourceLoader, ErrorListener
@@ -107,6 +107,9 @@
*/
private ErrorListener _errorListener = this;
+ // flag indicating whether there's an user's ErrorListener
+ private boolean _hasUserErrListener;
+
/**
* This URIResolver is passed to all created Templates and Transformers
*/
@@ -297,6 +300,7 @@
"TransformerFactory");
throw new IllegalArgumentException(err.toString());
}
+ _hasUserErrListener = true;
_errorListener = listener;
}
@@ -946,7 +950,7 @@
}
// Create and initialize a stylesheet compiler
- final XSLTC xsltc = new XSLTC(_xmlFeatures);
+ final XSLTC xsltc = new XSLTC(_xmlFeatures, _hasUserErrListener);
if (_debug) xsltc.setDebug(true);
if (_enableInlining)
xsltc.setTemplateInlining(true);
@@ -1104,7 +1108,7 @@
// through the factory instance
buildCatalogFeatures();
final TemplatesHandlerImpl handler =
- new TemplatesHandlerImpl(_indentNumber, this);
+ new TemplatesHandlerImpl(_indentNumber, this, _hasUserErrListener);
if (_uriResolver != null) {
handler.setURIResolver(_uriResolver);
}
--- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/identity/XPathMatcher.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/identity/XPathMatcher.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
@@ -37,6 +37,7 @@
* @xerces.internal
*
* @author Andy Clark, IBM
+ * @LastModified: July 2019
*
*/
public class XPathMatcher {
@@ -88,25 +89,25 @@
//
/** XPath location path. */
- private XPath.LocationPath[] fLocationPaths;
+ private final XPath.LocationPath[] fLocationPaths;
/** True if XPath has been matched. */
- private int[] fMatched;
+ private final int[] fMatched;
/** The matching string. */
protected Object fMatchedString;
/** Integer stack of step indexes. */
- private IntStack[] fStepIndexes;
+ private final IntStack[] fStepIndexes;
/** Current step. */
- private int[] fCurrentStep;
+ private final int[] fCurrentStep;
/**
* No match depth. The value of this field will be zero while
* matching is successful for the given xpath expression.
*/
- private int [] fNoMatchDepth;
+ private final int [] fNoMatchDepth;
final QName fQName = new QName();
@@ -207,7 +208,7 @@
*
* @throws SAXException Thrown by handler to signal an error.
*/
- public void startElement(QName element, XMLAttributes attributes){
+ public void startElement(QName element, XMLAttributes attributes) {
if (DEBUG_METHODS2) {
System.out.println(toString()+"#startElement("+
"element={"+element+"},"+
@@ -215,7 +216,7 @@
")");
}
- for(int i = 0; i < fLocationPaths.length; i++) {
+ for (int i = 0; i < fLocationPaths.length; i++) {
// push context
int startStep = fCurrentStep[i];
fStepIndexes[i].push(startStep);
@@ -284,18 +285,16 @@
if (DEBUG_MATCH) {
System.out.println(toString()+" [CHILD] before");
}
- if (nodeTest.type == XPath.NodeTest.QNAME) {
- if (!nodeTest.name.equals(element)) {
- if(fCurrentStep[i] > descendantStep) {
- fCurrentStep[i] = descendantStep;
- continue;
- }
- fNoMatchDepth[i]++;
- if (DEBUG_MATCH) {
- System.out.println(toString()+" [CHILD] after NO MATCH");
- }
+ if (!matches(nodeTest, element)) {
+ if (fCurrentStep[i] > descendantStep) {
+ fCurrentStep[i] = descendantStep;
continue;
}
+ fNoMatchDepth[i]++;
+ if (DEBUG_MATCH) {
+ System.out.println(toString()+" [CHILD] after NO MATCH");
+ }
+ continue;
}
fCurrentStep[i]++;
if (DEBUG_MATCH) {
@@ -303,10 +302,11 @@
}
}
if (fCurrentStep[i] == steps.length) {
- if(sawDescendant) {
+ if (sawDescendant) {
fCurrentStep[i] = descendantStep;
fMatched[i] = MATCHED_DESCENDANT;
- } else {
+ }
+ else {
fMatched[i] = MATCHED;
}
continue;
@@ -324,8 +324,7 @@
for (int aIndex = 0; aIndex < attrCount; aIndex++) {
attributes.getName(aIndex, fQName);
- if (nodeTest.type != XPath.NodeTest.QNAME ||
- nodeTest.name.equals(fQName)) {
+ if (matches(nodeTest, fQName)) {
fCurrentStep[i]++;
if (fCurrentStep[i] == steps.length) {
fMatched[i] = MATCHED_ATTRIBUTE;
@@ -384,7 +383,7 @@
"element={"+element+"},"+
")");
}
- for(int i = 0; i<fLocationPaths.length; i++) {
+ for (int i = 0; i < fLocationPaths.length; i++) {
// go back a step
fCurrentStep[i] = fStepIndexes[i].pop();
@@ -395,10 +394,13 @@
// signal match, if appropriate
else {
- int j=0;
- for(; j<i && ((fMatched[j] & MATCHED) != MATCHED); j++);
- if ((j<i) || (fMatched[j] == 0) ||
- ((fMatched[j] & MATCHED_ATTRIBUTE) == MATCHED_ATTRIBUTE)) {
+ int j = 0;
+ for(; j < i && ((fMatched[j] & MATCHED) != MATCHED); j++);
+ if ((j < i) || (fMatched[j] == 0)) {
+ continue;
+ }
+ if ((fMatched[j] & MATCHED_ATTRIBUTE) == MATCHED_ATTRIBUTE) {
+ fMatched[i] = 0;
continue;
}
// only certain kinds of matchers actually
@@ -476,6 +478,18 @@
return str.toString();
} // normalize(String):String
+ /** Returns true if the given QName matches the node test. **/
+ private static boolean matches(XPath.NodeTest nodeTest, QName value) {
+ if (nodeTest.type == XPath.NodeTest.QNAME) {
+ return nodeTest.name.equals(value);
+ }
+ if (nodeTest.type == XPath.NodeTest.NAMESPACE) {
+ return nodeTest.name.uri == value.uri;
+ }
+ // XPath.NodeTest.WILDCARD
+ return true;
+ } // matches(XPath.NodeTest,QName):boolean
+
//
// MAIN
//
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.xml/share/classes/jdk/xml/internal/ErrorHandlerProxy.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019, 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 jdk.xml.internal;
+
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Implements an ErrorHandler that simply passes on the Exception.
+ */
+public class ErrorHandlerProxy implements ErrorHandler {
+
+ @Override
+ public void warning(SAXParseException exception) throws SAXException {
+ throw exception;
+ }
+
+ @Override
+ public void error(SAXParseException exception) throws SAXException {
+ throw exception;
+ }
+
+ @Override
+ public void fatalError(SAXParseException exception) throws SAXException {
+ throw exception;
+ }
+}
--- a/src/jdk.attach/aix/classes/sun/tools/attach/VirtualMachineImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.attach/aix/classes/sun/tools/attach/VirtualMachineImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2015, 2018, SAP SE. All rights reserved.
+ * Copyright (c) 2015, 2019, SAP SE. 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
@@ -254,8 +254,12 @@
return VirtualMachineImpl.read(s, bs, off, len);
}
- public void close() throws IOException {
- VirtualMachineImpl.close(s);
+ public synchronized void close() throws IOException {
+ if (s != -1) {
+ int toClose = s;
+ s = -1;
+ VirtualMachineImpl.close(toClose);
+ }
}
}
--- a/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -260,12 +260,11 @@
return VirtualMachineImpl.read(s, bs, off, len);
}
- public void close() throws IOException {
- synchronized (this) {
- if (s != -1) {
- VirtualMachineImpl.close(s);
- s = -1;
- }
+ public synchronized void close() throws IOException {
+ if (s != -1) {
+ int toClose = s;
+ s = -1;
+ VirtualMachineImpl.close(toClose);
}
}
}
--- a/src/jdk.attach/macosx/classes/sun/tools/attach/VirtualMachineImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.attach/macosx/classes/sun/tools/attach/VirtualMachineImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -256,8 +256,12 @@
return VirtualMachineImpl.read(s, bs, off, len);
}
- public void close() throws IOException {
- VirtualMachineImpl.close(s);
+ public synchronized void close() throws IOException {
+ if (s != -1) {
+ int toClose = s;
+ s = -1;
+ VirtualMachineImpl.close(toClose);
+ }
}
}
--- a/src/jdk.attach/solaris/classes/sun/tools/attach/VirtualMachineImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.attach/solaris/classes/sun/tools/attach/VirtualMachineImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -204,8 +204,12 @@
return VirtualMachineImpl.read(s, bs, off, len);
}
- public void close() throws IOException {
- VirtualMachineImpl.close(s);
+ public synchronized void close() throws IOException {
+ if (s != -1) {
+ int toClose = s;
+ s = -1;
+ VirtualMachineImpl.close(toClose);
+ }
}
}
--- a/src/jdk.attach/windows/classes/sun/tools/attach/VirtualMachineImpl.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.attach/windows/classes/sun/tools/attach/VirtualMachineImpl.java Thu Jul 25 08:24:01 2019 -0400
@@ -169,10 +169,11 @@
return VirtualMachineImpl.readPipe(hPipe, bs, off, len);
}
- public void close() throws IOException {
+ public synchronized void close() throws IOException {
if (hPipe != -1) {
- VirtualMachineImpl.closePipe(hPipe);
+ long toClose = hPipe;
hPipe = -1;
+ VirtualMachineImpl.closePipe(toClose);
}
}
}
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -109,8 +109,10 @@
import com.sun.tools.javac.resources.CompilerProperties.Warnings;
import com.sun.tools.javac.tree.DCTree;
import com.sun.tools.javac.tree.DCTree.DCBlockTag;
+import com.sun.tools.javac.tree.DCTree.DCComment;
import com.sun.tools.javac.tree.DCTree.DCDocComment;
import com.sun.tools.javac.tree.DCTree.DCEndPosTree;
+import com.sun.tools.javac.tree.DCTree.DCEntity;
import com.sun.tools.javac.tree.DCTree.DCErroneous;
import com.sun.tools.javac.tree.DCTree.DCIdentifier;
import com.sun.tools.javac.tree.DCTree.DCParam;
@@ -302,6 +304,14 @@
return dcComment.comment.getSourcePos(block.pos + block.getTagName().length() + 1);
}
+ case ENTITY: {
+ DCEntity endEl = (DCEntity) tree;
+ return dcComment.comment.getSourcePos(endEl.pos + (endEl.name != names.error ? endEl.name.length() : 0) + 2);
+ }
+ case COMMENT: {
+ DCComment endEl = (DCComment) tree;
+ return dcComment.comment.getSourcePos(endEl.pos + endEl.body.length());
+ }
default:
DocTree last = getLastChild(tree);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java Thu Jul 25 08:24:01 2019 -0400
@@ -930,7 +930,7 @@
skipWhitespace();
if (ch == '>') {
nextChar();
- return m.at(p).newEndElementTree(name);
+ return m.at(p).newEndElementTree(name).setEndPos(bp);
}
}
} else if (ch == '!') {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java Thu Jul 25 08:24:01 2019 -0400
@@ -99,6 +99,10 @@
*/
protected boolean shouldTranslateEscapes;
+ /** Has the string broken escapes?
+ */
+ protected boolean hasBrokenEscapes;
+
protected ScannerFactory fac;
// The set of lint options currently in effect. It is initialized
@@ -261,6 +265,7 @@
case '\\':
reader.putChar(true); break;
default:
+ hasBrokenEscapes = true;
lexError(reader.bp, Errors.IllegalEscChar);
}
}
@@ -426,6 +431,7 @@
// Clear flags.
shouldStripIndent = false;
shouldTranslateEscapes = false;
+ hasBrokenEscapes = false;
// Check if text block string methods are present.
boolean hasTextBlockSupport = TextBlockSupport.hasSupport();
// Track the end of first line for error recovery.
@@ -1038,7 +1044,7 @@
string = TextBlockSupport.stripIndent(string);
}
// Translate escape sequences if present.
- if (shouldTranslateEscapes) {
+ if (shouldTranslateEscapes && !hasBrokenEscapes) {
string = TextBlockSupport.translateEscapes(string);
}
// Build string token.
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -326,7 +326,7 @@
}
}
- public static class DCEndElement extends DCTree implements EndElementTree {
+ public static class DCEndElement extends DCEndPosTree<DCStartElement> implements EndElementTree {
public final Name name;
DCEndElement(Name name) {
--- a/src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSigner.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSigner.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -671,26 +671,18 @@
throw new AssertionError(asae);
}
- PrintStream ps = new PrintStream(os);
- ZipOutputStream zos = new ZipOutputStream(ps);
+ ZipOutputStream zos = new ZipOutputStream(os);
Manifest manifest = new Manifest();
- Map<String, Attributes> mfEntries = manifest.getEntries();
-
- // The Attributes of manifest before updating
- Attributes oldAttr = null;
-
- boolean mfModified = false;
- boolean mfCreated = false;
byte[] mfRawBytes = null;
// Check if manifest exists
- ZipEntry mfFile;
- if ((mfFile = getManifestFile(zipFile)) != null) {
+ ZipEntry mfFile = getManifestFile(zipFile);
+ boolean mfCreated = mfFile == null;
+ if (!mfCreated) {
// Manifest exists. Read its raw bytes.
mfRawBytes = zipFile.getInputStream(mfFile).readAllBytes();
manifest.read(new ByteArrayInputStream(mfRawBytes));
- oldAttr = (Attributes) (manifest.getMainAttributes().clone());
} else {
// Create new manifest
Attributes mattr = manifest.getMainAttributes();
@@ -701,7 +693,6 @@
mattr.putValue("Created-By", jdkVersion + " (" + javaVendor
+ ")");
mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
- mfCreated = true;
}
/*
@@ -728,8 +719,12 @@
// out first
mfFiles.addElement(ze);
- if (SignatureFileVerifier.isBlockOrSF(
- ze.getName().toUpperCase(Locale.ENGLISH))) {
+ String zeNameUp = ze.getName().toUpperCase(Locale.ENGLISH);
+ if (SignatureFileVerifier.isBlockOrSF(zeNameUp)
+ // no need to preserve binary manifest portions
+ // if the only existing signature will be replaced
+ && !zeNameUp.startsWith(SignatureFile
+ .getBaseSignatureFilesName(signerName))) {
wasSigned = true;
}
@@ -742,55 +737,69 @@
if (manifest.getAttributes(ze.getName()) != null) {
// jar entry is contained in manifest, check and
// possibly update its digest attributes
- if (updateDigests(ze, zipFile, digests,
- manifest)) {
- mfModified = true;
- }
+ updateDigests(ze, zipFile, digests, manifest);
} else if (!ze.isDirectory()) {
// Add entry to manifest
Attributes attrs = getDigestAttributes(ze, zipFile, digests);
- mfEntries.put(ze.getName(), attrs);
- mfModified = true;
+ manifest.getEntries().put(ze.getName(), attrs);
}
}
- // Recalculate the manifest raw bytes if necessary
- if (mfModified) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ /*
+ * Note:
+ *
+ * The Attributes object is based on HashMap and can handle
+ * continuation lines. Therefore, even if the contents are not changed
+ * (in a Map view), the bytes that it write() may be different from
+ * the original bytes that it read() from. Since the signature is
+ * based on raw bytes, we must retain the exact bytes.
+ */
+ boolean mfModified;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ if (mfCreated || !wasSigned) {
+ mfModified = true;
manifest.write(baos);
- if (wasSigned) {
- byte[] newBytes = baos.toByteArray();
- if (mfRawBytes != null
- && oldAttr.equals(manifest.getMainAttributes())) {
+ mfRawBytes = baos.toByteArray();
+ } else {
- /*
- * Note:
- *
- * The Attributes object is based on HashMap and can handle
- * continuation columns. Therefore, even if the contents are
- * not changed (in a Map view), the bytes that it write()
- * may be different from the original bytes that it read()
- * from. Since the signature on the main attributes is based
- * on raw bytes, we must retain the exact bytes.
- */
+ // the manifest before updating
+ Manifest oldManifest = new Manifest(
+ new ByteArrayInputStream(mfRawBytes));
+ mfModified = !oldManifest.equals(manifest);
+ if (!mfModified) {
+ // leave whole manifest (mfRawBytes) unmodified
+ } else {
+ // reproduce the manifest raw bytes for unmodified sections
+ manifest.write(baos);
+ byte[] mfNewRawBytes = baos.toByteArray();
+ baos.reset();
+
+ ManifestDigester oldMd = new ManifestDigester(mfRawBytes);
+ ManifestDigester newMd = new ManifestDigester(mfNewRawBytes);
- int newPos = findHeaderEnd(newBytes);
- int oldPos = findHeaderEnd(mfRawBytes);
+ // main attributes
+ if (manifest.getMainAttributes().equals(
+ oldManifest.getMainAttributes())
+ && (manifest.getEntries().isEmpty() ||
+ oldMd.getMainAttsEntry().isProperlyDelimited())) {
+ oldMd.getMainAttsEntry().reproduceRaw(baos);
+ } else {
+ newMd.getMainAttsEntry().reproduceRaw(baos);
+ }
- if (newPos == oldPos) {
- System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos);
+ // individual sections
+ for (Map.Entry<String,Attributes> entry :
+ manifest.getEntries().entrySet()) {
+ String sectionName = entry.getKey();
+ Attributes entryAtts = entry.getValue();
+ if (entryAtts.equals(oldManifest.getAttributes(sectionName))
+ && oldMd.get(sectionName).isProperlyDelimited()) {
+ oldMd.get(sectionName).reproduceRaw(baos);
} else {
- // cat oldHead newTail > newBytes
- byte[] lastBytes = new byte[oldPos +
- newBytes.length - newPos];
- System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos);
- System.arraycopy(newBytes, newPos, lastBytes, oldPos,
- newBytes.length - newPos);
- newBytes = lastBytes;
+ newMd.get(sectionName).reproduceRaw(baos);
}
}
- mfRawBytes = newBytes;
- } else {
+
mfRawBytes = baos.toByteArray();
}
}
@@ -801,13 +810,12 @@
mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
}
if (handler != null) {
- if (mfCreated) {
+ if (mfCreated || !mfModified) {
handler.accept("adding", mfFile.getName());
- } else if (mfModified) {
+ } else {
handler.accept("updating", mfFile.getName());
}
}
-
zos.putNextEntry(mfFile);
zos.write(mfRawBytes);
@@ -826,9 +834,8 @@
}
signer.initSign(privateKey);
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.reset();
sf.write(baos);
-
byte[] content = baos.toByteArray();
signer.update(content);
@@ -889,6 +896,14 @@
if (!ze.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)
&& !ze.getName().equalsIgnoreCase(sfFilename)
&& !ze.getName().equalsIgnoreCase(bkFilename)) {
+ if (ze.getName().startsWith(SignatureFile
+ .getBaseSignatureFilesName(signerName))
+ && SignatureFileVerifier.isBlockOrSF(ze.getName())) {
+ if (handler != null) {
+ handler.accept("updating", ze.getName());
+ }
+ continue;
+ }
if (handler != null) {
if (manifest.getAttributes(ze.getName()) != null) {
handler.accept("signing", ze.getName());
@@ -942,11 +957,9 @@
}
}
- private boolean updateDigests(ZipEntry ze, ZipFile zf,
+ private void updateDigests(ZipEntry ze, ZipFile zf,
MessageDigest[] digests,
Manifest mf) throws IOException {
- boolean update = false;
-
Attributes attrs = mf.getAttributes(ze.getName());
String[] base64Digests = getDigests(ze, zf, digests);
@@ -976,19 +989,9 @@
if (name == null) {
name = digests[i].getAlgorithm() + "-Digest";
- attrs.putValue(name, base64Digests[i]);
- update = true;
- } else {
- // compare digests, and replace the one in the manifest
- // if they are different
- String mfDigest = attrs.getValue(name);
- if (!mfDigest.equalsIgnoreCase(base64Digests[i])) {
- attrs.putValue(name, base64Digests[i]);
- update = true;
- }
}
+ attrs.putValue(name, base64Digests[i]);
}
- return update;
}
private Attributes getDigestAttributes(
@@ -1051,30 +1054,6 @@
return base64Digests;
}
- @SuppressWarnings("fallthrough")
- private int findHeaderEnd(byte[] bs) {
- // Initial state true to deal with empty header
- boolean newline = true; // just met a newline
- int len = bs.length;
- for (int i = 0; i < len; i++) {
- switch (bs[i]) {
- case '\r':
- if (i < len - 1 && bs[i + 1] == '\n') i++;
- // fallthrough
- case '\n':
- if (newline) return i + 1; //+1 to get length
- newline = true;
- break;
- default:
- newline = false;
- }
- }
- // If header end is not found, it means the MANIFEST.MF has only
- // the main attributes section and it does not end with 2 newlines.
- // Returns the whole length so that it can be completely replaced.
- return len;
- }
-
/*
* Try to load the specified signing mechanism.
* The URL class loader is used.
@@ -1145,14 +1124,12 @@
}
// create digest of the manifest main attributes
- ManifestDigester.Entry mde =
- md.get(ManifestDigester.MF_MAIN_ATTRS, false);
+ ManifestDigester.Entry mde = md.getMainAttsEntry(false);
if (mde != null) {
- for (MessageDigest digest: digests) {
- mattr.putValue(digest.getAlgorithm() +
- "-Digest-" + ManifestDigester.MF_MAIN_ATTRS,
- Base64.getEncoder().encodeToString(
- mde.digest(digest)));
+ for (MessageDigest digest : digests) {
+ mattr.putValue(digest.getAlgorithm() + "-Digest-" +
+ ManifestDigester.MF_MAIN_ATTRS,
+ Base64.getEncoder().encodeToString(mde.digest(digest)));
}
} else {
throw new IllegalStateException
@@ -1181,15 +1158,19 @@
sf.write(out);
}
+ private static String getBaseSignatureFilesName(String baseName) {
+ return "META-INF/" + baseName + ".";
+ }
+
// get .SF file name
public String getMetaName() {
- return "META-INF/" + baseName + ".SF";
+ return getBaseSignatureFilesName(baseName) + "SF";
}
// get .DSA (or .DSA, .EC) file name
public String getBlockName(PrivateKey privateKey) {
String keyAlgorithm = privateKey.getAlgorithm();
- return "META-INF/" + baseName + "." + keyAlgorithm;
+ return getBaseSignatureFilesName(baseName) + keyAlgorithm;
}
// Generates the PKCS#7 content of block file
--- a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java Thu Jul 25 08:24:01 2019 -0400
@@ -944,11 +944,10 @@
// Don't read from the newManifest InputStream, as we
// might need it below, and we can't re-read the same data
// twice.
- FileInputStream fis = new FileInputStream(mname);
- boolean ambiguous = isAmbiguousMainClass(new Manifest(fis));
- fis.close();
- if (ambiguous) {
- return false;
+ try (FileInputStream fis = new FileInputStream(mname)) {
+ if (isAmbiguousMainClass(new Manifest(fis))) {
+ return false;
+ }
}
}
// Update the manifest.
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Extern.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Extern.java Thu Jul 25 08:24:01 2019 -0400
@@ -409,9 +409,10 @@
}
checkLinkCompatibility(elemname, moduleName, path);
Item item = new Item(elemname, elempath, relative);
- packageItems.computeIfAbsent(moduleName == null ?
- DocletConstants.DEFAULT_ELEMENT_NAME : moduleName, k -> new TreeMap<>())
- .put(elemname, item);
+ packageItems.computeIfAbsent(
+ moduleName == null ? DocletConstants.DEFAULT_ELEMENT_NAME : moduleName,
+ k -> new TreeMap<>())
+ .putIfAbsent(elemname, item); // first-one-wins semantics
}
}
}
--- a/src/jdk.javadoc/share/legal/pako.md Thu Jul 25 08:13:44 2019 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-## Pako v1.0
-
-### Pako License
-<pre>
-Copyright (C) 2014-2017 by Vitaly Puzrin and Andrei Tuputcyn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-(C) 1995-2013 Jean-loup Gailly and Mark Adler
-(C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not
-claim that you wrote the original software. If you use this software
-in a product, an acknowledgment in the product documentation would be
-appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
-
-</pre>
-
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jcmd/share/classes/sun/tools/common/PrintStreamPrinter.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019, 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 sun.tools.common;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.util.Arrays;
+
+/**
+ * A helper class which prints the content of input streams to print streams.
+ */
+public class PrintStreamPrinter {
+
+ /**
+ * Reads characters in UTF-8 format from the input stream and prints them
+ * with the given print stream. Closes the input stream before it returns.
+ *
+ * @return The number of printed characters.
+ */
+ public static long drainUTF8(InputStream is, PrintStream ps) throws IOException {
+ long result = 0;
+
+ try (BufferedInputStream bis = new BufferedInputStream(is);
+ InputStreamReader isr = new InputStreamReader(bis, "UTF-8")) {
+ char c[] = new char[256];
+ int n;
+
+ do {
+ n = isr.read(c);
+
+ if (n > 0) {
+ result += n;
+ ps.print(n == c.length ? c : Arrays.copyOf(c, n));
+ }
+ } while (n > 0);
+ }
+
+ return result;
+ }
+}
--- a/src/jdk.jcmd/share/classes/sun/tools/jcmd/JCmd.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.jcmd/share/classes/sun/tools/jcmd/JCmd.java Thu Jul 25 08:24:01 2019 -0400
@@ -25,13 +25,10 @@
package sun.tools.jcmd;
+import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.IOException;
import java.io.UnsupportedEncodingException;
-import java.util.List;
import java.util.Collection;
-import java.util.Collections;
import java.util.Comparator;
import java.net.URISyntaxException;
@@ -42,6 +39,7 @@
import sun.tools.attach.HotSpotVirtualMachine;
import sun.tools.common.ProcessArgumentMatcher;
+import sun.tools.common.PrintStreamPrinter;
import sun.tools.jstat.JStatLogger;
import sun.jvmstat.monitor.Monitor;
import sun.jvmstat.monitor.MonitoredHost;
@@ -122,23 +120,11 @@
if (line.trim().equals("stop")) {
break;
}
- try (InputStream in = hvm.executeJCmd(line);
- InputStreamReader isr = new InputStreamReader(in, "UTF-8")) {
- // read to EOF and just print output
- char c[] = new char[256];
- int n;
- boolean messagePrinted = false;
- do {
- n = isr.read(c);
- if (n > 0) {
- String s = new String(c, 0, n);
- System.out.print(s);
- messagePrinted = true;
- }
- } while (n > 0);
- if (!messagePrinted) {
- System.out.println("Command executed successfully");
- }
+
+ InputStream is = hvm.executeJCmd(line);
+
+ if (PrintStreamPrinter.drainUTF8(is, System.out) == 0) {
+ System.out.println("Command executed successfully");
}
}
vm.detach();
--- a/src/jdk.jcmd/share/classes/sun/tools/jinfo/JInfo.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.jcmd/share/classes/sun/tools/jinfo/JInfo.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2019, 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
@@ -30,10 +30,10 @@
import java.util.Collection;
import com.sun.tools.attach.VirtualMachine;
-import com.sun.tools.attach.VirtualMachineDescriptor;
import sun.tools.attach.HotSpotVirtualMachine;
import sun.tools.common.ProcessArgumentMatcher;
+import sun.tools.common.PrintStreamPrinter;
/*
* This class is the main class for the JInfo utility. It parses its arguments
@@ -203,17 +203,7 @@
// Read the stream from the target VM until EOF, then detach
private static void drain(VirtualMachine vm, InputStream in) throws IOException {
- // read to EOF and just print output
- byte b[] = new byte[256];
- int n;
- do {
- n = in.read(b);
- if (n > 0) {
- String s = new String(b, 0, n, "UTF-8");
- System.out.print(s);
- }
- } while (n > 0);
- in.close();
+ PrintStreamPrinter.drainUTF8(in, System.out);
vm.detach();
}
--- a/src/jdk.jcmd/share/classes/sun/tools/jstack/JStack.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.jcmd/share/classes/sun/tools/jstack/JStack.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -29,9 +29,9 @@
import java.util.Collection;
import com.sun.tools.attach.VirtualMachine;
-import com.sun.tools.attach.VirtualMachineDescriptor;
import sun.tools.attach.HotSpotVirtualMachine;
import sun.tools.common.ProcessArgumentMatcher;
+import sun.tools.common.PrintStreamPrinter;
/*
* This class is the main class for the JStack utility. It parses its arguments
@@ -128,18 +128,8 @@
// Cast to HotSpotVirtualMachine as this is implementation specific
// method.
InputStream in = ((HotSpotVirtualMachine)vm).remoteDataDump((Object[])args);
-
// read to EOF and just print output
- byte b[] = new byte[256];
- int n;
- do {
- n = in.read(b);
- if (n > 0) {
- String s = new String(b, 0, n, "UTF-8");
- System.out.print(s);
- }
- } while (n > 0);
- in.close();
+ PrintStreamPrinter.drainUTF8(in, System.out);
vm.detach();
}
--- a/src/jdk.jsobject/share/classes/jdk/internal/netscape/javascript/spi/JSObjectProvider.java Thu Jul 25 08:13:44 2019 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2016, 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 jdk.internal.netscape.javascript.spi;
-
-import java.applet.Applet;
-import netscape.javascript.JSException;
-import netscape.javascript.JSObject;
-
-@SuppressWarnings("deprecation")
-public interface JSObjectProvider {
- /**
- * Return a JSObject for the window containing the given applet.
- * Implementations of this class should return null if not connected to a
- * browser, for example, when running in AppletViewer.
- *
- * @param applet The applet.
- * @return JSObject for the window containing the given applet or null if we
- * are not connected to a browser.
- * @throws JSException when an error is encountered.
- */
- public JSObject getWindow(Applet applet) throws JSException;
-}
--- a/src/jdk.jsobject/share/classes/module-info.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.jsobject/share/classes/module-info.java Thu Jul 25 08:24:01 2019 -0400
@@ -30,9 +30,5 @@
* @since 9
*/
module jdk.jsobject {
- requires java.desktop;
-
exports netscape.javascript;
-
- uses jdk.internal.netscape.javascript.spi.JSObjectProvider;
}
--- a/src/jdk.jsobject/share/classes/netscape/javascript/JSObject.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.jsobject/share/classes/netscape/javascript/JSObject.java Thu Jul 25 08:24:01 2019 -0400
@@ -25,8 +25,6 @@
package netscape.javascript;
-import jdk.internal.netscape.javascript.spi.JSObjectProvider;
-import java.applet.Applet;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Iterator;
@@ -139,52 +137,4 @@
*/
public abstract void setSlot(int index, Object value) throws JSException;
- /**
- * Returns a JSObject for the window containing the given applet. This
- * method only works when the Java code is running in a browser as an
- * applet. The object returned may be used to access the HTML DOM directly.
- *
- * @param applet The applet.
- * @return JSObject representing the window containing the given applet or
- * {@code null} if we are not connected to a browser.
- * @throws JSException when an error is reported from the browser or
- * JavaScript engine or if applet is {@code null}
- *
- * @deprecated The Applet API is deprecated, no replacement. See the
- * <a href="{@docRoot}/java.desktop/java/applet/package-summary.html">
- * java.applet package documentation</a> for further information.
- */
-
- @Deprecated(since="9", forRemoval=true)
- @SuppressWarnings("exports")
- public static JSObject getWindow(Applet applet) throws JSException {
- return ProviderLoader.callGetWindow(applet);
- }
-
- private static class ProviderLoader {
- private static final JSObjectProvider provider;
-
- static {
- provider = AccessController.doPrivileged(
- new PrivilegedAction<>() {
- @Override
- public JSObjectProvider run() {
- Iterator<JSObjectProvider> providers =
- ServiceLoader.loadInstalled(JSObjectProvider.class).iterator();
- if (providers.hasNext()) {
- return providers.next();
- }
- return null;
- }
- }
- );
- }
-
- private static JSObject callGetWindow(Applet applet) {
- if (provider != null) {
- return provider.getWindow(applet);
- }
- return null;
- }
- }
}
--- a/src/jdk.net/linux/classes/jdk/net/LinuxSocketOptions.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.net/linux/classes/jdk/net/LinuxSocketOptions.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -95,9 +95,14 @@
private static native boolean keepAliveOptionsSupported0();
private static native boolean quickAckSupported0();
static {
- AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
+ if (System.getSecurityManager() == null) {
System.loadLibrary("extnet");
- return null;
- });
+ } else {
+ AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
+ System.loadLibrary("extnet");
+ return null;
+ });
+ }
}
-}
\ No newline at end of file
+}
+
--- a/src/jdk.net/macosx/classes/jdk/net/MacOSXSocketOptions.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.net/macosx/classes/jdk/net/MacOSXSocketOptions.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -77,9 +77,13 @@
private static native int getTcpKeepAliveIntvl0(int fd) throws SocketException;
private static native boolean keepAliveOptionsSupported0();
static {
- AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
+ if (System.getSecurityManager() == null) {
System.loadLibrary("extnet");
- return null;
- });
+ } else {
+ AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
+ System.loadLibrary("extnet");
+ return null;
+ });
+ }
}
}
--- a/src/jdk.net/solaris/classes/jdk/net/SolarisSocketOptions.java Thu Jul 25 08:13:44 2019 -0400
+++ b/src/jdk.net/solaris/classes/jdk/net/SolarisSocketOptions.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -45,12 +45,16 @@
private static native void init();
static {
- AccessController.doPrivileged(new PrivilegedAction<Void>() {
- public Void run() {
- System.loadLibrary("extnet");
- return null;
- }
- });
+ if (System.getSecurityManager() == null) {
+ System.loadLibrary("extnet");
+ } else {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("extnet");
+ return null;
+ }
+ });
+ }
init();
}
}
--- a/test/hotspot/jtreg/TEST.ROOT Thu Jul 25 08:13:44 2019 -0400
+++ b/test/hotspot/jtreg/TEST.ROOT Thu Jul 25 08:24:01 2019 -0400
@@ -35,7 +35,9 @@
# to determine additional characteristics of the system for use with the @requires tag.
# Note: compiled bootlibs code will be located in the folder 'bootClasses'
requires.extraPropDefns = ../../jtreg-ext/requires/VMProps.java
-requires.extraPropDefns.bootlibs = ../../lib/sun ../../lib/jdk/test/lib/Platform.java
+requires.extraPropDefns.bootlibs = ../../lib/sun \
+ ../../lib/jdk/test/lib/Platform.java \
+ ../../lib/jdk/test/lib/Container.java
requires.extraPropDefns.vmOpts = -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:bootClasses
requires.properties= \
sun.arch.data.model \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/containers/docker/EventGeneratorLoop.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+import jdk.jfr.Event;
+import jdk.jfr.Description;
+import jdk.jfr.Label;
+
+
+// This class generates simple event in a loop
+// for a specified time.
+// Pass the time in seconds as a parameter.
+public class EventGeneratorLoop {
+
+ @Label("SimpleEvent")
+ @Description("Simple custom event")
+ static class SimpleEvent extends Event {
+ @Label("Message")
+ String msg;
+
+ @Label("Count")
+ int count;
+ }
+
+
+ public static void main(String[] args) throws Exception {
+ if ((args.length < 1) || (args[0] == null)) {
+ throw new IllegalArgumentException("Expecting one argument: time to run (seconds)");
+ }
+ int howLong = Integer.parseInt(args[0]);
+
+ for (int i=0; i < howLong; i++) {
+ SimpleEvent ev = new SimpleEvent();
+ ev.msg = "Hello";
+ ev.count = i;
+ ev.commit();
+
+ try { Thread.sleep(1000); } catch (InterruptedException e) {}
+ System.out.print(".");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+
+/*
+ * @test
+ * @summary Test JCMD with side car pattern.
+ * Sidecar is a common pattern used in the cloud environments for monitoring
+ * and other uses. In side car pattern the main application/service container
+ * is paired with a sidecar container by sharing certain aspects of container
+ * namespace such as PID namespace, specific sub-directories, IPC and more.
+ * @requires docker.support
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ * java.management
+ * jdk.jartool/sun.tools.jar
+ * @build EventGeneratorLoop
+ * @run driver TestJcmdWithSideCar
+ */
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import jdk.test.lib.Container;
+import jdk.test.lib.Utils;
+import jdk.test.lib.containers.docker.Common;
+import jdk.test.lib.containers.docker.DockerRunOptions;
+import jdk.test.lib.containers.docker.DockerTestUtils;
+import jdk.test.lib.process.OutputAnalyzer;
+
+
+public class TestJcmdWithSideCar {
+ private static final String IMAGE_NAME = Common.imageName("jfr-jcmd");
+ private static final int TIME_TO_RUN_MAIN_PROCESS = (int) (30 * Utils.TIMEOUT_FACTOR); // seconds
+ private static final String MAIN_CONTAINER_NAME = "test-container-main";
+
+ public static void main(String[] args) throws Exception {
+ if (!DockerTestUtils.canTestDocker()) {
+ return;
+ }
+
+ DockerTestUtils.buildJdkDockerImage(IMAGE_NAME, "Dockerfile-BasicTest", "jdk-docker");
+
+ try {
+ // Start the loop process in the "main" container, then run test cases
+ // using a sidecar container.
+ DockerThread t = startMainContainer();
+
+ waitForMainContainerToStart(500, 10);
+ t.checkForErrors();
+
+ OutputAnalyzer jcmdOut = testCase01();
+ long mainProcPid = findProcess(jcmdOut, "EventGeneratorLoop");
+
+ t.assertIsAlive();
+ testCase02(mainProcPid);
+
+ // JCMD does not work in sidecar configuration, except for "jcmd -l".
+ // Including this test case to assist in reproduction of the problem.
+ // t.assertIsAlive();
+ // testCase03(mainProcPid);
+
+ t.join(TIME_TO_RUN_MAIN_PROCESS * 1000);
+ t.checkForErrors();
+ } finally {
+ DockerTestUtils.removeDockerImage(IMAGE_NAME);
+ }
+ }
+
+
+ // Run "jcmd -l" in a sidecar container and find a process that runs EventGeneratorLoop
+ private static OutputAnalyzer testCase01() throws Exception {
+ return runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "-l")
+ .shouldHaveExitValue(0)
+ .shouldContain("sun.tools.jcmd.JCmd")
+ .shouldContain("EventGeneratorLoop");
+ }
+
+ // run jhsdb jinfo <PID> (jhsdb uses PTRACE)
+ private static void testCase02(long pid) throws Exception {
+ runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jhsdb", "jinfo", "--pid", "" + pid)
+ .shouldHaveExitValue(0)
+ .shouldContain("Java System Properties")
+ .shouldContain("VM Flags");
+ }
+
+ // test jcmd with some commands (help, start JFR recording)
+ // JCMD will use signal mechanism and Unix Socket
+ private static void testCase03(long pid) throws Exception {
+ runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "" + pid, "help")
+ .shouldHaveExitValue(0)
+ .shouldContain("VM.version");
+ runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "" + pid, "JFR.start")
+ .shouldHaveExitValue(0)
+ .shouldContain("Started recording");
+ }
+
+ private static DockerThread startMainContainer() throws Exception {
+ // start "main" container (the observee)
+ DockerRunOptions opts = commonDockerOpts("EventGeneratorLoop");
+ opts.addDockerOpts("--cap-add=SYS_PTRACE")
+ .addDockerOpts("--name", MAIN_CONTAINER_NAME)
+ .addDockerOpts("-v", "/tmp")
+ .addJavaOpts("-XX:+UsePerfData")
+ .addClassOptions("" + TIME_TO_RUN_MAIN_PROCESS);
+ DockerThread t = new DockerThread(opts);
+ t.start();
+
+ return t;
+ }
+
+ private static void waitForMainContainerToStart(int delayMillis, int count) throws Exception {
+ boolean started = false;
+ for(int i=0; i < count; i++) {
+ try {
+ Thread.sleep(delayMillis);
+ } catch (InterruptedException e) {}
+ if (isMainContainerRunning()) {
+ started = true;
+ break;
+ }
+ }
+ if (!started) {
+ throw new RuntimeException("Main container did not start");
+ }
+ }
+
+ private static boolean isMainContainerRunning() throws Exception {
+ OutputAnalyzer out =
+ DockerTestUtils.execute(Container.ENGINE_COMMAND,
+ "ps", "--no-trunc",
+ "--filter", "name=" + MAIN_CONTAINER_NAME);
+ return out.getStdout().contains(MAIN_CONTAINER_NAME);
+ }
+
+ // JCMD relies on the attach mechanism (com.sun.tools.attach),
+ // which in turn relies on JVMSTAT mechanism, which puts its mapped
+ // buffers in /tmp directory (hsperfdata_<user>). Thus, in sidecar
+ // we mount /tmp via --volumes-from from the main container.
+ private static OutputAnalyzer runSideCar(String MAIN_CONTAINER_NAME, String whatToRun,
+ String... args) throws Exception {
+ List<String> cmd = new ArrayList<>();
+ String[] command = new String[] {
+ Container.ENGINE_COMMAND, "run",
+ "--tty=true", "--rm",
+ "--cap-add=SYS_PTRACE", "--sig-proxy=true",
+ "--pid=container:" + MAIN_CONTAINER_NAME,
+ "--volumes-from", MAIN_CONTAINER_NAME,
+ IMAGE_NAME, whatToRun
+ };
+
+ cmd.addAll(Arrays.asList(command));
+ cmd.addAll(Arrays.asList(args));
+ return DockerTestUtils.execute(cmd);
+ }
+
+ private static long findProcess(OutputAnalyzer out, String name) throws Exception {
+ List<String> l = out.asLines()
+ .stream()
+ .filter(s -> s.contains(name))
+ .collect(Collectors.toList());
+ if (l.isEmpty()) {
+ throw new RuntimeException("Could not find matching process");
+ }
+ String psInfo = l.get(0);
+ System.out.println("findProcess(): psInfo: " + psInfo);
+ String pid = psInfo.substring(0, psInfo.indexOf(' '));
+ System.out.println("findProcess(): pid: " + pid);
+ return Long.parseLong(pid);
+ }
+
+ private static DockerRunOptions commonDockerOpts(String className) {
+ return new DockerRunOptions(IMAGE_NAME, "/jdk/bin/java", className)
+ .addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/")
+ .addJavaOpts("-cp", "/test-classes/");
+ }
+
+
+ static class DockerThread extends Thread {
+ DockerRunOptions runOpts;
+ Exception exception;
+
+ DockerThread(DockerRunOptions opts) {
+ runOpts = opts;
+ }
+
+ public void run() {
+ try {
+ DockerTestUtils.dockerRunJava(runOpts);
+ } catch (Exception e) {
+ exception = e;
+ }
+ }
+
+ public void assertIsAlive() throws Exception {
+ if (!isAlive()) {
+ throw new RuntimeException("DockerThread stopped unexpectedly");
+ }
+ }
+
+ public void checkForErrors() throws Exception {
+ if (exception != null) {
+ throw new RuntimeException("DockerThread threw exception"
+ + exception.getMessage());
+ }
+ }
+ }
+
+}
--- a/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java Thu Jul 25 08:24:01 2019 -0400
@@ -97,6 +97,8 @@
// Merge Heap Roots
new LogMessageWithLevel("Merge Heap Roots", Level.INFO),
+ new LogMessageWithLevel("Prepare Merge Heap Roots", Level.DEBUG),
+ new LogMessageWithLevel("Eager Reclaim", Level.DEBUG),
new LogMessageWithLevel("Remembered Sets", Level.DEBUG),
new LogMessageWithLevel("Merged Sparse", Level.DEBUG),
new LogMessageWithLevel("Merged Fine", Level.DEBUG),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/gc/g1/TestNoUseHCC.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019, 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 gc.g1;
+
+/*
+ * @test TestNoUseHCC
+ * @summary Check that G1 survives a GC without HCC enabled
+ * @requires vm.gc.G1
+ * @key gc
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib
+ * @build sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -Xlog:gc+phases=debug -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -Xmx64M -XX:G1ConcRSLogCacheSize=0 gc.g1.TestNoUseHCC
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class TestNoUseHCC {
+
+ private static final WhiteBox WB = WhiteBox.getWhiteBox();
+
+ public static void main(String [] args) {
+ WB.youngGC();
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/gc/metaspace/TestSizeTransitions.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, Twitter, Inc.
+ * 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 gc.metaspace;
+
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+
+/* @test TestSizeTransitionsSerial
+ * @key gc
+ * @requires vm.gc.Serial
+ * @summary Tests that the metaspace size transition logging is done correctly.
+ * @library /test/lib
+ * @run driver gc.metaspace.TestSizeTransitions false -XX:+UseSerialGC
+ * @run driver gc.metaspace.TestSizeTransitions true -XX:+UseSerialGC
+ */
+
+/* @test TestSizeTransitionsParallel
+ * @key gc
+ * @requires vm.gc.Parallel
+ * @summary Tests that the metaspace size transition logging is done correctly.
+ * @library /test/lib
+ * @run driver gc.metaspace.TestSizeTransitions false -XX:+UseParallelGC
+ * @run driver gc.metaspace.TestSizeTransitions true -XX:+UseParallelGC
+ */
+
+/* @test TestSizeTransitionsG1
+ * @key gc
+ * @requires vm.gc.G1
+ * @summary Tests that the metaspace size transition logging is done correctly.
+ * @library /test/lib
+ * @run driver gc.metaspace.TestSizeTransitions false -XX:+UseG1GC
+ * @run driver gc.metaspace.TestSizeTransitions true -XX:+UseG1GC
+ */
+
+/* @test TestSizeTransitionsCMS
+ * @key gc
+ * @requires vm.gc.ConcMarkSweep
+ * @summary Tests that the metaspace size transition logging is done correctly.
+ * @library /test/lib
+ * @run driver gc.metaspace.TestSizeTransitions false -XX:+UseConcMarkSweepGC
+ * @run driver gc.metaspace.TestSizeTransitions true -XX:+UseConcMarkSweepGC
+ */
+
+public class TestSizeTransitions {
+ public static class Run {
+ public static void main(String... args) throws Exception {
+ System.out.println("Run started.");
+
+ // easiest way to generate a metaspace transition is to ask for a full GC
+ System.gc();
+
+ System.out.println("Run finished.");
+ }
+ }
+
+ // matches the log tags
+ // e.g., [0.043s][info][gc]
+ private static final String LOG_TAGS_REGEX = "(\\[.*\\])+ ";
+
+ // matches a size transition
+ // e.g., 177K(4864K)->177K(4864K)
+ private static final String SIZE_TRANSITION_REGEX = "\\d+K\\(\\d+K\\)->\\d+K\\(\\d+K\\)";
+
+ // matches -coops metaspace size transitions
+ private static final String NO_COOPS_REGEX =
+ String.format("^%s.* Metaspace: %s$",
+ LOG_TAGS_REGEX,
+ SIZE_TRANSITION_REGEX);
+
+ // matches +coops metaspace size transitions
+ private static final String COOPS_REGEX =
+ String.format("^%s.* Metaspace: %s NonClass: %s Class: %s$",
+ LOG_TAGS_REGEX,
+ SIZE_TRANSITION_REGEX,
+ SIZE_TRANSITION_REGEX,
+ SIZE_TRANSITION_REGEX);
+
+ public static void main(String... args) throws Exception {
+ // args: <use-coops> <gc-arg>
+ if (args.length != 2) {
+ throw new RuntimeException("wrong number of args: " + args.length);
+ }
+
+ final boolean useCoops = Boolean.parseBoolean(args[0]);
+ final String gcArg = args[1];
+ final String[] jvmArgs = {
+ useCoops ? "-XX:+UseCompressedOops" : "-XX:-UseCompressedOops",
+ gcArg,
+ "-Xmx256m",
+ "-Xlog:gc,gc+metaspace=info",
+ TestSizeTransitions.Run.class.getName()
+ };
+
+ System.out.println("JVM args:");
+ for (String a : jvmArgs) {
+ System.out.println(" " + a);
+ }
+
+ final ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(jvmArgs);
+ final OutputAnalyzer output = new OutputAnalyzer(pb.start());
+ System.out.println(output.getStdout());
+ output.shouldHaveExitValue(0);
+
+ if (useCoops) {
+ output.stdoutShouldMatch(COOPS_REGEX);
+ output.stdoutShouldNotMatch(NO_COOPS_REGEX);
+ } else {
+ output.stdoutShouldMatch(NO_COOPS_REGEX);
+ output.stdoutShouldNotMatch(COOPS_REGEX);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/CheckUnhandledOops/TestOutOfMemory.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/*
+ * @test
+ * @bug 8227766
+ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+CheckUnhandledOops -Xmx100m TestOutOfMemory
+ */
+
+public class TestOutOfMemory {
+ public static void main(java.lang.String[] unused) {
+ final int BIG = 0x100000;
+ // Getting OOM breaks the unhandled oop detector
+ try {
+ int[][] X = new int[BIG][];
+ for (int i = 0; i < BIG; i++) {
+ X[i] = new int[BIG];
+ System.out.println("length = " + X.length);
+ }
+ } catch (OutOfMemoryError oom) {
+ System.out.println("OOM expected");
+ }
+ }
+}
--- a/test/hotspot/jtreg/runtime/appcds/SharedArchiveConsistency.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/SharedArchiveConsistency.java Thu Jul 25 08:24:01 2019 -0400
@@ -61,13 +61,17 @@
public class SharedArchiveConsistency {
public static WhiteBox wb;
- public static int offset_magic; // FileMapHeader::_magic
- public static int sp_offset_crc; // CDSFileMapRegion::_crc
+ public static int offset_magic; // CDSFileMapHeaderBase::_magic
+ public static int offset_version; // CDSFileMapHeaderBase::_version
+ public static int offset_jvm_ident; // FileMapHeader::_jvm_ident
+ public static int sp_offset_crc; // CDSFileMapRegion::_crc
+ public static int offset_paths_misc_info_size;
public static int file_header_size = -1;// total size of header, variant, need calculation
public static int CDSFileMapRegion_size; // size of CDSFileMapRegion
public static int sp_offset; // offset of CDSFileMapRegion
public static int sp_used_offset; // offset of CDSFileMapRegion::_used
public static int size_t_size; // size of size_t
+ public static int int_size; // size of int
public static File jsa; // will be updated during test
public static File orgJsaFile; // kept the original file not touched.
@@ -94,6 +98,8 @@
public static void getFileOffsetInfo() throws Exception {
wb = WhiteBox.getWhiteBox();
offset_magic = wb.getOffsetForName("FileMapHeader::_magic");
+ offset_version = wb.getOffsetForName("FileMapHeader::_version");
+ offset_jvm_ident = wb.getOffsetForName("FileMapHeader::_jvm_ident");
sp_offset_crc = wb.getOffsetForName("CDSFileMapRegion::_crc");
try {
int nonExistOffset = wb.getOffsetForName("FileMapHeader::_non_exist_offset");
@@ -113,13 +119,13 @@
return file_header_size;
}
// this is not real header size, it is struct size
- int int_size = wb.getOffsetForName("int_size");
+ int_size = wb.getOffsetForName("int_size");
file_header_size = wb.getOffsetForName("file_header_size");
- int offset_path_misc_info = wb.getOffsetForName("FileMapHeader::_paths_misc_info_size") -
+ offset_paths_misc_info_size = wb.getOffsetForName("FileMapHeader::_paths_misc_info_size") -
offset_magic;
- int path_misc_info_size = (int)readInt(fc, offset_path_misc_info, int_size);
- file_header_size += path_misc_info_size; //readInt(fc, offset_path_misc_info, size_t_size);
- System.out.println("offset_path_misc_info = " + offset_path_misc_info);
+ int path_misc_info_size = (int)readInt(fc, offset_paths_misc_info_size, int_size);
+ file_header_size += path_misc_info_size;
+ System.out.println("offset_paths_misc_info_size = " + offset_paths_misc_info_size);
System.out.println("path_misc_info_size = " + path_misc_info_size);
System.out.println("file_header_size = " + file_header_size);
file_header_size = (int)align_up_page(file_header_size);
@@ -157,18 +163,17 @@
public static void writeData(FileChannel fc, long offset, ByteBuffer bb) throws Exception {
fc.position(offset);
fc.write(bb);
- fc.force(true);
}
- public static FileChannel getFileChannel() throws Exception {
+ public static FileChannel getFileChannel(File jsaFile) throws Exception {
List<StandardOpenOption> arry = new ArrayList<StandardOpenOption>();
arry.add(READ);
arry.add(WRITE);
- return FileChannel.open(jsa.toPath(), new HashSet<StandardOpenOption>(arry));
+ return FileChannel.open(jsaFile.toPath(), new HashSet<StandardOpenOption>(arry));
}
- public static void modifyJsaContentRandomly() throws Exception {
- FileChannel fc = getFileChannel();
+ public static void modifyJsaContentRandomly(File jsaFile) throws Exception {
+ FileChannel fc = getFileChannel(jsaFile);
// corrupt random area in the data areas
long[] used = new long[num_regions]; // record used bytes
long start0, start, end, off;
@@ -210,8 +215,8 @@
return used;
}
- public static boolean modifyJsaContent(int region) throws Exception {
- FileChannel fc = getFileChannel();
+ public static boolean modifyJsaContent(int region, File jsaFile) throws Exception {
+ FileChannel fc = getFileChannel(jsaFile);
byte[] buf = new byte[4096];
ByteBuffer bbuf = ByteBuffer.wrap(buf);
@@ -241,15 +246,14 @@
bbuf.clear();
bytes_written += 4096;
}
- fc.force(true);
if (fc.isOpen()) {
fc.close();
}
return true;
}
- public static void modifyJsaHeader() throws Exception {
- FileChannel fc = getFileChannel();
+ public static void modifyJsaHeader(File jsaFile) throws Exception {
+ FileChannel fc = getFileChannel(jsaFile);
// screw up header info
byte[] buf = new byte[getFileHeaderSize(fc)];
ByteBuffer bbuf = ByteBuffer.wrap(buf);
@@ -259,6 +263,30 @@
}
}
+ public static void modifyJvmIdent() throws Exception {
+ FileChannel fc = getFileChannel(jsa);
+ int headerSize = getFileHeaderSize(fc);
+ System.out.println(" offset_jvm_ident " + offset_jvm_ident);
+ byte[] buf = new byte[256];
+ ByteBuffer bbuf = ByteBuffer.wrap(buf);
+ writeData(fc, (long)offset_jvm_ident, bbuf);
+ if (fc.isOpen()) {
+ fc.close();
+ }
+ }
+
+ public static void modifyHeaderIntField(long offset, int value) throws Exception {
+ FileChannel fc = getFileChannel(jsa);
+ int headerSize = getFileHeaderSize(fc);
+ System.out.println(" offset " + offset);
+ byte[] buf = ByteBuffer.allocate(4).putInt(value).array();
+ ByteBuffer bbuf = ByteBuffer.wrap(buf);
+ writeData(fc, offset, bbuf);
+ if (fc.isOpen()) {
+ fc.close();
+ }
+ }
+
public static void copyFile(File from, File to) throws Exception {
if (to.exists()) {
if(!to.delete()) {
@@ -348,10 +376,10 @@
// test, should pass
System.out.println("1. Normal, should pass but may fail\n");
- String[] execArgs = {"-cp", jarFile, "Hello"};
+ String[] execArgs = {"-Xlog:cds", "-cp", jarFile, "Hello"};
// tests that corrupt contents of the archive need to run with
// VerifySharedSpaces enabled to detect inconsistencies
- String[] verifyExecArgs = {"-XX:+VerifySharedSpaces", "-cp", jarFile, "Hello"};
+ String[] verifyExecArgs = {"-Xlog:cds", "-XX:+VerifySharedSpaces", "-cp", jarFile, "Hello"};
OutputAnalyzer output = TestCommon.execCommon(execArgs);
@@ -373,10 +401,36 @@
orgJsaFile = new File(new File(currentDir), "appcds.jsa.bak");
copyFile(jsa, orgJsaFile);
-
// modify jsa header, test should fail
System.out.println("\n2. Corrupt header, should fail\n");
- modifyJsaHeader();
+ modifyJsaHeader(jsa);
+ output = TestCommon.execCommon(execArgs);
+ output.shouldContain("The shared archive file has a bad magic number");
+ output.shouldNotContain("Checksum verification failed");
+
+ copyFile(orgJsaFile, jsa);
+ // modify _jvm_ident and _paths_misc_info_size, test should fail
+ System.out.println("\n2a. Corrupt _jvm_ident and _paths_misc_info_size, should fail\n");
+ modifyJvmIdent();
+ modifyHeaderIntField(offset_paths_misc_info_size, Integer.MAX_VALUE);
+ output = TestCommon.execCommon(execArgs);
+ output.shouldContain("The shared archive file was created by a different version or build of HotSpot");
+ output.shouldNotContain("Checksum verification failed");
+
+ copyFile(orgJsaFile, jsa);
+ // modify _magic and _paths_misc_info_size, test should fail
+ System.out.println("\n2b. Corrupt _magic and _paths_misc_info_size, should fail\n");
+ modifyHeaderIntField(offset_magic, 0x00000000);
+ modifyHeaderIntField(offset_paths_misc_info_size, Integer.MAX_VALUE);
+ output = TestCommon.execCommon(execArgs);
+ output.shouldContain("The shared archive file has a bad magic number");
+ output.shouldNotContain("Checksum verification failed");
+
+ copyFile(orgJsaFile, jsa);
+ // modify _version and _paths_misc_info_size, test should fail
+ System.out.println("\n2c. Corrupt _version and _paths_misc_info_size, should fail\n");
+ modifyHeaderIntField(offset_version, 0x00000000);
+ modifyHeaderIntField(offset_paths_misc_info_size, Integer.MAX_VALUE);
output = TestCommon.execCommon(execArgs);
output.shouldContain("The shared archive file has the wrong version");
output.shouldNotContain("Checksum verification failed");
@@ -387,8 +441,9 @@
for (int i=0; i<num_regions; i++) {
newJsaFile = new File(TestCommon.getNewArchiveName(shared_region_name[i]));
copyFile(orgJsaFile, newJsaFile);
- if (modifyJsaContent(i)) {
- testAndCheck(execArgs);
+ TestCommon.setCurrentArchiveName(newJsaFile.toString());
+ if (modifyJsaContent(i, newJsaFile)) {
+ testAndCheck(verifyExecArgs);
}
}
@@ -396,15 +451,17 @@
System.out.println("\n4. Corrupt Header and Content, should fail\n");
newJsaFile = new File(TestCommon.getNewArchiveName("header-and-content"));
copyFile(orgJsaFile, newJsaFile);
- modifyJsaHeader();
- modifyJsaContent(0); // this will not be reached since failed on header change first
+ TestCommon.setCurrentArchiveName(newJsaFile.toString());
+ modifyJsaHeader(newJsaFile);
+ modifyJsaContent(0, newJsaFile); // this will not be reached since failed on header change first
output = TestCommon.execCommon(execArgs);
- output.shouldContain("The shared archive file has the wrong version");
+ output.shouldContain("The shared archive file has a bad magic number");
output.shouldNotContain("Checksum verification failed");
// delete bytes in data section
System.out.println("\n5. Delete bytes at beginning of data section, should fail\n");
copyFile(orgJsaFile, jsa, true);
+ TestCommon.setCurrentArchiveName(jsa.toString());
testAndCheck(verifyExecArgs);
// insert bytes in data section forward
@@ -415,7 +472,8 @@
System.out.println("\n7. modify Content in random areas, should fail\n");
newJsaFile = new File(TestCommon.getNewArchiveName("random-areas"));
copyFile(orgJsaFile, newJsaFile);
- modifyJsaContentRandomly();
+ TestCommon.setCurrentArchiveName(newJsaFile.toString());
+ modifyJsaContentRandomly(newJsaFile);
testAndCheck(verifyExecArgs);
}
}
--- a/test/hotspot/jtreg/runtime/appcds/TestCommon.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/TestCommon.java Thu Jul 25 08:24:01 2019 -0400
@@ -90,6 +90,10 @@
return currentArchiveName;
}
+ public static void setCurrentArchiveName(String archiveName) {
+ currentArchiveName = archiveName;
+ }
+
public static String getNewArchiveName() {
return getNewArchiveName(null);
}
--- a/test/hotspot/jtreg/runtime/clinit/ClassInitBarrier.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/hotspot/jtreg/runtime/clinit/ClassInitBarrier.java Thu Jul 25 08:24:01 2019 -0400
@@ -27,24 +27,24 @@
*
* @requires !vm.graal.enabled
*
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -Xint -DTHROW=false ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -Xint -DTHROW=true ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -Xint -DTHROW=false -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -Xint -DTHROW=true -Xcheck:jni ClassInitBarrier
*
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true -Xcheck:jni ClassInitBarrier
*
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=false ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=true ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=false -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=true -Xcheck:jni ClassInitBarrier
*
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -XX:CompileCommand=dontinline,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true -XX:CompileCommand=dontinline,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=false -XX:CompileCommand=dontinline,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=true -XX:CompileCommand=dontinline,*::static* ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -XX:CompileCommand=dontinline,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true -XX:CompileCommand=dontinline,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=false -XX:CompileCommand=dontinline,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=true -XX:CompileCommand=dontinline,*::static* -Xcheck:jni ClassInitBarrier
*
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -XX:CompileCommand=exclude,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true -XX:CompileCommand=exclude,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=false -XX:CompileCommand=exclude,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=true -XX:CompileCommand=exclude,*::static* ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -XX:CompileCommand=exclude,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true -XX:CompileCommand=exclude,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=false -XX:CompileCommand=exclude,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=true -XX:CompileCommand=exclude,*::static* -Xcheck:jni ClassInitBarrier
*/
import jdk.test.lib.Asserts;
@@ -70,6 +70,10 @@
static class Test {
static class A {
static {
+ if (!init(B.class)) {
+ throw new Error("init failed");
+ }
+
changePhase(Phase.IN_PROGRESS);
runTests(); // interpreted mode
warmup(); // trigger compilation
@@ -89,13 +93,15 @@
int f;
void m() {}
+
+ static native boolean init(Class<B> cls);
}
static class B extends A {}
- static void testInvokeStatic(Runnable action) { A.staticM(action); }
- static void testInvokeStaticSync(Runnable action) { A.staticS(action); }
- static void testInvokeStaticNative(Runnable action) { A.staticN(action); }
+ static void testInvokeStatic(Runnable action) { A.staticM(action); }
+ static void testInvokeStaticSync(Runnable action) { A.staticS(action); }
+ static void testInvokeStaticNative(Runnable action) { A.staticN(action); }
static int testGetStatic(Runnable action) { int v = A.staticF; action.run(); return v; }
static void testPutStatic(Runnable action) { A.staticF = 1; action.run(); }
@@ -106,20 +112,45 @@
static void testPutField(A recv, Runnable action) { recv.f = 1; action.run(); }
static void testInvokeVirtual(A recv, Runnable action) { recv.m(); action.run(); }
+ static native void testInvokeStaticJNI(Runnable action);
+ static native void testInvokeStaticSyncJNI(Runnable action);
+ static native void testInvokeStaticNativeJNI(Runnable action);
+
+ static native int testGetStaticJNI(Runnable action);
+ static native void testPutStaticJNI(Runnable action);
+ static native A testNewInstanceAJNI(Runnable action);
+ static native B testNewInstanceBJNI(Runnable action);
+
+ static native int testGetFieldJNI(A recv, Runnable action);
+ static native void testPutFieldJNI(A recv, Runnable action);
+ static native void testInvokeVirtualJNI(A recv, Runnable action);
+
static void runTests() {
checkBlockingAction(Test::testInvokeStatic); // invokestatic
+ checkBlockingAction(Test::testInvokeStaticSync); // invokestatic
checkBlockingAction(Test::testInvokeStaticNative); // invokestatic
- checkBlockingAction(Test::testInvokeStaticSync); // invokestatic
checkBlockingAction(Test::testGetStatic); // getstatic
checkBlockingAction(Test::testPutStatic); // putstatic
checkBlockingAction(Test::testNewInstanceA); // new
+ checkNonBlockingAction(Test::testInvokeStaticJNI); // invokestatic
+ checkNonBlockingAction(Test::testInvokeStaticSyncJNI); // invokestatic
+ checkNonBlockingAction(Test::testInvokeStaticNativeJNI); // invokestatic
+ checkNonBlockingAction(Test::testGetStaticJNI); // getstatic
+ checkNonBlockingAction(Test::testPutStaticJNI); // putstatic
+ checkBlockingAction(Test::testNewInstanceAJNI); // new
+
A recv = testNewInstanceB(NON_BLOCKING.get()); // trigger B initialization
checkNonBlockingAction(Test::testNewInstanceB); // new: NO BLOCKING: same thread: A being initialized, B fully initialized
checkNonBlockingAction(recv, Test::testGetField); // getfield
checkNonBlockingAction(recv, Test::testPutField); // putfield
checkNonBlockingAction(recv, Test::testInvokeVirtual); // invokevirtual
+
+ checkNonBlockingAction(Test::testNewInstanceBJNI); // new: NO BLOCKING: same thread: A being initialized, B fully initialized
+ checkNonBlockingAction(recv, Test::testGetFieldJNI); // getfield
+ checkNonBlockingAction(recv, Test::testPutFieldJNI); // putfield
+ checkNonBlockingAction(recv, Test::testInvokeVirtualJNI); // invokevirtual
}
static void warmup() {
--- a/test/hotspot/jtreg/runtime/clinit/libClassInitBarrier.cpp Thu Jul 25 08:13:44 2019 -0400
+++ b/test/hotspot/jtreg/runtime/clinit/libClassInitBarrier.cpp Thu Jul 25 08:24:01 2019 -0400
@@ -25,6 +25,17 @@
static jmethodID methodId;
+static jclass test_class_A;
+static jclass test_class_B;
+
+static jmethodID test_staticM_id;
+static jmethodID test_staticS_id;
+static jmethodID test_staticN_id;
+static jmethodID test_A_m_id;
+
+static jfieldID test_staticF_id;
+static jfieldID test_A_f_id;
+
extern "C" {
JNIEXPORT jboolean JNICALL Java_ClassInitBarrier_init(JNIEnv* env, jclass cls) {
jclass runnable = env->FindClass("java/lang/Runnable");
@@ -36,7 +47,103 @@
return JNI_TRUE;
}
+ JNIEXPORT jboolean JNICALL Java_ClassInitBarrier_00024Test_00024A_init(JNIEnv* env, jclass cls, jclass arg1) {
+ test_class_A = (jclass)env->NewGlobalRef(cls);
+ if (test_class_A == NULL) return JNI_FALSE;
+
+ test_class_B = (jclass)env->NewGlobalRef(arg1);
+ if (test_class_B == NULL) return JNI_FALSE;
+
+ test_staticM_id = env->GetStaticMethodID(test_class_A, "staticM", "(Ljava/lang/Runnable;)V");
+ if (test_staticM_id == NULL) return JNI_FALSE;
+
+ test_staticS_id = env->GetStaticMethodID(test_class_A, "staticS", "(Ljava/lang/Runnable;)V");
+ if (test_staticS_id == NULL) return JNI_FALSE;
+
+ test_staticN_id = env->GetStaticMethodID(test_class_A, "staticN", "(Ljava/lang/Runnable;)V");
+ if (test_staticN_id == NULL) return JNI_FALSE;
+
+ test_A_m_id = env->GetMethodID(test_class_A, "m", "()V");
+ if (test_A_m_id == NULL) return JNI_FALSE;
+
+ test_staticF_id = env->GetStaticFieldID(test_class_A, "staticF", "I");
+ if (test_staticF_id == NULL) return JNI_FALSE;
+
+ test_A_f_id = env->GetFieldID(test_class_A, "f", "I");
+ if (test_A_f_id == NULL) return JNI_FALSE;
+
+ return JNI_TRUE;
+ }
+
JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_00024A_staticN(JNIEnv* env, jclass cls, jobject action) {
env->CallVoidMethod(action, methodId);
}
+
+ JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeStaticJNI(JNIEnv* env, jclass cls, jobject action) {
+ env->CallStaticVoidMethod(test_class_A, test_staticM_id, action);
+ }
+
+ JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeStaticSyncJNI(JNIEnv* env, jclass cls, jobject action) {
+ env->CallStaticVoidMethod(test_class_A, test_staticS_id, action);
+ }
+
+ JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeStaticNativeJNI(JNIEnv* env, jclass cls, jobject action) {
+ env->CallStaticVoidMethod(test_class_A, test_staticN_id, action);
+ }
+
+ JNIEXPORT jint JNICALL Java_ClassInitBarrier_00024Test_testGetStaticJNI(JNIEnv* env, jclass cls, jobject action) {
+ jint v = env->GetStaticIntField(test_class_A, test_staticF_id); // int v = A.staticF;
+ env->CallVoidMethod(action, methodId); // action.run();
+ return v;
+ }
+
+ JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testPutStaticJNI(JNIEnv* env, jclass cls, jobject action) {
+ env->SetStaticIntField(test_class_A, test_staticF_id, 1); // A.staticF = 1;
+ env->CallVoidMethod(action, methodId); // action.run();
+ }
+
+ JNIEXPORT jobject JNICALL Java_ClassInitBarrier_00024Test_testNewInstanceAJNI(JNIEnv* env, jclass cls, jobject action) {
+ jobject obj = env->AllocObject(test_class_A); // A obj = new A();
+ if (env->ExceptionOccurred()) {
+ return NULL;
+ } else if (obj == NULL) {
+ jclass errorClass = env->FindClass("java/lang/AssertionError");
+ int ret = env->ThrowNew(errorClass, "JNI: AllocObject: allocation failed, but no exception thrown");
+ return NULL;
+ }
+ env->CallVoidMethod(action, methodId); // action.run();
+ return obj;
+ }
+
+ JNIEXPORT jobject JNICALL Java_ClassInitBarrier_00024Test_testNewInstanceBJNI(JNIEnv* env, jclass cls, jobject action) {
+ jobject obj = env->AllocObject(test_class_B); // B obj = new B();
+ if (env->ExceptionOccurred()) {
+ return NULL;
+ } else if (obj == NULL) {
+ jclass errorClass = env->FindClass("java/lang/AssertionError");
+ int ret = env->ThrowNew(errorClass, "JNI: AllocObject: allocation failed, but no exception thrown");
+ return NULL;
+ }
+ env->CallVoidMethod(action, methodId); // action.run();
+ return obj;
+ }
+
+ JNIEXPORT jint JNICALL Java_ClassInitBarrier_00024Test_testGetFieldJNI(JNIEnv* env, jclass cls, jobject recv, jobject action) {
+ jint v = env->GetIntField(recv, test_A_f_id); // int v = recv.f;
+ env->CallVoidMethod(action, methodId); // action.run();
+ return v;
+ }
+
+ JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testPutFieldJNI(JNIEnv* env, jclass cls, jobject recv, jobject action) {
+ env->SetIntField(recv, test_A_f_id, 1); // A.staticF = 1;
+ env->CallVoidMethod(action, methodId); // action.run();
+ }
+
+ JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeVirtualJNI(JNIEnv* env, jclass cls, jobject recv, jobject action) {
+ env->CallVoidMethod(recv, test_A_m_id); // recv.m();
+ if (env->ExceptionOccurred()) {
+ return;
+ }
+ env->CallVoidMethod(action, methodId); // action.run();
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/condy/staticInit/Example.jasm Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+// This class gets an initialization error in a condy invokestatic. Need jasm so that StaticInit isn't
+// initialized before the condy call.
+// Test that second invocation gets same error as first.
+
+public class Example
+ version 55:0
+{
+
+
+static Method $jacocoInit:"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;"
+ stack 1 locals 3
+{
+ invokestatic Method StaticInit.get:"()Ljava/lang/Object;";
+ areturn;
+}
+
+public static Method foo:"()V"
+ stack 1 locals 2
+{
+ ldc Dynamic REF_invokeStatic:Example.$jacocoInit:"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;":$jacocoData:"Ljava/lang/Object;";
+ astore_1;
+ return;
+}
+
+public static Method main:"([Ljava/lang/String;)V"
+ stack 1 locals 2
+{
+ try t0;
+ invokestatic Method Example.foo:"()V";
+ endtry t0;
+ goto L7;
+ catch t0 java/lang/Error;
+ stack_frame_type stack1;
+ stack_map class java/lang/Error;
+ astore_1;
+ aload_1;
+ invokevirtual Method java/lang/Error.printStackTrace:"()V";
+ L7: stack_frame_type same;
+ invokestatic Method Example.foo:"()V";
+ return;
+}
+} // end Class Example
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/condy/staticInit/StaticInit.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+class StaticInit {
+ static {
+ if (true)
+ throw new RuntimeException();
+ }
+
+ static Object get() {
+ return new Object();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/condy/staticInit/TestInitException.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/*
+ * @test
+ * @bug 8228485
+ * @summary Correctly handle initialization error for Condy BSM.
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib
+ * @compile Example.jasm
+ * @compile StaticInit.java
+ * @run main/othervm TestInitException
+ */
+
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class TestInitException {
+ public static void main(java.lang.String[] unused) throws Exception {
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("Example");
+ OutputAnalyzer oa = new OutputAnalyzer(pb.start());
+ // First call stack trace
+ oa.shouldContain("at Example.$jacocoInit(Example.jasm)");
+ oa.shouldContain("Caused by: java.lang.RuntimeException");
+ oa.shouldContain("at StaticInit.<clinit>(StaticInit.java:27)");
+ // Second call stack trace, with the message
+ oa.shouldContain("java.lang.ExceptionInInitializerError: $jacocoData");
+ oa.shouldContain("at Example.foo(Example.jasm)");
+ oa.shouldContain("at Example.main(Example.jasm)");
+ oa.shouldHaveExitValue(1);
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/attach/ConcAttachTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/*
+ * @test
+ * @bug 8225690
+ * @requires os.family != "windows"
+ * @library /test/lib
+ * @modules jdk.attach/com.sun.tools.attach
+ * @run main ConcAttachTest
+ */
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import com.sun.tools.attach.VirtualMachine;
+import com.sun.tools.attach.AttachNotSupportedException;
+
+import jdk.test.lib.apps.LingeredApp;
+import jdk.test.lib.Asserts;
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class ConcAttachTest implements Runnable {
+
+ private static final int NUM_CONC_REQUESTS = 100;
+
+ private static final int THREAD_POOL_TIMEOUT_IN_SEC = 30;
+
+ private static CountDownLatch latch;
+
+ private static String strPID;
+
+ // Attach to LingeredApp concurrently.
+ public void run() {
+ VirtualMachine vm = null;
+
+ try {
+ latch.countDown();
+ latch.await();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
+ try {
+ vm = VirtualMachine.attach(strPID);
+ } catch (AttachNotSupportedException | IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ try {
+ vm.detach();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private static void checkAttachListenerThread() throws InterruptedException, IOException {
+ JDKToolLauncher jcmd = JDKToolLauncher.createUsingTestJDK("jcmd");
+ jcmd.addToolArg(strPID);
+ jcmd.addToolArg("Thread.print");
+
+ ProcessBuilder pb = new ProcessBuilder(jcmd.getCommand());
+ Process jcmdProc = pb.start();
+
+ OutputAnalyzer out = new OutputAnalyzer(jcmdProc);
+
+ jcmdProc.waitFor();
+
+ System.out.println(out.getStdout());
+ System.err.println(out.getStderr());
+
+ long numOfAttachListener = out.asLines()
+ .stream()
+ .filter(l -> l.contains("Attach Listener"))
+ .count();
+
+ Asserts.assertEquals(1L, numOfAttachListener, "AttachListener should exist only 1 thread.");
+ }
+
+ public static void main(String... args) throws Exception {
+ LingeredApp app = null;
+ latch = new CountDownLatch(NUM_CONC_REQUESTS);
+ ExecutorService pool = Executors.newFixedThreadPool(NUM_CONC_REQUESTS);
+
+ try {
+ app = LingeredApp.startApp();
+ strPID = Long.toString(app.getPid());
+
+ for (int i = 0; i < NUM_CONC_REQUESTS; i++) {
+ pool.submit(new ConcAttachTest());
+ }
+
+ pool.shutdown();
+ pool.awaitTermination(THREAD_POOL_TIMEOUT_IN_SEC, TimeUnit.SECONDS);
+
+ checkAttachListenerThread();
+ } finally {
+ LingeredApp.stopApp(app);
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/attach/RemovingUnixDomainSocketTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/*
+ * @test
+ * @bug 8225193
+ * @requires os.family != "windows"
+ * @library /test/lib
+ * @run main RemovingUnixDomainSocketTest
+ */
+
+import java.io.IOException;
+import java.nio.file.Path;
+
+import jdk.test.lib.apps.LingeredApp;
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class RemovingUnixDomainSocketTest {
+
+ private static void runJCmd(long pid) throws InterruptedException, IOException {
+ JDKToolLauncher jcmd = JDKToolLauncher.createUsingTestJDK("jcmd");
+ jcmd.addToolArg(Long.toString(pid));
+ jcmd.addToolArg("VM.version");
+
+ ProcessBuilder pb = new ProcessBuilder(jcmd.getCommand());
+ Process jcmdProc = pb.start();
+
+ OutputAnalyzer out = new OutputAnalyzer(jcmdProc);
+
+ jcmdProc.waitFor();
+
+ System.out.println(out.getStdout());
+ System.err.println(out.getStderr());
+
+ out.stderrShouldBeEmpty();
+ }
+
+ public static void main(String... args) throws Exception {
+ LingeredApp app = null;
+ try {
+ app = LingeredApp.startApp();
+
+ // Access to Attach Listener
+ runJCmd(app.getPid());
+
+ // Remove unix domain socket file
+ var sockFile = Path.of(System.getProperty("java.io.tmpdir"),
+ ".java_pid" + app.getPid())
+ .toFile();
+ System.out.println("Remove " + sockFile.toString());
+ sockFile.delete();
+
+ // Access to Attach Listener again
+ runJCmd(app.getPid());
+ } finally {
+ LingeredApp.stopApp(app);
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jaxp/javax/xml/jaxp/unittest/transform/ErrorListenerTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2019, 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 transform;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.PrintStream;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXSource;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.xml.sax.InputSource;
+
+/*
+ * @test
+ * @bug 8157830
+ * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest
+ * @run testng/othervm transform.ErrorListenerTest
+ * @summary Verifies that ErrorListeners are handled properly
+ */
+public class ErrorListenerTest {
+
+ static final private String INVALID_STYLESHEET = "xxx";
+ static final private String SYSTEM_ID = "http://openjdk_java_net/xsl/dummy.xsl";
+
+ PrintStream original;
+
+ @BeforeClass
+ public void setUpClass() throws Exception {
+ // save the PrintStream
+ original = System.err;
+ }
+
+ @AfterClass
+ protected void tearDown() throws Exception {
+ // set back to the original
+ System.setErr(original);
+ }
+
+ /**
+ * Verifies that when an ErrorListener is registered, parser errors are passed
+ * onto the listener without other output.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void test() throws Exception {
+ InputStream is = new ByteArrayInputStream(INVALID_STYLESHEET.getBytes());
+ InputSource source = new InputSource(is);
+ source.setSystemId(SYSTEM_ID);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+ System.setErr(ps);
+
+ TransformerFactory factory = TransformerFactory.newInstance();
+ factory.setErrorListener(new ErrListener());
+
+ try {
+ factory.newTransformer(new SAXSource(source));
+ } catch (TransformerConfigurationException e) {
+ // nothing
+ }
+
+ // all errors are handled by the ErrorListener, no other output
+ Assert.assertEquals(baos.toString(), "");
+
+ }
+
+ class ErrListener implements ErrorListener {
+
+ @Override
+ public void error(TransformerException exception)
+ throws TransformerException {
+ System.out.println("Correctly handled error: " + exception.getMessage());
+ }
+
+ @Override
+ public void fatalError(TransformerException exception)
+ throws TransformerException {
+ System.out.println("Correctly handled fatal: " + exception.getMessage());
+ }
+
+ @Override
+ public void warning(TransformerException exception)
+ throws TransformerException {
+ System.out.println("Correctly handled warning: " + exception.getMessage());
+ }
+ }
+}
--- a/test/jaxp/javax/xml/jaxp/unittest/validation/ValidationTest.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jaxp/javax/xml/jaxp/unittest/validation/ValidationTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -36,11 +36,12 @@
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
+import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/*
* @test
- * @bug 8220818
+ * @bug 8220818 8176447
* @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest
* @run testng/othervm validation.ValidationTest
* @summary Runs validations with schemas and sources
@@ -71,6 +72,17 @@
};
}
+ /*
+ DataProvider: uniqueness
+ */
+ @DataProvider(name = "uniqueness")
+ Object[][] getUniqueData() {
+ return new Object[][]{
+ {"JDK8176447a.xsd", "JDK8176447a.xml"},
+ {"JDK8176447b.xsd", "JDK8176447b.xml"},
+ };
+ }
+
@Test(dataProvider = "invalid", expectedExceptions = SAXParseException.class)
public void testValidateRefType(String xsd, String xml) throws Exception {
validate(xsd, xml);
@@ -81,6 +93,19 @@
validate(xsd, xml);
}
+ /**
+ * @bug 8176447
+ * Verifies that the uniqueness constraint is checked.
+ * @param xsd the XSD
+ * @param xml the XML
+ * @throws Exception expected when the uniqueness constraint is validated
+ * correctly.
+ */
+ @Test(dataProvider = "uniqueness", expectedExceptions = SAXException.class)
+ public void testUnique(String xsd, String xml) throws Exception {
+ validate(xsd, xml);
+ }
+
private void validate(String xsd, String xml) throws Exception {
final SchemaFactory schemaFactory = SchemaFactory.newInstance(
XMLConstants.W3C_XML_SCHEMA_NS_URI);
@@ -90,4 +115,5 @@
validator.validate(new StreamSource(
new File(getClass().getResource(FILE_PATH + xml).getFile())));
}
+
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jaxp/javax/xml/jaxp/unittest/validation/files/JDK8176447a.xml Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,8 @@
+<test xmlns="http://openjdk_java_net/test.xml"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="JDK8176447a.xsd">
+ <innerObject>
+ <innerInnerObject test-unique-attribute="1" />
+ <innerInnerObject test-unique-attribute="1" />
+ </innerObject>
+</test>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jaxp/javax/xml/jaxp/unittest/validation/files/JDK8176447a.xsd Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:h="http://www.w3.org/1999/xhtml"
+ xmlns:sn="http://openjdk_java_net/test.xml"
+ targetNamespace="http://openjdk_java_net/test.xml" elementFormDefault="qualified">
+ <xsd:element name="test" type="sn:object">
+ <xsd:unique name="testunique">
+ <xsd:selector xpath="sn:innerObject"/>
+ <xsd:field xpath="sn:innerInnerObject/@test-unique-attribute"/>
+ </xsd:unique>
+ </xsd:element>
+ <xsd:complexType name="object">
+ <xsd:sequence>
+ <xsd:element name="innerObject" maxOccurs="unbounded" type="sn:testType" />
+ </xsd:sequence>
+ </xsd:complexType>
+ <xsd:complexType name="testType">
+ <xsd:sequence>
+ <xsd:element name="innerInnerObject" maxOccurs="unbounded" type="sn:testObjectType"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ <xsd:complexType name="testObjectType">
+ <xsd:attribute use="optional" name="test-unique-attribute" type="xsd:int" />
+ </xsd:complexType>
+</xsd:schema>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jaxp/javax/xml/jaxp/unittest/validation/files/JDK8176447b.xml Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="JDK8176447b.xsd">
+ <e>
+ <e1 a1="a" >
+ <e2 a2="a"/>
+ <e2 a2="a"/>
+ </e1>
+ <e1 a1="a">
+ <e2 a2="b"/>
+ <e2 a2="a"/>
+ </e1>
+ </e>
+</root>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jaxp/javax/xml/jaxp/unittest/validation/files/JDK8176447b.xsd Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
+ <xs:element name="root">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="e" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element maxOccurs="unbounded" name="e1">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element maxOccurs="unbounded" name="e2">
+ <xs:complexType>
+ <xs:attribute name="a2" use="required" type="xs:NCName"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="a1" use="required" type="xs:NCName"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:key name="checkAttrib">
+ <xs:selector xpath=".//e1"/>
+ <xs:field xpath="@a1"/>
+ <xs:field xpath="e2/@a2"/>
+ </xs:key>
+
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
--- a/test/jdk/ProblemList.txt Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/ProblemList.txt Thu Jul 25 08:24:01 2019 -0400
@@ -114,7 +114,6 @@
# jdk_awt
-java/awt/Color/AlphaColorTest.java 8224825 linux-all
java/awt/event/MouseEvent/MouseClickTest/MouseClickTest.java 8168389 windows-all,macosx-all
java/awt/event/KeyEvent/SwallowKeyEvents/SwallowKeyEvents.java 8224055 macosx-all
java/awt/Focus/ActualFocusedWindowTest/ActualFocusedWindowBlockingTest.java 8168408 windows-all,macosx-all
@@ -186,7 +185,7 @@
java/awt/Mixing/AWT_Mixing/JTextFieldInGlassPaneOverlapping.java 8158801 windows-all
java/awt/Mixing/AWT_Mixing/JTextFieldOverlapping.java 8158801 windows-all
java/awt/Mixing/AWT_Mixing/JToggleButtonInGlassPaneOverlapping.java 8158801 windows-all
-java/awt/Mixing/AWT_Mixing/JToggleButtonOverlapping.java 8158801 windows-all
+java/awt/Mixing/AWT_Mixing/JToggleButtonOverlapping.java 8158801 windows-all
java/awt/Mixing/NonOpaqueInternalFrame.java 7124549 macosx-all
java/awt/Focus/ActualFocusedWindowTest/ActualFocusedWindowRetaining.java 6829264 generic-all
java/awt/datatransfer/DragImage/MultiResolutionDragImageTest.java 8080982 generic-all
@@ -284,7 +283,6 @@
java/awt/PrintJob/PrinterException.java 8196301 windows-all,macosx-all
java/awt/Choice/PopupPosTest/PopupPosTest.java 8197811 windows-all
java/awt/Choice/ChoiceMouseWheelTest/ChoiceMouseWheelTest.java 7100044 macosx-all,linux-all
-java/awt/Choice/ChoiceKeyEventReaction/ChoiceKeyEventReaction.java 8214469 macosx-all
java/awt/Component/CreateImage/CreateImage.java 8198334 windows-all
java/awt/Component/GetScreenLocTest/GetScreenLocTest.java 4753654 generic-all
java/awt/Component/SetEnabledPerformance/SetEnabledPerformance.java 8165863 macosx-all
@@ -481,7 +479,6 @@
java/awt/Modal/OnTop/OnTopModal4Test.java 8198666 macosx-all
java/awt/Modal/OnTop/OnTopModal5Test.java 8198666 macosx-all
java/awt/Modal/OnTop/OnTopModal6Test.java 8198666 macosx-all
-java/awt/Modal/OnTop/OnTopModal6Test.java 8198666 macosx-all
java/awt/Modal/OnTop/OnTopModeless1Test.java 8198666 macosx-all
java/awt/Modal/OnTop/OnTopModeless2Test.java 8198666 macosx-all
java/awt/Modal/OnTop/OnTopModeless3Test.java 8198666 macosx-all
@@ -508,7 +505,6 @@
java/awt/datatransfer/ConstructFlavoredObjectTest/ConstructFlavoredObjectTest.java 8202860 linux-all
java/awt/dnd/DisposeFrameOnDragCrash/DisposeFrameOnDragTest.java 8202790 macosx-all,linux-all
java/awt/FileDialog/FilenameFilterTest/FilenameFilterTest.java 8202882 linux-all
-java/awt/Frame/FramesGC/FramesGC.java 8079069 macosx-all
java/awt/dnd/MissingDragExitEventTest/MissingDragExitEventTest.java 8030121 macosx-all,linux-all
java/awt/Choice/ChoicePopupLocation/ChoicePopupLocation.java 8202931 macosx-all,linux-all
java/awt/Focus/NonFocusableBlockedOwnerTest/NonFocusableBlockedOwnerTest.java 7124275 macosx-all
@@ -656,13 +652,13 @@
sun/security/pkcs11/ec/TestKeyFactory.java 8026976 generic-all
sun/security/pkcs11/Secmod/AddTrustedCert.java 8180837 generic-all
sun/security/pkcs11/tls/TestKeyMaterial.java 8180837 generic-all
-sun/security/pkcs11/tls/tls12/FipsModeTLS12.java 8224954,8225678 windows-all,linux-all
sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java 8161536 generic-all
sun/security/tools/keytool/ListKeychainStore.sh 8156889 macosx-all
sun/security/tools/keytool/KeyToolTest.java 8224644 solaris-all
sun/security/tools/keytool/WeakAlg.java 8224644 solaris-all
+sun/security/tools/jarsigner/compatibility/SignTwice.java 8228341 windows-all
sun/security/tools/jarsigner/warnings/BadKeyUsageTest.java 8026393 generic-all
javax/net/ssl/ServerName/SSLEngineExplorerMatchedSNI.java 8212096 generic-all
@@ -677,6 +673,28 @@
security/infra/java/security/cert/CertPathValidator/certification/ActalisCA.java 8224768 generic-all
+sun/security/smartcardio/TestChannel.java 8039280 generic-all
+sun/security/smartcardio/TestConnect.java 8039280 generic-all
+sun/security/smartcardio/TestConnectAgain.java 8039280 generic-all
+sun/security/smartcardio/TestControl.java 8039280 generic-all
+sun/security/smartcardio/TestDefault.java 8039280 generic-all
+sun/security/smartcardio/TestDirect.java 8039280 generic-all
+sun/security/smartcardio/TestExclusive.java 8039280 generic-all
+sun/security/smartcardio/TestMultiplePresent.java 8039280 generic-all
+sun/security/smartcardio/TestPresent.java 8039280 generic-all
+sun/security/smartcardio/TestTransmit.java 8039280 generic-all
+com/sun/crypto/provider/Cipher/DES/PerformanceTest.java 8039280 generic-all
+com/sun/security/auth/callback/TextCallbackHandler/Default.java 8039280 generic-all
+com/sun/security/auth/callback/TextCallbackHandler/Password.java 8039280 generic-all
+com/sun/security/sasl/gsskerb/AuthOnly.java 8039280 generic-all
+com/sun/security/sasl/gsskerb/ConfSecurityLayer.java 8039280 generic-all
+com/sun/security/sasl/gsskerb/NoSecurityLayer.java 8039280 generic-all
+javax/security/auth/kerberos/KerberosHashEqualsTest.java 8039280 generic-all
+javax/security/auth/kerberos/KerberosTixDateTest.java 8039280 generic-all
+sun/security/provider/PolicyFile/GrantAllPermToExtWhenNoPolicy.java 8039280 generic-all
+sun/security/provider/PolicyParser/ExtDirsChange.java 8039280 generic-all
+sun/security/provider/PolicyParser/PrincipalExpansionError.java 8039280 generic-all
+
############################################################################
# jdk_sound
@@ -723,7 +741,7 @@
javax/swing/AbstractButton/6711682/bug6711682.java 8060765 windows-all,macosx-all
javax/swing/Action/8133039/bug8133039.java 8196089 windows-all,macosx-all
javax/swing/JComboBox/6559152/bug6559152.java 8196090 windows-all,macosx-all
-javax/swing/JComboBox/8032878/bug8032878.java 8196092 windows-all,macosx-all
+javax/swing/JComboBox/8032878/bug8032878.java 8196092,8196439 windows-all,macosx-all,linux-all
javax/swing/JComboBox/8057893/bug8057893.java 8169953 windows-all,macosx-all
javax/swing/JComboBox/8072767/bug8072767.java 8196093 windows-all,macosx-all
javax/swing/JComponent/4337267/bug4337267.java 8146451 windows-all
@@ -746,12 +764,11 @@
javax/swing/text/DefaultCaret/HidingSelection/MultiSelectionTest.java 8213562 linux-all
javax/swing/text/JTextComponent/5074573/bug5074573.java 8196100 windows-all
javax/swing/JFileChooser/6798062/bug6798062.java 8146446 windows-all
-javax/swing/JComboBox/8032878/bug8032878.java 8196439 linux-all
javax/swing/JComboBox/8182031/ComboPopupTest.java 8196465 linux-all,macosx-all
javax/swing/JFileChooser/6738668/bug6738668.java 8194946 generic-all
javax/swing/JFileChooser/8021253/bug8021253.java 8169954 windows-all,linux-all,macosx-all
javax/swing/JFileChooser/8062561/bug8062561.java 8196466 linux-all,macosx-all
-javax/swing/JInternalFrame/Test6325652.java 8196467 linux-all,macosx-all
+javax/swing/JInternalFrame/Test6325652.java 8224977 macosx-all
javax/swing/JInternalFrame/8145896/TestJInternalFrameMaximize.java 8194944 macosx-all
javax/swing/JLabel/6596966/bug6596966.java 8040914 macosx-all
javax/swing/JPopupMenu/4870644/bug4870644.java 8194130 macosx-all,linux-all
@@ -770,7 +787,6 @@
javax/swing/text/AbstractDocument/DocumentInsert/DocumentInsertAtWrongPositionTest.java 8198396 generic-all
javax/swing/JFileChooser/6868611/bug6868611.java 7059834 windows-all
javax/swing/SwingWorker/6493680/bug6493680.java 8198410 windows-all
-javax/swing/plaf/basic/BasicMenuUI/4983388/bug4983388.java 8042383 macosx-all
javax/swing/PopupFactory/6276087/NonOpaquePopupMenuTest.java 8065099 macosx-all
javax/swing/DataTransfer/8059739/bug8059739.java 8199074 generic-all
javax/swing/JCheckBox/8032667/bug8032667_image_diff.java 8199063 macosx-all
--- a/test/jdk/TEST.ROOT Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/TEST.ROOT Thu Jul 25 08:24:01 2019 -0400
@@ -37,7 +37,9 @@
# to determine additional characteristics of the system for use with the @requires tag.
# Note: compiled bootlibs code will be located in the folder 'bootClasses'
requires.extraPropDefns = ../jtreg-ext/requires/VMProps.java
-requires.extraPropDefns.bootlibs = ../lib/sun ../lib/jdk/test/lib/Platform.java
+requires.extraPropDefns.bootlibs = ../lib/sun \
+ ../lib/jdk/test/lib/Platform.java \
+ ../lib/jdk/test/lib/Container.java
requires.extraPropDefns.vmOpts = -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:bootClasses
requires.properties= \
sun.arch.data.model \
--- a/test/jdk/com/sun/java/swing/plaf/gtk/4928019/bug4928019.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/com/sun/java/swing/plaf/gtk/4928019/bug4928019.java Thu Jul 25 08:24:01 2019 -0400
@@ -24,6 +24,7 @@
/*
* @test
* @bug 4928019
+ * @key headful
* @summary Makes sure all the basic classes can be created with GTK.
* @author Scott Violet
*/
--- a/test/jdk/com/sun/java/swing/plaf/gtk/Test6635110.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/com/sun/java/swing/plaf/gtk/Test6635110.java Thu Jul 25 08:24:01 2019 -0400
@@ -23,6 +23,7 @@
/* @test
@bug 6635110
+ @key headful
@summary GTK icons should not throw NPE when called by non-GTK UI
@author Peter Zhelezniakov
@run main Test6635110
--- a/test/jdk/com/sun/java/swing/plaf/gtk/Test6963870.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/com/sun/java/swing/plaf/gtk/Test6963870.java Thu Jul 25 08:24:01 2019 -0400
@@ -23,6 +23,7 @@
/* @test
@bug 6963870
+ @key headful
@summary Tests that GTKPainter.ListTableFocusBorder.getBorderInsets()
doesn't return null
@author Peter Zhelezniakov
--- a/test/jdk/com/sun/management/OperatingSystemMXBean/GetCommittedVirtualMemorySize.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/com/sun/management/OperatingSystemMXBean/GetCommittedVirtualMemorySize.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -57,8 +57,7 @@
// Careful with these values.
private static final long MIN_SIZE_FOR_PASS = 1;
- // Max size for pass dynamically determined below
- private static long max_size_for_pass = Long.MAX_VALUE;
+ private static long MAX_SIZE_FOR_PASS = Long.MAX_VALUE;
private static boolean trace = false;
@@ -67,16 +66,6 @@
trace = true;
}
- // 4934082: On Linux, VM size *can* be larger than total swap
- // size. Linux might not reserve swap memory immediately when
- // a page is mmaped. This means that the reported committed
- // memory size can grow beyond the swap limit.
- long max_size = mbean.getTotalSwapSpaceSize() +
- mbean.getTotalPhysicalMemorySize();
-
- if (max_size > 0) {
- max_size_for_pass = max_size;
- }
long size = mbean.getCommittedVirtualMemorySize();
if (size == -1) {
System.out.println("getCommittedVirtualMemorySize() is not supported");
@@ -88,11 +77,11 @@
size);
}
- if (size < MIN_SIZE_FOR_PASS || size > max_size_for_pass) {
+ if (size < MIN_SIZE_FOR_PASS || size > MAX_SIZE_FOR_PASS) {
throw new RuntimeException("Committed virtual memory size " +
"illegal value: " + size + " bytes " +
"(MIN = " + MIN_SIZE_FOR_PASS + "; " +
- "MAX = " + max_size_for_pass + ")");
+ "MAX = " + MAX_SIZE_FOR_PASS + ")");
}
System.out.println("Test passed.");
--- a/test/jdk/java/awt/Choice/ChoiceKeyEventReaction/ChoiceKeyEventReaction.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/awt/Choice/ChoiceKeyEventReaction/ChoiceKeyEventReaction.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -30,18 +30,18 @@
* @run main ChoiceKeyEventReaction
*/
-import java.awt.Robot;
import java.awt.Choice;
-import java.awt.Point;
-import java.awt.Toolkit;
-import java.awt.TextField;
import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.Point;
+import java.awt.Robot;
+import java.awt.TextField;
+import java.awt.Toolkit;
import java.awt.event.InputEvent;
-import java.awt.event.KeyEvent;
import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
-import java.awt.event.ItemListener;
-import java.awt.Frame;
+import java.awt.event.KeyEvent;
public class ChoiceKeyEventReaction
{
@@ -60,6 +60,7 @@
try {
robot = new Robot();
robot.setAutoDelay(100);
+ robot.waitForIdle();
moveFocusToTextField();
testKeyOnChoice(InputEvent.BUTTON1_MASK, KeyEvent.VK_UP);
@@ -99,6 +100,7 @@
frame.add(choice1);
frame.setLayout (new FlowLayout());
frame.setSize (200,200);
+ frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
@@ -108,33 +110,31 @@
robot.mousePress(button);
robot.mouseRelease(button);
+ robot.waitForIdle();
robot.keyPress(key);
robot.keyRelease(key);
+ robot.waitForIdle();
System.out.println("keyTypedOnTextField = "+keyTypedOnTextField +": itemChanged = " + itemChanged);
if (itemChanged) {
throw new RuntimeException("Test failed. ItemChanged event occur on Choice.");
}
- // We may just write
- // if (toolkit.equals("sun.awt.windows.WToolkit") == keyTypedOnTextField) {fail;}
- // but must report differently in these cases so put two separate if statements for simplicity.
- if (toolkit.equals("sun.awt.windows.WToolkit") &&
- !keyTypedOnTextField) {
- throw new RuntimeException("Test failed. (Win32) KeyEvent wasn't addressed to TextField. ");
- }
+ // We may just write
+ // if (toolkit.equals("sun.awt.windows.WToolkit") == keyTypedOnTextField) {fail;}
+ // but must report differently in these cases so put two separate if statements for simplicity.
+ if (!toolkit.equals("sun.awt.X11.XToolkit") &&
+ !keyTypedOnTextField) {
+ throw new RuntimeException("Test failed. (Win32/MacOS) KeyEvent wasn't addressed to TextField. ");
+ }
- if (!toolkit.equals("sun.awt.windows.WToolkit") &&
- keyTypedOnTextField) {
- throw new RuntimeException("Test failed. (XToolkit/MToolkit). KeyEvent was addressed to TextField.");
+ if (toolkit.equals("sun.awt.X11.XToolkit") &&
+ keyTypedOnTextField) {
+ throw new RuntimeException("Test failed. (XToolkit/MToolkit). KeyEvent was addressed to TextField.");
}
System.out.println("Test passed. Unfocusable Choice doesn't react on keys.");
-
- //close opened choice
- robot.keyPress(KeyEvent.VK_ESCAPE);
- robot.keyRelease(KeyEvent.VK_ESCAPE);
}
public static void moveFocusToTextField() {
--- a/test/jdk/java/awt/Color/AlphaColorTest.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/awt/Color/AlphaColorTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -24,7 +24,7 @@
/**
* @test
* @key headful
- * @bug 8204931
+ * @bug 8204931 8227392 8224825
* @summary test alpha colors are blended with background.
*/
@@ -73,8 +73,14 @@
static Frame frame;
private static void createAndShowGUI() {
- frame = new Frame("Alpha Color Test");
- frame.setBackground(Color.black);
+ frame = new Frame("Alpha Color Test") {
+ @Override
+ public void paint(Graphics g) {
+ g.setColor(Color.black);
+ g.fillRect(0, 0, getWidth(), getHeight());
+ super.paint(g);
+ }
+ };
Color color = new Color(255, 255, 255, 127);
frame.add("Center", new AlphaColorTest(color));
frame.pack();
--- a/test/jdk/java/awt/GraphicsDevice/IncorrectDisplayModeExitFullscreen.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/awt/GraphicsDevice/IncorrectDisplayModeExitFullscreen.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -46,6 +46,7 @@
GraphicsEnvironment.getLocalGraphicsEnvironment()
.getScreenDevices();
if (devices.length < 2 || devices[0].getDisplayModes().length < 2
+ || !devices[0].isDisplayChangeSupported()
|| !devices[0].isFullScreenSupported()
|| !devices[1].isFullScreenSupported()) {
System.err.println("Testcase is not applicable");
--- a/test/jdk/java/lang/annotation/loaderLeak/Main.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/lang/annotation/loaderLeak/Main.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2019, 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
@@ -56,6 +56,7 @@
if (c.get() == null) throw new AssertionError();
System.gc();
System.gc();
+ Reference.reachabilityFence(loader);
loader = null;
// Might require multiple calls to System.gc() for weak-references
--- a/test/jdk/java/lang/invoke/AccessControlTest.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/lang/invoke/AccessControlTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -33,7 +33,6 @@
import java.lang.reflect.*;
import java.lang.reflect.Modifier;
import java.util.*;
-import org.testng.*;
import org.testng.annotations.*;
import static java.lang.invoke.MethodHandles.*;
@@ -62,23 +61,28 @@
private class LookupCase implements Comparable<LookupCase> {
final Lookup lookup;
final Class<?> lookupClass;
+ final Class<?> prevLookupClass;
final int lookupModes;
public LookupCase(Lookup lookup) {
this.lookup = lookup;
this.lookupClass = lookup.lookupClass();
+ this.prevLookupClass = lookup.previousLookupClass();
this.lookupModes = lookup.lookupModes();
+
assert(lookupString().equals(lookup.toString()));
numberOf(lookupClass().getClassLoader()); // assign CL#
}
- public LookupCase(Class<?> lookupClass, int lookupModes) {
+ public LookupCase(Class<?> lookupClass, Class<?> prevLookupClass, int lookupModes) {
this.lookup = null;
this.lookupClass = lookupClass;
+ this.prevLookupClass = prevLookupClass;
this.lookupModes = lookupModes;
numberOf(lookupClass().getClassLoader()); // assign CL#
}
- public final Class<?> lookupClass() { return lookupClass; }
- public final int lookupModes() { return lookupModes; }
+ public final Class<?> lookupClass() { return lookupClass; }
+ public final Class<?> prevLookupClass() { return prevLookupClass; }
+ public final int lookupModes() { return lookupModes; }
public Lookup lookup() { lookup.getClass(); return lookup; }
@@ -86,12 +90,24 @@
public int compareTo(LookupCase that) {
Class<?> c1 = this.lookupClass();
Class<?> c2 = that.lookupClass();
+ Class<?> p1 = this.prevLookupClass();
+ Class<?> p2 = that.prevLookupClass();
if (c1 != c2) {
int cmp = c1.getName().compareTo(c2.getName());
if (cmp != 0) return cmp;
cmp = numberOf(c1.getClassLoader()) - numberOf(c2.getClassLoader());
assert(cmp != 0);
return cmp;
+ } else if (p1 != p2){
+ if (p1 == null)
+ return 1;
+ else if (p2 == null)
+ return -1;
+ int cmp = p1.getName().compareTo(p2.getName());
+ if (cmp != 0) return cmp;
+ cmp = numberOf(p1.getClassLoader()) - numberOf(p2.getClassLoader());
+ assert(cmp != 0);
+ return cmp;
}
return -(this.lookupModes() - that.lookupModes());
}
@@ -102,6 +118,7 @@
}
public boolean equals(LookupCase that) {
return (this.lookupClass() == that.lookupClass() &&
+ this.prevLookupClass() == that.prevLookupClass() &&
this.lookupModes() == that.lookupModes());
}
@@ -113,20 +130,25 @@
/** Simulate all assertions in the spec. for Lookup.toString. */
private String lookupString() {
String name = lookupClass.getName();
+ if (prevLookupClass != null)
+ name += "/" + prevLookupClass.getName();
String suffix = "";
if (lookupModes == 0)
suffix = "/noaccess";
else if (lookupModes == PUBLIC)
suffix = "/public";
- else if (lookupModes == (PUBLIC|UNCONDITIONAL))
+ else if (lookupModes == UNCONDITIONAL)
suffix = "/publicLookup";
else if (lookupModes == (PUBLIC|MODULE))
suffix = "/module";
- else if (lookupModes == (PUBLIC|MODULE|PACKAGE))
+ else if (lookupModes == (PUBLIC|PACKAGE)
+ || lookupModes == (PUBLIC|MODULE|PACKAGE))
suffix = "/package";
- else if (lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE))
+ else if (lookupModes == (PUBLIC|PACKAGE|PRIVATE)
+ || lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE))
suffix = "/private";
- else if (lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED))
+ else if (lookupModes == (PUBLIC|PACKAGE|PRIVATE|PROTECTED)
+ || lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED))
suffix = "";
else
suffix = "/#"+Integer.toHexString(lookupModes);
@@ -138,41 +160,50 @@
* Creates a lookup on the specified new lookup class.
* [A1] The resulting object will report the specified
* class as its own {@link #lookupClass lookupClass}.
- * <p>
* [A2] However, the resulting {@code Lookup} object is guaranteed
* to have no more access capabilities than the original.
* In particular, access capabilities can be lost as follows:<ul>
- * <li> [A3] If the old lookup class is in a named module, and the new
- * lookup class is in a different module {@code M}, then no members, not
- * even public members in {@code M}'s exported packages, will be accessible.
- * The exception to this is when this lookup is publicLookup, in which case
- * public access is not lost.
- * <li> [A4] If the old lookup class is in an unnamed module, and the new
- * lookup class is a different module then module access is lost.
- * <li> [A5] If the new lookup class differs from the old one then UNCONDITIONAL
- * is lost. If the new lookup class is not within the same package member as the
- * old one, protected members will not be accessible by virtue of inheritance.
+ * [A3] If the new lookup class is in a different module from the old one,
+ * i.e. {@link #MODULE MODULE} access is lost.
+ * [A4] If the new lookup class is in a different package
+ * than the old one, protected and default (package) members will not be accessible,
+ * i.e. {@link #PROTECTED PROTECTED} and {@link #PACKAGE PACKAGE} access are lost.
+ * [A5] If the new lookup class is not within the same package member
+ * as the old one, private members will not be accessible, and protected members
+ * will not be accessible by virtue of inheritance,
+ * i.e. {@link #PRIVATE PRIVATE} access is lost.
* (Protected members may continue to be accessible because of package sharing.)
- * <li> [A6] If the new lookup class is in a different package than the old one,
- * protected and default (package) members will not be accessible.
- * <li> [A7] If the new lookup class is not within the same package member
- * as the old one, private members will not be accessible.
- * <li> [A8] If the new lookup class is not accessible to the old lookup class,
- * then no members, not even public members, will be accessible.
- * <li> [A9] (In all other cases, public members will continue to be accessible.)
- * </ul>
+ * [A6] If the new lookup class is not
+ * {@linkplain #accessClass(Class) accessible} to this lookup,
+ * then no members, not even public members, will be accessible
+ * i.e. all access modes are lost.
+ * [A7] If the new lookup class, the old lookup class and the previous lookup class
+ * are all in different modules i.e. teleporting to a third module,
+ * all access modes are lost.
+ * <p>
+ * The new previous lookup class is chosen as follows:
+ * [A8] If the new lookup object has {@link #UNCONDITIONAL UNCONDITIONAL} bit,
+ * the new previous lookup class is {@code null}.
+ * [A9] If the new lookup class is in the same module as the old lookup class,
+ * the new previous lookup class is the old previous lookup class.
+ * [A10] If the new lookup class is in a different module from the old lookup class,
+ * the new previous lookup class is the the old lookup class.
+ *
* Other than the above cases, the new lookup will have the same
- * access capabilities as the original. [A10]
+ * access capabilities as the original. [A11]
* <hr>
*/
public LookupCase in(Class<?> c2) {
Class<?> c1 = lookupClass();
- int m1 = lookupModes();
+ Module m1 = c1.getModule();
+ Module m2 = c2.getModule();
+ Module m0 = prevLookupClass() != null ? prevLookupClass.getModule() : c1.getModule();
+ int modes1 = lookupModes();
int changed = 0;
// for the purposes of access control then treat classes in different unnamed
// modules as being in the same module.
- boolean sameModule = (c1.getModule() == c2.getModule()) ||
- (!c1.getModule().isNamed() && !c2.getModule().isNamed());
+ boolean sameModule = (m1 == m2) ||
+ (!m1.isNamed() && !m2.isNamed());
boolean samePackage = (c1.getClassLoader() == c2.getClassLoader() &&
c1.getPackageName().equals(c2.getPackageName()));
boolean sameTopLevel = (topLevelClass(c1) == topLevelClass(c2));
@@ -180,40 +211,85 @@
assert(samePackage || !sameTopLevel);
assert(sameTopLevel || !sameClass);
boolean accessible = sameClass;
- if ((m1 & PACKAGE) != 0) accessible |= samePackage;
- if ((m1 & PUBLIC ) != 0) accessible |= (c2.getModifiers() & PUBLIC) != 0;
- if (!sameModule) {
- if (c1.getModule().isNamed() && (m1 & UNCONDITIONAL) == 0) {
- accessible = false; // [A3]
- } else {
- changed |= (MODULE|PACKAGE|PRIVATE|PROTECTED); // [A3] [A4]
- }
+
+ if ((modes1 & PACKAGE) != 0) accessible |= samePackage;
+ if ((modes1 & PUBLIC ) != 0) {
+ if (isModuleAccessible(c2))
+ accessible |= (c2.getModifiers() & PUBLIC) != 0;
+ else
+ accessible = false;
+ }
+ if ((modes1 & UNCONDITIONAL) != 0) {
+ if (m2.isExported(c2.getPackageName()))
+ accessible |= (c2.getModifiers() & PUBLIC) != 0;
+ else
+ accessible = false;
}
if (!accessible) {
- // Different package and no access to c2; lose all access.
- changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED); // [A8]
+ // no access to c2; lose all access.
+ changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED|UNCONDITIONAL); // [A6]
+ }
+ if (m2 != m1 && m0 != m1) {
+ // hop to a third module; lose all access
+ changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED); // [A7]
+ }
+ if (!sameModule) {
+ changed |= MODULE; // [A3]
}
if (!samePackage) {
// Different package; loose PACKAGE and lower access.
- changed |= (PACKAGE|PRIVATE|PROTECTED); // [A6]
+ changed |= (PACKAGE|PRIVATE|PROTECTED); // [A4]
}
if (!sameTopLevel) {
// Different top-level class. Lose PRIVATE and PROTECTED access.
- changed |= (PRIVATE|PROTECTED); // [A5] [A7]
+ changed |= (PRIVATE|PROTECTED); // [A5]
}
- if (!sameClass) {
- changed |= (UNCONDITIONAL); // [A5]
- } else {
- assert(changed == 0); // [A10] (no deprivation if same class)
+ if (sameClass) {
+ assert(changed == 0); // [A11] (no deprivation if same class)
}
- if (accessible) assert((changed & PUBLIC) == 0); // [A9]
- int m2 = m1 & ~changed;
- LookupCase l2 = new LookupCase(c2, m2);
- assert(l2.lookupClass() == c2); // [A1]
- assert((m1 | m2) == m1); // [A2] (no elevation of access)
+
+ if (accessible) assert((changed & PUBLIC) == 0);
+ int modes2 = modes1 & ~changed;
+ Class<?> plc = (m1 == m2) ? prevLookupClass() : c1; // [A9] [A10]
+ if ((modes1 & UNCONDITIONAL) != 0) plc = null; // [A8]
+ LookupCase l2 = new LookupCase(c2, plc, modes2);
+ assert(l2.lookupClass() == c2); // [A1]
+ assert((modes1 | modes2) == modes1); // [A2] (no elevation of access)
+ assert(l2.prevLookupClass() == null || (modes2 & MODULE) == 0);
return l2;
}
+ LookupCase dropLookupMode(int modeToDrop) {
+ int oldModes = lookupModes();
+ int newModes = oldModes & ~(modeToDrop | PROTECTED);
+ switch (modeToDrop) {
+ case PUBLIC: newModes &= ~(MODULE|PACKAGE|PROTECTED|PRIVATE); break;
+ case MODULE: newModes &= ~(PACKAGE|PRIVATE); break;
+ case PACKAGE: newModes &= ~(PRIVATE); break;
+ case PROTECTED:
+ case PRIVATE:
+ case UNCONDITIONAL: break;
+ default: throw new IllegalArgumentException(modeToDrop + " is not a valid mode to drop");
+ }
+ if (newModes == oldModes) return this; // return self if no change
+ LookupCase l2 = new LookupCase(lookupClass(), prevLookupClass(), newModes);
+ assert((oldModes | newModes) == oldModes); // [A2] (no elevation of access)
+ assert(l2.prevLookupClass() == null || (newModes & MODULE) == 0);
+ return l2;
+ }
+
+ boolean isModuleAccessible(Class<?> c) {
+ Module m1 = lookupClass().getModule();
+ Module m2 = c.getModule();
+ Module m0 = prevLookupClass() != null ? prevLookupClass.getModule() : m1;
+ String pn = c.getPackageName();
+ boolean accessible = m1.canRead(m2) && m2.isExported(pn, m1);
+ if (m1 != m0) {
+ accessible = accessible && m0.canRead(m2) && m2.isExported(pn, m0);
+ }
+ return accessible;
+ }
+
@Override
public String toString() {
String s = lookupClass().getSimpleName();
@@ -229,33 +305,48 @@
public boolean willAccess(Method m) {
Class<?> c1 = lookupClass();
Class<?> c2 = m.getDeclaringClass();
+ Module m1 = c1.getModule();
+ Module m2 = c2.getModule();
+ Module m0 = prevLookupClass != null ? prevLookupClass.getModule() : m1;
+ // unconditional has access to all public types/members of types that is in a package
+ // are unconditionally exported
+ if ((lookupModes & UNCONDITIONAL) != 0) {
+ return m2.isExported(c2.getPackageName())
+ && Modifier.isPublic(c2.getModifiers())
+ && Modifier.isPublic(m.getModifiers());
+ }
- // publicLookup has access to all public types/members of types in unnamed modules
- if ((lookupModes & UNCONDITIONAL) != 0
- && (lookupModes & PUBLIC) != 0
- && !c2.getModule().isNamed()
- && Modifier.isPublic(c2.getModifiers())
- && Modifier.isPublic(m.getModifiers()))
- return true;
+ // c1 and c2 are in different module
+ if (m1 != m2 || m0 != m2) {
+ return (lookupModes & PUBLIC) != 0
+ && isModuleAccessible(c2)
+ && Modifier.isPublic(c2.getModifiers())
+ && Modifier.isPublic(m.getModifiers());
+ }
+
+ assert(m1 == m2 && prevLookupClass == null);
+
+ if (!willAccessClass(c2, false))
+ return false;
LookupCase lc = this.in(c2);
- int m1 = lc.lookupModes();
- int m2 = fixMods(m.getModifiers());
+ int modes1 = lc.lookupModes();
+ int modes2 = fixMods(m.getModifiers());
// allow private lookup on nestmates. Otherwise, privacy is strictly enforced
- if (c1 != c2 && ((m2 & PRIVATE) == 0 || !c1.isNestmateOf(c2))) {
- m1 &= ~PRIVATE;
+ if (c1 != c2 && ((modes2 & PRIVATE) == 0 || !c1.isNestmateOf(c2))) {
+ modes1 &= ~PRIVATE;
}
// protected access is sometimes allowed
- if ((m2 & PROTECTED) != 0) {
- int prev = m2;
- m2 |= PACKAGE; // it acts like a package method also
+ if ((modes2 & PROTECTED) != 0) {
+ int prev = modes2;
+ modes2 |= PACKAGE; // it acts like a package method also
if ((lookupModes() & PROTECTED) != 0 &&
c2.isAssignableFrom(c1))
- m2 |= PUBLIC; // from a subclass, it acts like a public method also
+ modes2 |= PUBLIC; // from a subclass, it acts like a public method also
}
if (verbosity >= 2)
- System.out.format("%s willAccess %s m1=0x%h m2=0x%h => %s%n", this, lc, m1, m2, ((m2 & m1) != 0));
- return (m2 & m1) != 0;
+ System.out.format("%s willAccess %s modes1=0x%h modes2=0x%h => %s%n", lookupString(), lc.lookupString(), modes1, modes2, (modes2 & modes1) != 0);
+ return (modes2 & modes1) != 0;
}
/** Predict the success or failure of accessing this class. */
@@ -268,24 +359,36 @@
}
}
- // publicLookup has access to all public types/members of types in unnamed modules
- if ((lookupModes & UNCONDITIONAL) != 0
- && (lookupModes & PUBLIC) != 0
- && (!c2.getModule().isNamed())
- && Modifier.isPublic(c2.getModifiers()))
- return true;
+ Module m1 = c1.getModule();
+ Module m2 = c2.getModule();
+ Module m0 = prevLookupClass != null ? prevLookupClass.getModule() : m1;
+ // unconditional has access to all public types that is in an unconditionally exported package
+ if ((lookupModes & UNCONDITIONAL) != 0) {
+ return m2.isExported(c2.getPackageName()) && Modifier.isPublic(c2.getModifiers());
+ }
+ // c1 and c2 are in different module
+ if (m1 != m2 || m0 != m2) {
+ return (lookupModes & PUBLIC) != 0
+ && isModuleAccessible(c2)
+ && Modifier.isPublic(c2.getModifiers());
+ }
+
+ assert(m1 == m2 && prevLookupClass == null);
LookupCase lc = this.in(c2);
- int m1 = lc.lookupModes();
+ int modes1 = lc.lookupModes();
boolean r = false;
- if (m1 == 0) {
+ if (modes1 == 0) {
r = false;
} else {
- int m2 = fixMods(c2.getModifiers());
- if ((m2 & PUBLIC) != 0) {
- r = true;
- } else if ((m1 & PACKAGE) != 0 && c1.getPackage() == c2.getPackage()) {
- r = true;
+ if (Modifier.isPublic(c2.getModifiers())) {
+ if ((modes1 & MODULE) != 0)
+ r = true;
+ else if ((modes1 & PUBLIC) != 0)
+ r = m1.isExported(c2.getPackageName());
+ } else {
+ if ((modes1 & PACKAGE) != 0 && c1.getPackage() == c2.getPackage())
+ r = true;
}
}
if (verbosity >= 2) {
@@ -328,7 +431,7 @@
return i+1;
}
- private void addLookupEdge(LookupCase l1, Class<?> c2, LookupCase l2) {
+ private void addLookupEdge(LookupCase l1, Class<?> c2, LookupCase l2, int dropAccess) {
TreeSet<LookupCase> edges = CASE_EDGES.get(l2);
if (edges == null) CASE_EDGES.put(l2, edges = new TreeSet<>());
if (edges.add(l1)) {
@@ -337,7 +440,7 @@
int m1 = l1.lookupModes();
int m2 = l2.lookupModes();
assert((m1 | m2) == m1); // [A2] (no elevation of access)
- LookupCase expect = l1.in(c2);
+ LookupCase expect = dropAccess == 0 ? l1.in(c2) : l1.in(c2).dropLookupMode(dropAccess);
if (!expect.equals(l2))
System.out.println("*** expect "+l1+" => "+expect+" but got "+l2);
assertEquals(l2, expect);
@@ -358,9 +461,14 @@
for (int lastCount = -1; lastCount != CASES.size(); ) {
lastCount = CASES.size(); // if CASES grow in the loop we go round again
for (LookupCase lc1 : CASES.toArray(new LookupCase[0])) {
+ for (int mode : ACCESS_CASES) {
+ LookupCase lc2 = new LookupCase(lc1.lookup().dropLookupMode(mode));
+ addLookupEdge(lc1, lc1.lookupClass(), lc2, mode);
+ CASES.add(lc2);
+ }
for (Class<?> c2 : classes) {
LookupCase lc2 = new LookupCase(lc1.lookup().in(c2));
- addLookupEdge(lc1, c2, lc2);
+ addLookupEdge(lc1, c2, lc2, 0);
CASES.add(lc2);
}
}
@@ -386,8 +494,8 @@
if (verbosity > 0) {
verbosity += 9;
Method pro_in_self = targetMethod(THIS_CLASS, PROTECTED, methodType(void.class));
- testOneAccess(lookupCase("AccessControlTest/public"), pro_in_self, "find");
- testOneAccess(lookupCase("Remote_subclass/public"), pro_in_self, "find");
+ testOneAccess(lookupCase("AccessControlTest/module"), pro_in_self, "find");
+ testOneAccess(lookupCase("Remote_subclass/module"), pro_in_self, "find");
testOneAccess(lookupCase("Remote_subclass"), pro_in_self, "find");
verbosity -= 9;
}
@@ -398,6 +506,8 @@
String targetPlace = placeName(targetClass);
if (targetPlace == null) continue; // Object, String, not a target
for (int targetAccess : ACCESS_CASES) {
+ if (targetAccess == MODULE || targetAccess == UNCONDITIONAL)
+ continue;
MethodType methodType = methodType(void.class);
Method method = targetMethod(targetClass, targetAccess, methodType);
// Try to access target method from various contexts.
@@ -457,7 +567,6 @@
}
static Method targetMethod(Class<?> targetClass, int targetAccess, MethodType methodType) {
- assert targetAccess != MODULE;
String methodName = accessName(targetAccess)+placeName(targetClass);
if (verbosity >= 2)
System.out.println(targetClass.getSimpleName()+"."+methodName+methodType);
@@ -491,10 +600,13 @@
assert(false);
return "?";
}
- // MODULE not a test case at this time
private static final int[] ACCESS_CASES = {
- PUBLIC, PACKAGE, PRIVATE, PROTECTED
+ PUBLIC, PACKAGE, PRIVATE, PROTECTED, MODULE, UNCONDITIONAL
};
+ /*
+ * Adjust PUBLIC => PUBLIC|MODULE|UNCONDITIONAL
+ * Adjust 0 => PACKAGE
+ */
/** Return one of the ACCESS_CASES. */
static int fixMods(int mods) {
mods &= (PUBLIC|PRIVATE|PROTECTED);
--- a/test/jdk/java/lang/invoke/DropLookupModeTest.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/lang/invoke/DropLookupModeTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -106,37 +106,39 @@
assertTrue(lookup.lookupModes() == 0);
}
- /**
- * Test dropLookupMode on the public Lookup.
- */
- public void testPublicLookup() {
- final Lookup publicLookup = MethodHandles.publicLookup();
- final Class<?> lc = publicLookup.lookupClass();
- assertTrue(publicLookup.lookupModes() == (PUBLIC|UNCONDITIONAL));
-
- Lookup lookup = publicLookup.dropLookupMode(PRIVATE);
- assertTrue(lookup.lookupClass() == lc);
- assertTrue(lookup.lookupModes() == PUBLIC);
-
- lookup = publicLookup.dropLookupMode(PROTECTED);
- assertTrue(lookup.lookupClass() == lc);
- assertTrue(lookup.lookupModes() == PUBLIC);
+ @DataProvider(name = "unconditionals")
+ public Object[][] unconditionals() {
+ Lookup publicLookup = MethodHandles.publicLookup();
+ return new Object[][] {
+ { publicLookup, Object.class },
+ { publicLookup.in(String.class), String.class },
+ { publicLookup.in(DropLookupModeTest.class), DropLookupModeTest.class },
+ };
+ }
- lookup = publicLookup.dropLookupMode(PACKAGE);
- assertTrue(lookup.lookupClass() == lc);
- assertTrue(lookup.lookupModes() == PUBLIC);
-
- lookup = publicLookup.dropLookupMode(MODULE);
- assertTrue(lookup.lookupClass() == lc);
- assertTrue(lookup.lookupModes() == PUBLIC);
+ /**
+ * Test dropLookupMode on the lookup with public lookup
+ * and UNCONDITIONAL
+ */
+ @Test(dataProvider = "unconditionals")
+ public void testUnconditionalLookup(Lookup unconditionalLookup, Class<?> expected) {
+ assertTrue(unconditionalLookup.lookupModes() == UNCONDITIONAL);
- lookup = publicLookup.dropLookupMode(PUBLIC);
- assertTrue(lookup.lookupClass() == lc);
- assertTrue(lookup.lookupModes() == 0);
+ assertPublicLookup(unconditionalLookup.dropLookupMode(PRIVATE), expected);
+ assertPublicLookup(unconditionalLookup.dropLookupMode(PROTECTED), expected);
+ assertPublicLookup(unconditionalLookup.dropLookupMode(PACKAGE), expected);
+ assertPublicLookup(unconditionalLookup.dropLookupMode(MODULE), expected);
+ assertPublicLookup(unconditionalLookup.dropLookupMode(PUBLIC), expected);
- lookup = publicLookup.dropLookupMode(UNCONDITIONAL);
- assertTrue(lookup.lookupClass() == lc);
- assertTrue(lookup.lookupModes() == PUBLIC);
+ // drop all access
+ Lookup lookup = unconditionalLookup.dropLookupMode(UNCONDITIONAL);
+ assertTrue(lookup.lookupClass() == expected);
+ assertTrue(lookup.lookupModes() == 0);
+ }
+
+ private void assertPublicLookup(Lookup lookup, Class<?> expected) {
+ assertTrue(lookup.lookupClass() == expected);
+ assertTrue(lookup.lookupModes() == UNCONDITIONAL);
}
@DataProvider(name = "badInput")
@@ -157,4 +159,4 @@
MethodHandles.lookup().dropLookupMode(modeToDrop);
}
-}
\ No newline at end of file
+}
--- a/test/jdk/java/lang/invoke/MethodHandles/privateLookupIn/test/p/PrivateLookupInTests.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/lang/invoke/MethodHandles/privateLookupIn/test/p/PrivateLookupInTests.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -82,6 +82,7 @@
}
// Invoke MethodHandles.privateLookupIn with a reduced-power caller
+ @Test(expectedExceptions = {IllegalAccessException.class})
public void testReducedAccessCallerSameModule() throws Throwable {
Lookup caller = MethodHandles.lookup().dropLookupMode(PACKAGE);
assertTrue((caller.lookupModes() & PRIVATE) == 0);
@@ -89,12 +90,6 @@
assertTrue((caller.lookupModes() & MODULE) != 0);
Lookup lookup = MethodHandles.privateLookupIn(nonPublicType, caller);
- assertTrue(lookup.lookupClass() == nonPublicType);
- assertTrue(lookup.hasPrivateAccess());
-
- // use it
- MethodHandle mh = lookup.findStaticGetter(nonPublicType, "obj", Object.class);
- Object obj = mh.invokeExact();
}
// Invoke MethodHandles.privateLookupIn with the public lookup as caller
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/Driver1.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/**
+ * @test
+ * @bug 8173978
+ * @build m3/* m4/* m5/* Unnamed Unnamed1
+ * @run testng/othervm m3/jdk.test.ModuleAccessTest
+ * @summary Basic test case for module access checks and Lookup.in and
+ * MethodHandles.privateLookupIn
+ */
--- a/test/jdk/java/lang/invoke/modules/Unnamed.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/lang/invoke/modules/Unnamed.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -21,4 +21,11 @@
* questions.
*/
-public class Unnamed { }
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+public class Unnamed {
+ public static Lookup lookup() {
+ return MethodHandles.lookup();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/Unnamed1.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+public class Unnamed1 { }
--- a/test/jdk/java/lang/invoke/modules/m1/p1/Main.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/lang/invoke/modules/m1/p1/Main.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -45,7 +45,7 @@
private Class<?> p2_Type2; // m1, not exported
private Class<?> q1_Type1; // m2, exported
private Class<?> q2_Type2; // m2, not exported
- private Class<?> x500NameClass; // java.base, not exported
+ private Class<?> signalClass; // java.base, not exported
private Class<?> unnamedClass; // class in unnamed module
@BeforeTest
@@ -55,7 +55,7 @@
p2_Type2 = Class.forName("p2.Type2");
q1_Type1 = Class.forName("q1.Type1");
q2_Type2 = Class.forName("q2.Type2");
- x500NameClass = Class.forName("sun.security.x509.X500Name");
+ signalClass = Class.forName("jdk.internal.misc.Signal");
unnamedClass = Class.forName("Unnamed");
} catch (ClassNotFoundException e) {
throw new AssertionError(e);
@@ -105,7 +105,7 @@
// java.base
findConstructor(lookup, Object.class, void.class); // [A2]
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class); // [A3]
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class); // [A3]
// unnamed
findConstructor(lookup, unnamedClass, void.class); // [A3]
@@ -130,7 +130,7 @@
// java.base
findConstructor(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
// unnamed
findConstructor(lookup, unnamedClass, void.class);
@@ -139,51 +139,70 @@
/**
* Hop to lookup class in another named module
*
- * [A0] has no access
+ * [A0] has PUBLIC access if accessible; otherwise no access
+ * [A1] old lookup class becomes previous lookup class
*/
public void testFromNamedToNamedModule() throws Exception {
+ // m2/q1_Type1 is accessible to m1 whereas m2/q_Type2 is not accessible
Lookup lookup = MethodHandles.lookup().in(q1_Type1);
- assertTrue(lookup.lookupModes() == 0); // [A0]
+ assertTrue(lookup.lookupModes() == PUBLIC); // [A0]
+ assertTrue(lookup.previousLookupClass() == Main.class); // [A1]
+
+ Lookup lookup2 = MethodHandles.lookup().in(q2_Type2);
+ assertTrue(lookup2.lookupModes() == 0); // [A0]
+ assertTrue(lookup2.previousLookupClass() == Main.class); // [A1]
// m1
findConstructorExpectingIAE(lookup, p1_Type1, void.class);
findConstructorExpectingIAE(lookup, p2_Type2, void.class);
+ findConstructorExpectingIAE(lookup2, p1_Type1, void.class);
+ findConstructorExpectingIAE(lookup2, p2_Type2, void.class);
+
// m2
- findConstructorExpectingIAE(lookup, q1_Type1, void.class);
+ findConstructor(lookup, q1_Type1, void.class); // m2/q1 is exported
findConstructorExpectingIAE(lookup, q2_Type2, void.class);
+ findConstructorExpectingIAE(lookup2, q1_Type1, void.class);
+ findConstructorExpectingIAE(lookup2, q2_Type2, void.class);
+
// java.base
- findConstructorExpectingIAE(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+ findConstructor(lookup, Object.class, void.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
+
+ findConstructorExpectingIAE(lookup2, Object.class, void.class);
+ findConstructorExpectingIAE(lookup2, signalClass, void.class, String.class);
// unnamed
findConstructorExpectingIAE(lookup, unnamedClass, void.class);
+
+ findConstructorExpectingIAE(lookup2, unnamedClass, void.class);
+
}
/**
* Hop to lookup class in an unnamed module
*
- * [A0] has no access
+ * [A0] has PUBLIC access
*/
public void testFromNamedToUnnamedModule() throws Exception {
Lookup lookup = MethodHandles.lookup().in(unnamedClass);
- assertTrue(lookup.lookupModes() == 0); // [A0]
+ assertTrue(lookup.lookupModes() == PUBLIC); // [A0]
// m1
- findConstructorExpectingIAE(lookup, p1_Type1, void.class);
+ findConstructor(lookup, p1_Type1, void.class); // p1 is exported
findConstructorExpectingIAE(lookup, p2_Type2, void.class);
// m2
- findConstructorExpectingIAE(lookup, q1_Type1, void.class);
+ findConstructor(lookup, q1_Type1, void.class);
findConstructorExpectingIAE(lookup, q2_Type2, void.class);
// java.base
- findConstructorExpectingIAE(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+ findConstructor(lookup, Object.class, void.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
// unnamed
- findConstructorExpectingIAE(lookup, unnamedClass, void.class);
+ findConstructor(lookup, unnamedClass, void.class);
}
/**
@@ -206,7 +225,7 @@
// java.base
findConstructor(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
// unnamed
findConstructor(lookup, unnamedClass, void.class);
@@ -215,11 +234,11 @@
/**
* MethodHandles.publicLookup()
*
- * [A0] has PUBLIC|UNCONDITIONAL access
+ * [A0] has UNCONDITIONAL access
*/
public void testPublicLookup() throws Exception {
Lookup lookup = MethodHandles.publicLookup();
- assertTrue(lookup.lookupModes() == (PUBLIC|UNCONDITIONAL)); // A0
+ assertTrue(lookup.lookupModes() == UNCONDITIONAL); // A0
// m1
findConstructor(lookup, p1_Type1, void.class);
@@ -231,7 +250,7 @@
// java.base
findConstructor(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
// unnamed
findConstructor(lookup, unnamedClass, void.class);
@@ -239,36 +258,12 @@
/**
* Hop from publicLookup to accessible type in java.base
+ *
+ * [A0] has UNCONDITIONAL access
*/
public void testPublicLookupToBaseModule() throws Exception {
Lookup lookup = MethodHandles.publicLookup().in(String.class);
- assertTrue(lookup.lookupModes() == PUBLIC); // A0
-
- // m1
- findConstructorExpectingIAE(lookup, p1_Type1, void.class);
- findConstructorExpectingIAE(lookup, p2_Type2, void.class);
-
- // m2
- findConstructorExpectingIAE(lookup, q1_Type1, void.class);
- findConstructorExpectingIAE(lookup, q2_Type2, void.class);
-
- // java.base
- findConstructor(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
-
- // unnamed
- findConstructorExpectingIAE(lookup, unnamedClass, void.class);
- }
-
-
- /**
- * Hop from publicLookup to accessible type in named module.
- *
- * [A0] has PUBLIC access
- */
- public void testPublicLookupToAccessibleTypeInNamedModule() throws Exception {
- Lookup lookup = MethodHandles.publicLookup().in(p1_Type1);
- assertTrue(lookup.lookupModes() == PUBLIC); // A0
+ assertTrue(lookup.lookupModes() == UNCONDITIONAL); // A0
// m1
findConstructor(lookup, p1_Type1, void.class);
@@ -280,7 +275,33 @@
// java.base
findConstructor(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
+
+ // unnamed
+ findConstructor(lookup, unnamedClass, void.class);
+ }
+
+
+ /**
+ * Hop from publicLookup to accessible type in named module.
+ *
+ * [A0] has UNCONDITIONAL access
+ */
+ public void testPublicLookupToAccessibleTypeInNamedModule() throws Exception {
+ Lookup lookup = MethodHandles.publicLookup().in(p1_Type1);
+ assertTrue(lookup.lookupModes() == UNCONDITIONAL); // A0
+
+ // m1
+ findConstructor(lookup, p1_Type1, void.class);
+ findConstructorExpectingIAE(lookup, p2_Type2, void.class);
+
+ // m2
+ findConstructor(lookup, q1_Type1, void.class);
+ findConstructorExpectingIAE(lookup, q2_Type2, void.class);
+
+ // java.base
+ findConstructor(lookup, Object.class, void.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
// unnamed
findConstructor(lookup, unnamedClass, void.class);
@@ -305,7 +326,7 @@
// java.base
findConstructorExpectingIAE(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
// unnamed
findConstructorExpectingIAE(lookup, unnamedClass, void.class);
@@ -314,11 +335,11 @@
/**
* Teleport from publicLookup to public type in unnamed module
*
- * [A0] has PUBLIC access
+ * [A0] has UNCONDITIONAL access
*/
public void testPublicLookupToUnnamedModule() throws Exception {
Lookup lookup = MethodHandles.publicLookup().in(unnamedClass);
- assertTrue(lookup.lookupModes() == PUBLIC); // A0
+ assertTrue(lookup.lookupModes() == UNCONDITIONAL); // A0
// m1
findConstructor(lookup, p1_Type1, void.class);
@@ -330,7 +351,7 @@
// java.base
findConstructor(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
// unnamed
findConstructor(lookup, unnamedClass, void.class);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m3/c1/C1.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, 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 c1;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+public class C1 {
+ public C1() { }
+
+ public static Lookup lookup() {
+ return MethodHandles.lookup();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m3/c1/C2.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 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 c1;
+
+public class C2 {
+ public C2() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m3/c2/C3.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 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 c2;
+
+public class C3{
+ public C3() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m3/jdk/test/ModuleAccessTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,568 @@
+/*
+ * Copyright (c) 2019, 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 jdk.test;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import e1.CrackM5Access;
+
+import static java.lang.invoke.MethodHandles.Lookup.*;
+import static org.testng.Assert.*;
+
+public class ModuleAccessTest {
+ static ModuleLookup m3;
+ static ModuleLookup m4;
+ static ModuleLookup m5;
+ static Map<String, ModuleLookup> moduleLookupMap = new HashMap<>();
+ static Lookup privLookupIn;
+ static Lookup privLookupIn2;
+ static Lookup unnamedLookup;
+ static Class<?> unnamed;
+ static Class<?> unnamed1;
+
+ @BeforeTest
+ public void setup() throws Exception {
+ m3 = new ModuleLookup("m3", 'C');
+ m4 = new ModuleLookup("m4", 'D');
+ m5 = new ModuleLookup("m5", 'E');
+ moduleLookupMap.put(m3.name(), m3);
+ moduleLookupMap.put(m4.name(), m4);
+ moduleLookupMap.put(m5.name(), m5);
+
+ privLookupIn = MethodHandles.privateLookupIn(m3.type2, m3.lookup);
+ privLookupIn2 = MethodHandles.privateLookupIn(m4.type1, m3.lookup);
+
+ unnamed = Class.forName("Unnamed");
+ unnamed1 = Class.forName("Unnamed1");
+ unnamedLookup = (Lookup)unnamed.getMethod("lookup").invoke(null);
+
+ // m5 reads m3
+ CrackM5Access.addReads(m3.module);
+ CrackM5Access.addReads(unnamed.getModule());
+ }
+
+ @DataProvider(name = "samePackage")
+ public Object[][] samePackage() throws Exception {
+ return new Object[][] {
+ { m3.lookup, m3.type2 },
+ { privLookupIn, m3.type1 },
+ { privLookupIn2, m4.type2 },
+ { unnamedLookup, unnamed1 }
+ };
+ }
+
+ /**
+ * Test lookup.in(T) where T is in the same package of the lookup class.
+ *
+ * [A0] targetClass becomes the lookup class
+ * [A1] no change in previous lookup class
+ * [A2] PROTECTED and PRIVATE are dropped
+ */
+ @Test(dataProvider = "samePackage")
+ public void testLookupInSamePackage(Lookup lookup, Class<?> targetClass) throws Exception {
+ Class<?> lookupClass = lookup.lookupClass();
+ Lookup lookup2 = lookup.in(targetClass);
+
+ assertTrue(lookupClass.getPackage() == targetClass.getPackage());
+ assertTrue(lookupClass.getModule() == targetClass.getModule());
+ assertTrue(lookup2.lookupClass() == targetClass); // [A0]
+ assertTrue(lookup2.previousLookupClass() == lookup.previousLookupClass()); // [A1]
+ assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE))); // [A2]
+ }
+
+ @DataProvider(name = "sameModule")
+ public Object[][] sameModule() throws Exception {
+ return new Object[][] {
+ { m3.lookup, m3.type3},
+ { privLookupIn, m3.type3},
+ { privLookupIn2, m4.type3}
+ };
+ }
+
+ /**
+ * Test lookup.in(T) where T is in the same module but different package from the lookup class.
+ *
+ * [A0] targetClass becomes the lookup class
+ * [A1] no change in previous lookup class
+ * [A2] PROTECTED, PRIVATE and PACKAGE are dropped
+ */
+ @Test(dataProvider = "sameModule")
+ public void testLookupInSameModule(Lookup lookup, Class<?> targetClass) throws Exception {
+ Class<?> lookupClass = lookup.lookupClass();
+ Lookup lookup2 = lookup.in(targetClass);
+
+ assertTrue(lookupClass.getPackage() != targetClass.getPackage());
+ assertTrue(lookupClass.getModule() == targetClass.getModule());
+ assertTrue(lookup2.lookupClass() == targetClass); // [A0]
+ assertTrue(lookup2.previousLookupClass() == lookup.previousLookupClass()); // [A1]
+ assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE))); // [A2]
+ }
+
+ @DataProvider(name = "anotherModule")
+ public Object[][] anotherModule() throws Exception {
+ return new Object[][] {
+ { m3.lookup, m4.type1, m5, m5.accessibleTypesTo(m3.module, m4.module) },
+ { m4.lookup, m5.type2, m3, m3.accessibleTypesTo(m4.module, m5.module) },
+ { m3.lookup, m5.type1, m4, m4.accessibleTypesTo(m3.module, m5.module) },
+ { m5.lookup, unnamed, m3, m3.accessibleTypesTo(m5.module, unnamed.getModule()) },
+ };
+ }
+
+ /**
+ * Test lookup.in(T) where T is in a different module from the lookup class.
+ *
+ * [A0] targetClass becomes the lookup class
+ * [A1] lookup class becomes previous lookup class
+ * [A2] PROTECTED, PRIVATE, PACKAGE, and MODULE are dropped
+ * [A3] no access to module internal types in m0 and m1
+ * [A4] if m1 reads m0, can access public types in m0; otherwise no access.
+ * [A5] can access public types in m1 exported to m0
+ * [A6] can access public types in m2 exported to m0 and m1
+ */
+ @Test(dataProvider = "anotherModule")
+ public void testLookupInAnotherModule(Lookup lookup, Class<?> targetClass,
+ ModuleLookup m2, Set<Class<?>> otherTypes) throws Exception {
+ Class<?> lookupClass = lookup.lookupClass();
+ Module m0 = lookupClass.getModule();
+ Module m1 = targetClass.getModule();
+
+ assertTrue(m0 != m1);
+ assertTrue(m0.canRead(m1));
+ assertTrue(m1.isExported(targetClass.getPackageName(), m0));
+
+ Lookup lookup2 = lookup.in(targetClass);
+ assertTrue(lookup2.lookupClass() == targetClass); // [A0]
+ assertTrue(lookup2.previousLookupClass() == lookup.lookupClass()); // [A1]
+ assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE|MODULE))); // [A2]
+
+ // [A3] no access to module internal type in m0
+ // [A4] if m1 reads m0,
+ // [A4] no access to public types exported from m0 unconditionally
+ // [A4] no access to public types exported from m0
+ ModuleLookup ml0 = moduleLookupMap.get(m0.getName());
+ if (m1.canRead(m0)) {
+ for (Class<?> type : ml0.unconditionalExports()) {
+ testAccess(lookup2, type);
+ }
+ for (Class<?> type : ml0.qualifiedExportsTo(m1)) {
+ testAccess(lookup2, type);
+ }
+ } else {
+ findConstructorExpectingIAE(lookup2, ml0.type1, void.class);
+ findConstructorExpectingIAE(lookup2, ml0.type2, void.class);
+ findConstructorExpectingIAE(lookup2, ml0.type3, void.class);
+ }
+
+ // [A5] can access public types exported from m1 unconditionally
+ // [A5] can access public types exported from m1 to m0
+ if (m1.isNamed()) {
+ ModuleLookup ml1 = moduleLookupMap.get(m1.getName());
+ assertTrue(ml1.unconditionalExports().size() + ml1.qualifiedExportsTo(m0).size() > 0);
+ for (Class<?> type : ml1.unconditionalExports()) {
+ testAccess(lookup2, type);
+ }
+ for (Class<?> type : ml1.qualifiedExportsTo(m0)) {
+ testAccess(lookup2, type);
+ }
+ } else {
+ // unnamed module
+ testAccess(lookup2, unnamed1);
+ }
+
+ // [A5] can access public types exported from m2 unconditionally
+ // [A5] can access public types exported from m2 to m0 and m1
+ for (Class<?> type : otherTypes) {
+ assertTrue(type.getModule() == m2.module);
+ testAccess(lookup2, type);
+ }
+
+ // test inaccessible types
+ for (Class<?> type : Set.of(m2.type1, m2.type2, m2.type3)) {
+ if (!otherTypes.contains(type)) {
+ // type is accessible to this lookup
+ try {
+ lookup2.accessClass(type);
+ assertTrue(false);
+ } catch (IllegalAccessException e) {}
+
+ findConstructorExpectingIAE(lookup2, type, void.class);
+ }
+ }
+ }
+
+ public void testAccess(Lookup lookup, Class<?> type) throws Exception {
+ // type is accessible to this lookup
+ assertTrue(lookup.accessClass(type) == type);
+
+ // can find constructor
+ findConstructor(lookup, type, void.class);
+
+ Module m0 = lookup.previousLookupClass().getModule();
+ Module m1 = lookup.lookupClass().getModule();
+ Module m2 = type.getModule();
+
+ assertTrue(m0 != m1 && m0 != null);
+ assertTrue((lookup.lookupModes() & MODULE) == 0);
+ assertTrue(m0 != m2 || m1 != m2);
+
+ MethodHandles.Lookup lookup2 = lookup.in(type);
+ if (m2 == m1) {
+ // the same module of the lookup class
+ assertTrue(lookup2.lookupClass() == type);
+ assertTrue(lookup2.previousLookupClass() == lookup.previousLookupClass());
+ } else if (m2 == m0) {
+ // hop back to the module of the previous lookup class
+ assertTrue(lookup2.lookupClass() == type);
+ assertTrue(lookup2.previousLookupClass() == lookup.lookupClass());
+ } else {
+ // hop to a third module
+ assertTrue(lookup2.lookupClass() == type);
+ assertTrue(lookup2.previousLookupClass() == lookup.lookupClass());
+ assertTrue(lookup2.lookupModes() == 0);
+ }
+ }
+
+ @DataProvider(name = "thirdModule")
+ public Object[][] thirdModule() throws Exception {
+ return new Object[][] {
+ { m3.lookup, m4.type1, m5.type1},
+ { m3.lookup, m4.type2, m5.type1},
+ { unnamedLookup, m3.type1, m4.type1 },
+ };
+ }
+
+ /**
+ * Test lookup.in(c1).in(c2) where c1 is in second module and c2 is in a third module.
+ *
+ * [A0] c2 becomes the lookup class
+ * [A1] c1 becomes previous lookup class
+ * [A2] all access bits are dropped
+ */
+ @Test(dataProvider = "thirdModule")
+ public void testLookupInThirdModule(Lookup lookup, Class<?> c1, Class<?> c2) throws Exception {
+ Class<?> c0 = lookup.lookupClass();
+ Module m0 = c0.getModule();
+ Module m1 = c1.getModule();
+ Module m2 = c2.getModule();
+
+ assertTrue(m0 != m1 && m0 != m2 && m1 != m2);
+ assertTrue(m0.canRead(m1) && m0.canRead(m2));
+ assertTrue(m1.canRead(m2));
+ assertTrue(m1.isExported(c1.getPackageName(), m0));
+ assertTrue(m2.isExported(c2.getPackageName(), m0) && m2.isExported(c2.getPackageName(), m1));
+
+ Lookup lookup1 = lookup.in(c1);
+ assertTrue(lookup1.lookupClass() == c1);
+ assertTrue(lookup1.previousLookupClass() == c0);
+ assertTrue(lookup1.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE|MODULE)));
+
+ Lookup lookup2 = lookup1.in(c2);
+ assertTrue(lookup2.lookupClass() == c2); // [A0]
+ assertTrue(lookup2.previousLookupClass() == c1); // [A1]
+ assertTrue(lookup2.lookupModes() == 0, lookup2.toString()); // [A2]
+ }
+
+ @DataProvider(name = "privLookupIn")
+ public Object[][] privLookupIn() throws Exception {
+ return new Object[][] {
+ { m3.lookup, m4.type1 },
+ { m3.lookup, m5.type1 },
+ { m4.lookup, m5.type2 },
+ { m5.lookup, m3.type3 },
+ { m5.lookup, unnamed }
+ };
+ }
+
+ /**
+ * Test privateLookupIn(T, lookup) where T is in another module
+ *
+ * [A0] full capabilities except MODULE bit
+ * [A1] target class becomes the lookup class
+ * [A2] the lookup class becomes previous lookup class
+ * [A3] IAE thrown if lookup has no MODULE access
+ */
+ @Test(dataProvider = "privLookupIn")
+ public void testPrivateLookupIn(Lookup lookup, Class<?> targetClass) throws Exception {
+ Module m0 = lookup.lookupClass().getModule();
+ Module m1 = targetClass.getModule();
+
+ // privateLookupIn from m0 to m1
+ assertTrue(m0 != m1);
+ assertTrue(m1.isOpen(targetClass.getPackageName(), m0));
+ Lookup privLookup1 = MethodHandles.privateLookupIn(targetClass, lookup);
+ assertTrue(privLookup1.lookupModes() == (PROTECTED|PRIVATE|PACKAGE|PUBLIC)); // [A0]
+ assertTrue(privLookup1.lookupClass() == targetClass); // [A1]
+ assertTrue(privLookup1.previousLookupClass() == lookup.lookupClass()); // [A2]
+
+ // privLookup1 has no MODULE access; can't do privateLookupIn
+ try {
+ Lookup privLookup2 = MethodHandles.privateLookupIn(targetClass, privLookup1); // [A3]
+ assertFalse(privLookup2 != null);
+ } catch (IllegalAccessException e) {}
+ }
+
+ /**
+ * Test member access from the Lookup returned from privateLookupIn
+ */
+ @Test
+ public void testPrivateLookupAccess() throws Exception {
+ Class<?> staticsClass = e1.Statics.class;
+ Lookup privLookup1 = MethodHandles.privateLookupIn(staticsClass, m4.lookup);
+ assertTrue((privLookup1.lookupModes() & MODULE) == 0);
+ assertTrue(privLookup1.lookupClass() == staticsClass);
+ assertTrue(privLookup1.previousLookupClass() == m4.lookup.lookupClass());
+
+ // access private member and default package member in m5
+ MethodType mtype = MethodType.methodType(void.class);
+ MethodHandle mh1 = privLookup1.findStatic(staticsClass, "privateMethod", mtype);
+ MethodHandle mh2 = privLookup1.findStatic(staticsClass, "packageMethod", mtype);
+
+ // access public member in exported types from m5 to m4
+ findConstructor(privLookup1, m5.type1, void.class);
+ // no access to public member in non-exported types to m5
+ findConstructorExpectingIAE(privLookup1, m5.type3, void.class);
+
+ // no access to public types in m4 since m5 does not read m4
+ assertFalse(m5.module.canRead(m4.module));
+ findConstructorExpectingIAE(privLookup1, m4.type1, void.class);
+
+ // teleport from a privateLookup to another class in the same package
+ // lose private access
+ Lookup privLookup2 = MethodHandles.privateLookupIn(m5.type1, m4.lookup);
+ Lookup lookup = privLookup2.in(staticsClass);
+ assertTrue((lookup.lookupModes() & PRIVATE) == 0);
+ MethodHandle mh3 = lookup.findStatic(staticsClass, "packageMethod", mtype);
+ try {
+ lookup.findStatic(staticsClass, "privateMethod", mtype);
+ assertTrue(false);
+ } catch (IllegalAccessException e) {}
+ }
+
+ /**
+ * Test member access from the Lookup returned from privateLookupIn and
+ * the lookup mode after dropLookupMode
+ */
+ @Test
+ public void testDropLookupMode() throws Exception {
+ Lookup lookup = MethodHandles.privateLookupIn(m5.type1, m4.lookup);
+ assertTrue((lookup.lookupModes() & MODULE) == 0);
+
+ Lookup lookup1 = lookup.dropLookupMode(PRIVATE);
+ assertTrue(lookup1.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE)));
+ Lookup lookup2 = lookup.dropLookupMode(PACKAGE);
+ assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE)));
+ Lookup lookup3 = lookup.dropLookupMode(MODULE);
+ assertTrue(lookup3.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE)));
+ Lookup lookup4 = lookup.dropLookupMode(PUBLIC);
+ assertTrue(lookup4.lookupModes() == 0);
+
+ }
+
+ /**
+ * Test no access to a public member on a non-public class
+ */
+ @Test
+ public void testPrivateLookupOnNonPublicType() throws Exception {
+ // privateLookup in a non-public type
+ Class<?> nonPUblicType = Class.forName("e1.NonPublic");
+ Lookup privLookup = MethodHandles.privateLookupIn(nonPUblicType, m4.lookup);
+ MethodType mtype = MethodType.methodType(void.class);
+ MethodHandle mh1 = privLookup.findStatic(nonPUblicType, "publicStatic", mtype);
+
+ // drop MODULE access i.e. only PUBLIC access
+ Lookup lookup = privLookup.dropLookupMode(MODULE);
+ assertTrue(lookup.lookupModes() == PUBLIC);
+ try {
+ MethodHandle mh2 = lookup.findStatic(nonPUblicType, "publicStatic", mtype);
+ assertFalse(mh2 != null);
+ } catch (IllegalAccessException e) {}
+ }
+
+ @Test
+ public void testPublicLookup() {
+ Lookup publicLookup = MethodHandles.publicLookup();
+ Lookup pub1 = publicLookup.in(m3.type1);
+ Lookup pub2 = pub1.in(java.lang.String.class);
+ Lookup pub3 = pub2.in(java.lang.management.ThreadMXBean.class);
+ Lookup pub4 = pub3.dropLookupMode(UNCONDITIONAL);
+
+ assertTrue(publicLookup.lookupClass() == Object.class);
+ assertTrue(publicLookup.lookupModes() == UNCONDITIONAL);
+ assertTrue(pub1.lookupClass() == m3.type1);
+ assertTrue(pub1.lookupModes() == UNCONDITIONAL);
+ assertTrue(pub2.lookupClass() == String.class);
+ assertTrue(pub2.lookupModes() == UNCONDITIONAL);
+ assertTrue(pub3.lookupClass() == java.lang.management.ThreadMXBean.class);
+ assertTrue(pub3.lookupModes() == UNCONDITIONAL);
+ assertTrue(pub4.lookupModes() == 0);
+
+ // publicLookup has no MODULE access; can't do privateLookupIn
+ try {
+ Lookup pub5 = MethodHandles.privateLookupIn(m4.type1, pub1);
+ assertFalse(pub5 != null);
+ } catch (IllegalAccessException e) {}
+ }
+
+ static class ModuleLookup {
+ private final Module module;
+ private final Set<String> packages;
+ private final Lookup lookup;
+ private final Class<?> type1;
+ private final Class<?> type2;
+ private final Class<?> type3;
+
+ ModuleLookup(String mn, char c) throws Exception {
+ this.module = ModuleLayer.boot().findModule(mn).orElse(null);
+ assertNotNull(this.module);
+ this.packages = module.getDescriptor().packages();
+ assertTrue(packages.size() <= 3);
+ Lookup lookup = null;
+ Class<?> type1 = null;
+ Class<?> type2 = null;
+ Class<?> type3 = null;
+ for (String pn : packages) {
+ char n = pn.charAt(pn.length() - 1);
+ switch (n) {
+ case '1':
+ type1 = Class.forName(pn + "." + c + "1");
+ type2 = Class.forName(pn + "." + c + "2");
+ Method m = type1.getMethod("lookup");
+ lookup = (Lookup) m.invoke(null);
+ break;
+ case '2':
+ type3 = Class.forName(pn + "." + c + "3");
+ break;
+
+ default:
+ }
+ }
+ this.lookup = lookup;
+ this.type1 = type1;
+ this.type2 = type2;
+ this.type3 = type3;
+ }
+
+ String name() {
+ return module.getName();
+ }
+
+ /*
+ * Returns the set of types that are unconditionally exported.
+ */
+ Set<Class<?>> unconditionalExports() {
+ return Stream.of(type1, type2, type3)
+ .filter(c -> module.isExported(c.getPackageName()))
+ .collect(Collectors.toSet());
+ }
+
+ /*
+ * Returns the set of types that are qualifiedly exported to the specified
+ * caller module
+ */
+ Set<Class<?>> qualifiedExportsTo(Module caller) {
+ if (caller.canRead(this.module)) {
+ return Stream.of(type1, type2, type3)
+ .filter(c -> !module.isExported(c.getPackageName())
+ && module.isExported(c.getPackageName(), caller))
+ .collect(Collectors.toSet());
+ } else {
+ return Set.of();
+ }
+ }
+
+ /*
+ * Returns the set of types that are qualifiedly exported to the specified
+ * caller module
+ */
+ Set<Class<?>> accessibleTypesTo(Module m0, Module m1) {
+ if (m0.canRead(this.module) && m1.canRead(this.module)) {
+ return Stream.of(type1, type2, type3)
+ .filter(c -> module.isExported(c.getPackageName(), m0)
+ && module.isExported(c.getPackageName(), m1))
+ .collect(Collectors.toSet());
+ } else {
+ return Set.of();
+ }
+ }
+
+ /*
+ * Returns the set of types that are open to the specified caller
+ * unconditionally or qualifiedly.
+ */
+ Set<Class<?>> opensTo(Module caller) {
+ if (caller.canRead(this.module)) {
+ return Stream.of(type1, type2, type3)
+ .filter(c -> module.isOpen(c.getPackageName(), caller))
+ .collect(Collectors.toSet());
+ } else {
+ return Set.of();
+ }
+ }
+
+ public String toString() {
+ return module.toString();
+ }
+ }
+
+ /**
+ * Invokes Lookup findConstructor with a method type constructed from the
+ * given return and parameter types, expecting IllegalAccessException to be
+ * thrown.
+ */
+ static void findConstructorExpectingIAE(Lookup lookup,
+ Class<?> clazz,
+ Class<?> rtype,
+ Class<?>... ptypes) throws Exception {
+ try {
+ MethodHandle mh = findConstructor(lookup, clazz, rtype, ptypes);
+ assertTrue(false);
+ } catch (IllegalAccessException expected) { }
+ }
+
+ /**
+ * Invokes Lookup findConstructor with a method type constructored from the
+ * given return and parameter types.
+ */
+ static MethodHandle findConstructor(Lookup lookup,
+ Class<?> clazz,
+ Class<?> rtype,
+ Class<?>... ptypes) throws Exception {
+ MethodType mt = MethodType.methodType(rtype, ptypes);
+ return lookup.findConstructor(clazz, mt);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m3/module-info.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+module m3 {
+ requires m4;
+ requires m5;
+ requires testng;
+ requires java.management;
+ exports c1;
+ opens c2 to m5;
+ exports jdk.test;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m4/d1/D1.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, 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 d1;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+public class D1 {
+ public D1() { }
+
+ public static Lookup lookup() {
+ return MethodHandles.lookup();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m4/d1/D2.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 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 d1;
+
+public class D2 {
+ public D2() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m4/d2/D3.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 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 d2;
+
+public class D3 {
+ public D3() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m4/module-info.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+module m4 {
+ requires m5;
+ opens d1;
+ exports d2 to m3;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e1/CrackM5Access.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019, 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 e1;
+
+public class CrackM5Access {
+ private static void privateMethod() { }
+
+ static void packageMethod() { }
+
+ public static void addReads(Module m) {
+ CrackM5Access.class.getModule().addReads(m);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e1/E1.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, 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 e1;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+public class E1 {
+ public E1() { }
+
+ public static Lookup lookup() {
+ return MethodHandles.lookup();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e1/E2.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 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 e1;
+
+public class E2 {
+ public E2() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e1/NonPublic.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 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 e1;
+
+class NonPublic {
+ public static void publicStatic() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e1/Statics.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, 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 e1;
+
+public class Statics {
+ private static void privateMethod() { }
+
+ static void packageMethod() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e2/E3.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 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 e2;
+
+public class E3 {
+ public E3() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/module-info.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+module m5 {
+ exports e1 to m3;
+ opens e1 to m3, m4;
+ exports e2 to m3;
+}
--- a/test/jdk/java/net/NetworkInterface/NetworkInterfaceRetrievalTests.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/net/NetworkInterface/NetworkInterfaceRetrievalTests.java Thu Jul 25 08:24:01 2019 -0400
@@ -24,6 +24,7 @@
/**
* @test
* @bug 8179559 8225239
+ * @modules java.base/java.net:open
*/
import java.net.InetAddress;
--- a/test/jdk/java/net/URLPermission/URLTest.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/net/URLPermission/URLTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -276,7 +276,9 @@
}
static class CustomPolicy extends Policy {
+ static final Policy DEFAULT_POLICY = Policy.getPolicy();
final PermissionCollection perms = new Permissions();
+
CustomPolicy(Permission... permissions) {
java.util.Arrays.stream(permissions).forEach(perms::add);
@@ -301,7 +303,7 @@
}
public boolean implies(ProtectionDomain domain, Permission perm) {
- return perms.implies(perm);
+ return perms.implies(perm) || DEFAULT_POLICY.implies(domain, perm);
}
}
}
--- a/test/jdk/java/sql/testng/util/TestPolicy.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/sql/testng/util/TestPolicy.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -42,6 +42,7 @@
* JDBC concrete classes
*/
public class TestPolicy extends Policy {
+ static final Policy DEFAULT_POLICY = Policy.getPolicy();
final PermissionCollection permissions = new Permissions();
@@ -137,6 +138,6 @@
@Override
public boolean implies(ProtectionDomain domain, Permission perm) {
- return permissions.implies(perm);
+ return permissions.implies(perm) || DEFAULT_POLICY.implies(domain, perm);
}
}
--- a/test/jdk/java/time/tck/java/time/format/TCKOffsetPrinterParser.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/time/tck/java/time/format/TCKOffsetPrinterParser.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -69,6 +69,7 @@
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.TextStyle;
+import java.util.Locale;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
@@ -557,20 +558,20 @@
ZonedDateTime zdt = ldt.atZone(offset);
DateTimeFormatter f = new DateTimeFormatterBuilder().appendLocalizedOffset(style)
- .toFormatter();
+ .toFormatter(Locale.US);
assertEquals(f.format(odt), expected);
assertEquals(f.format(zdt), expected);
assertEquals(f.parse(expected, ZoneOffset::from), offset);
if (style == TextStyle.FULL) {
f = new DateTimeFormatterBuilder().appendPattern("ZZZZ")
- .toFormatter();
+ .toFormatter(Locale.US);
assertEquals(f.format(odt), expected);
assertEquals(f.format(zdt), expected);
assertEquals(f.parse(expected, ZoneOffset::from), offset);
f = new DateTimeFormatterBuilder().appendPattern("OOOO")
- .toFormatter();
+ .toFormatter(Locale.US);
assertEquals(f.format(odt), expected);
assertEquals(f.format(zdt), expected);
assertEquals(f.parse(expected, ZoneOffset::from), offset);
@@ -578,7 +579,7 @@
if (style == TextStyle.SHORT) {
f = new DateTimeFormatterBuilder().appendPattern("O")
- .toFormatter();
+ .toFormatter(Locale.US);
assertEquals(f.format(odt), expected);
assertEquals(f.format(zdt), expected);
assertEquals(f.parse(expected, ZoneOffset::from), offset);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/time/test/java/time/format/TestLocalizedOffsetPrinterParser.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/*
+ * @test
+ * @bug 8154520
+ * @summary This test verifies that the localized text for "GMT" from CLDR is
+ * applied/recognized during printing/parsing timestamps. For example, the
+ * localized text for "GMT" on some particular locale may be "UTC", and the
+ * resulting formatted string should have UTC+<offset> (instead of GMT+<offset>).
+ * Since the test relies on CLDR data, the "expected" text in the test data may
+ * require to be modified in accordance with changes to CLDR, if any.
+ * @modules jdk.localedata
+ */
+
+package test.java.time.format;
+
+import static org.testng.Assert.assertEquals;
+
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.TextStyle;
+import java.util.Locale;
+import java.util.Objects;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/**
+ * Test DateTimeFormatterBuilder.appendOffset().
+ */
+@Test
+public class TestLocalizedOffsetPrinterParser {
+
+ private static final LocalDateTime DT_2012_06_30_12_30_40 = LocalDateTime.of(2012, 6, 30, 12, 30, 40);
+
+ private static final Locale LOCALE_GA = new Locale("ga");
+
+ @DataProvider(name="print_localized_custom_locale")
+ Object[][] data_print_localized_custom_locale() {
+ return new Object[][] {
+ {TextStyle.FULL, DT_2012_06_30_12_30_40, ZoneOffset.UTC, LOCALE_GA, "MAG"},
+ {TextStyle.SHORT, DT_2012_06_30_12_30_40, ZoneOffset.ofHours(1), LOCALE_GA, "MAG+1"},
+ {TextStyle.FULL, DT_2012_06_30_12_30_40, ZoneOffset.ofHours(-1), LOCALE_GA, "MAG-01:00"}
+ };
+ }
+
+ @Test(dataProvider="print_localized_custom_locale")
+ public void test_print_localized_custom_locale(TextStyle style, LocalDateTime ldt, ZoneOffset offset, Locale locale, String expected) {
+
+ Objects.requireNonNull(locale, "Locale must not be null");
+
+ OffsetDateTime odt = OffsetDateTime.of(ldt, offset);
+ ZonedDateTime zdt = ldt.atZone(offset);
+
+ DateTimeFormatter f = new DateTimeFormatterBuilder().appendLocalizedOffset(style).toFormatter(locale);
+ assertEquals(f.format(odt), expected);
+ assertEquals(f.format(zdt), expected);
+ assertEquals(f.parse(expected, ZoneOffset::from), offset);
+
+ if (style == TextStyle.FULL) {
+ f = new DateTimeFormatterBuilder().appendPattern("ZZZZ").toFormatter(locale);
+ assertEquals(f.format(odt), expected);
+ assertEquals(f.format(zdt), expected);
+ assertEquals(f.parse(expected, ZoneOffset::from), offset);
+
+ f = new DateTimeFormatterBuilder().appendPattern("OOOO").toFormatter(locale);
+ assertEquals(f.format(odt), expected);
+ assertEquals(f.format(zdt), expected);
+ assertEquals(f.parse(expected, ZoneOffset::from), offset);
+ }
+
+ if (style == TextStyle.SHORT) {
+ f = new DateTimeFormatterBuilder().appendPattern("O").toFormatter(locale);
+ assertEquals(f.format(odt), expected);
+ assertEquals(f.format(zdt), expected);
+ assertEquals(f.parse(expected, ZoneOffset::from), offset);
+ }
+
+ }
+
+}
--- a/test/jdk/java/util/Calendar/SupplementalJapaneseEraTestRun.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/util/Calendar/SupplementalJapaneseEraTestRun.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -93,7 +93,9 @@
private static void testRun(String property, List<String> javaParam)
throws Throwable{
JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java");
- launcher.addToolArg("-cp")
+ launcher.addToolArg("-ea")
+ .addToolArg("-esa")
+ .addToolArg("-cp")
.addToolArg(Utils.TEST_CLASS_PATH)
.addToolArg("-Djdk.calendar.japanese.supplemental.era=" + property)
.addToolArg("SupplementalJapaneseEraTest");
--- a/test/jdk/java/util/EnumSet/BogusEnumSet.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/util/EnumSet/BogusEnumSet.java Thu Jul 25 08:24:01 2019 -0400
@@ -32,11 +32,11 @@
public class BogusEnumSet {
public static void main(String[] args) throws Throwable {
- // This test depends on the current serialVersionUID of EnumSet,
- // which may change if the EnumSet class is modified.
- // The current value is -2409567991088730183L = 0xde8f7eadb5012fb9L
- // If the value changes, it will have to be patched into the
- // serialized byte stream below at the location noted.
+ // This test tries to deserialize a bogus stream produced with
+ // hypothetical EnumSet without a writeReplace() method - i.e.
+ // not using serialization proxy pattern. It tests that such
+ // stream is not accepted as valid stream - the EnumSet class
+ // declares a readObject() method which throws exception.
byte[] serializedForm = {
(byte)0xac, (byte)0xed, 0x0, 0x5, 0x73, 0x72, 0x0, 0x18,
0x6a, 0x61, 0x76, 0x61, 0x2e, 0x75, 0x74, 0x69,
@@ -45,10 +45,9 @@
0x7e, (byte)0xb0, (byte)0xd0, 0x7e, 0x2, 0x0, 0x1, 0x4a, 0x0, 0x8,
0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x78, 0x72, 0x0,
0x11, 0x6a, 0x61, 0x76, 0x61, 0x2e, 0x75, 0x74, 0x69,
- 0x6c, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x74,
- // EnumSet's serialVersionUID is the following eight bytes (big-endian)
- (byte)0xde, (byte)0x8f, 0x7e, (byte)0xad, (byte)0xb5, (byte)0x01, 0x2f, (byte)0xb9,
- 0x2, 0x0, 0x2, 0x4c, 0x0, 0xb, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+ 0x6c, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x74, 0xe,
+ 0x3, 0x21, 0x6a, (byte)0xcd, (byte)0x8c, 0x29, (byte)0xdd, 0x2,
+ 0x0, 0x2, 0x4c, 0x0, 0xb, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
0x54, 0x79, 0x70, 0x65, 0x74, 0x0, 0x11, 0x4c, 0x6a, 0x61, 0x76,
0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x43, 0x6c, 0x61, 0x73,
0x73, 0x3b, 0x5b, 0x0, 0x8, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/util/EnumSet/EnumSetClassSerialization.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/*
+ * @test
+ * @bug 8227368
+ * @summary Test deserialization of a stream containing EnumSet.class object
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.EnumSet;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+public class EnumSetClassSerialization {
+
+ public static void main(String[] args) throws Exception {
+ // EnumSet.class object serialized with JDK 8
+ int[] bytes = {
+ 0xac, 0xed, 0x00, 0x05, 0x76, 0x72, 0x00, 0x11, 0x6a, 0x61, 0x76, 0x61, 0x2e, 0x75, 0x74, 0x69,
+ 0x6c, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x74, 0x0e, 0x03, 0x21, 0x6a, 0xcd, 0x8c, 0x29,
+ 0xdd, 0x02, 0x00, 0x02, 0x4c, 0x00, 0x0b, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79,
+ 0x70, 0x65, 0x74, 0x00, 0x11, 0x4c, 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f,
+ 0x43, 0x6c, 0x61, 0x73, 0x73, 0x3b, 0x5b, 0x00, 0x08, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73,
+ 0x65, 0x74, 0x00, 0x11, 0x5b, 0x4c, 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f,
+ 0x45, 0x6e, 0x75, 0x6d, 0x3b, 0x78, 0x70
+ };
+
+ InputStream in = new InputStream() {
+ int i = 0;
+
+ @Override
+ public int read() {
+ return i < bytes.length ? bytes[i++] & 0xFF : -1;
+ }
+ };
+ ObjectInputStream ois = new ObjectInputStream(in);
+
+ Object res = ois.readObject();
+
+ if (res != EnumSet.class) {
+ throw new AssertionError(
+ "Expected: " + EnumSet.class + ", got: " + res);
+ }
+ }
+
+ /**
+ * This class can be used to print out lines that constitute
+ * the 'bytes' variable initializer in the test.
+ */
+ public static class Serializer {
+ public static void main(String[] args) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(EnumSet.class);
+ oos.close();
+ byte[] bytes = baos.toByteArray();
+ int bpl = 16;
+ System.out.print(
+ IntStream
+ .range(0, (bytes.length + bpl - 1) / bpl)
+ .mapToObj(i -> IntStream
+ .range(
+ i * bpl,
+ Math.min(i * bpl + bpl, bytes.length)
+ )
+ .mapToObj(ii -> {
+ String s = Integer.toHexString(bytes[ii] & 0xFF);
+ return s.length() == 1 ? "0x0" + s : "0x" + s;
+ })
+ .collect(Collectors.joining(", "))
+ )
+ .collect(Collectors.joining(",\n ", "int[] bytes = {\n ", "\n};"))
+ );
+ }
+ }
+}
--- a/test/jdk/java/util/ResourceBundle/Control/MissingResourceCauseTestRun.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/util/ResourceBundle/Control/MissingResourceCauseTestRun.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2019, 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
@@ -85,7 +85,8 @@
String cp = Utils.TEST_CLASSES + File.pathSeparator + Utils.TEST_SRC
+ File.pathSeparator + ".";
JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java");
- launcher.addToolArg("-esa")
+ launcher.addToolArg("-ea")
+ .addToolArg("-esa")
.addToolArg("-cp")
.addToolArg(cp)
.addToolArg("MissingResourceCauseTest");
--- a/test/jdk/java/util/ResourceBundle/modules/ModuleTestUtil.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/util/ResourceBundle/modules/ModuleTestUtil.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 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
@@ -134,7 +134,9 @@
public static void runModule(String mp, String mn, List<String> localeList)
throws Throwable {
JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java");
- launcher.addToolArg("-p")
+ launcher.addToolArg("-ea")
+ .addToolArg("-esa")
+ .addToolArg("-p")
.addToolArg(mp)
.addToolArg("-m")
.addToolArg(mn);
@@ -160,7 +162,9 @@
public static void runModuleWithCp(String cp, String mp, String mn,
List<String> localeList, boolean expected) throws Throwable {
JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java");
- launcher.addToolArg("-cp")
+ launcher.addToolArg("-ea")
+ .addToolArg("-esa")
+ .addToolArg("-cp")
.addToolArg(cp)
.addToolArg("-p")
.addToolArg(mp)
--- a/test/jdk/java/util/ResourceBundle/modules/layer/LayerTest.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/util/ResourceBundle/modules/layer/LayerTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -72,7 +72,9 @@
private static void runCmd() throws Throwable {
JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java");
- launcher.addToolArg("-cp")
+ launcher.addToolArg("-ea")
+ .addToolArg("-esa")
+ .addToolArg("-cp")
.addToolArg(Utils.TEST_CLASSES)
.addToolArg("Main")
.addToolArg(Utils.TEST_CLASSES);
--- a/test/jdk/java/util/ResourceBundle/modules/unnamed/UnNamedTest.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/util/ResourceBundle/modules/unnamed/UnNamedTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -74,7 +74,9 @@
private static void runCmd() throws Throwable {
// access resource bundles that are exported private unconditionally.
JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java");
- launcher.addToolArg("-cp")
+ launcher.addToolArg("-ea")
+ .addToolArg("-esa")
+ .addToolArg("-cp")
.addToolArg(Utils.TEST_CLASSES)
.addToolArg("--module-path")
.addToolArg(MODS_DIR.toString())
@@ -92,7 +94,9 @@
// --add-exports can't open resources
launcher = JDKToolLauncher.createUsingTestJDK("java");
- launcher.addToolArg("-cp")
+ launcher.addToolArg("-ea")
+ .addToolArg("-esa")
+ .addToolArg("-cp")
.addToolArg(Utils.TEST_CLASSES)
.addToolArg("--module-path")
.addToolArg(MODS_DIR.toString())
--- a/test/jdk/java/util/ResourceBundle/modules/visibility/VisibilityTest.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/util/ResourceBundle/modules/visibility/VisibilityTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -329,6 +329,8 @@
private int runCmd(List<String> argsList) throws Throwable {
JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java");
+ launcher.addToolArg("-ea")
+ .addToolArg("-esa");
argsList.forEach(launcher::addToolArg);
return ProcessTools.executeCommand(launcher.getCommand()).getExitValue();
--- a/test/jdk/java/util/TimeZone/Bug8066652Run.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/util/TimeZone/Bug8066652Run.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -51,6 +51,8 @@
JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java");
//Setting invalid TimeZone using VM option
launcher.addToolArg("-Duser.timezone=Foo/Bar")
+ .addToolArg("-ea")
+ .addToolArg("-esa")
.addToolArg("-cp")
.addToolArg(cp)
.addToolArg("Bug8066652");
--- a/test/jdk/java/util/TimeZone/TimeZoneDatePermissionCheckRun.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/java/util/TimeZone/TimeZoneDatePermissionCheckRun.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2019, 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
@@ -69,6 +69,8 @@
JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java");
launcher.addToolArg("-Djava.security.manager")
.addToolArg("-Djava.security.debug=access,failure,policy")
+ .addToolArg("-ea")
+ .addToolArg("-esa")
.addToolArg("-cp")
.addToolArg(jarPath)
.addToolArg("TimeZoneDatePermissionCheck");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/javax/accessibility/JTable/JTableCellEditor.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import java.awt.EventQueue;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Locale;
+
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleTable;
+import javax.swing.JFrame;
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.table.TableModel;
+
+/**
+ * @test
+ * @bug 8226653
+ * @key headful
+ * @summary The active cell editor should be reported as a child of the table.
+ * Note that the accessibility API ignores the real children of the
+ * table, but reports the "virtual" child per cell in the grid.
+ */
+public final class JTableCellEditor {
+
+ private static final int COUNT = 3;
+ private static JTable table;
+ private static JFrame frame;
+
+ public static void main(final String[] args)
+ throws InvocationTargetException, InterruptedException {
+ EventQueue.invokeAndWait(() -> {
+ frame = new JFrame();
+ table = new JTable(testSelectionWithFilterTable());
+ frame.add(table);
+ frame.pack();
+ });
+ EventQueue.invokeAndWait(() -> table.editCellAt(1, 1));
+ EventQueue.invokeAndWait(() -> {
+ AccessibleTable aTable = table.getAccessibleContext()
+ .getAccessibleTable();
+ int aColumns = aTable.getAccessibleColumnCount();
+ int aRows = aTable.getAccessibleRowCount();
+ // We cannot assume which component will be used as an editor of the
+ // table cell, but we can expect it will have the "text" role.
+ AccessibleRole role = aTable.getAccessibleAt(1, 1)
+ .getAccessibleContext()
+ .getAccessibleRole();
+ frame.dispose();
+ if (!role.toDisplayString(Locale.ENGLISH).equals("text")) {
+ throw new RuntimeException("Unexpected role: " + role);
+ }
+ if (aColumns != COUNT) {
+ throw new RuntimeException("Wrong columns: " + aColumns);
+ }
+ if (aRows != COUNT) {
+ throw new RuntimeException("Wrong rows: " + aRows);
+ }
+ });
+ }
+
+ /**
+ * Creates a dummy table model.
+ */
+ private static TableModel testSelectionWithFilterTable() {
+ DefaultTableModel model = new DefaultTableModel(0, 3);
+ for (int i = 0; i < COUNT; i++) {
+ model.addRow(new Object[]{i + "x0", i + "x1", i + "x2"});
+ }
+ return model;
+ }
+}
--- a/test/jdk/javax/swing/JInternalFrame/Test6325652.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/javax/swing/JInternalFrame/Test6325652.java Thu Jul 25 08:24:01 2019 -0400
@@ -63,6 +63,7 @@
public static void stepFirst() throws AWTException {
robot = new Robot(); // initialize shared static field first time
+ robot.setAutoDelay(50);
click(KeyEvent.VK_CONTROL, KeyEvent.VK_F9); // iconify internal frame
}
--- a/test/jdk/javax/swing/plaf/basic/BasicMenuUI/4983388/bug4983388.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/javax/swing/plaf/basic/BasicMenuUI/4983388/bug4983388.java Thu Jul 25 08:24:01 2019 -0400
@@ -78,10 +78,11 @@
});
Robot robot = new Robot();
+ robot.setAutoDelay(50);
robot.waitForIdle();
Util.hitMnemonics(robot, KeyEvent.VK_F);
robot.waitForIdle();
- robot.delay(1000);
+ robot.delay(200);
if (!bMenuSelected) {
throw new RuntimeException("shortcuts on menus do not work");
--- a/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java Thu Jul 25 08:24:01 2019 -0400
@@ -100,6 +100,7 @@
"CMRefRoots",
"WaitForStrongCLD",
"WeakCLDRoots",
+ "MergeER",
"MergeHCC",
"MergeRS",
"MergeLB",
--- a/test/jdk/jdk/modules/etc/JdkQualifiedExportTest.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/jdk/modules/etc/JdkQualifiedExportTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -73,8 +73,7 @@
"jdk.internal.vm.ci/jdk.vm.ci.runtime",
"jdk.internal.vm.ci/jdk.vm.ci.hotspot",
"jdk.internal.vm.ci/jdk.vm.ci.meta",
- "jdk.internal.vm.ci/jdk.vm.ci.code",
- "jdk.jsobject/jdk.internal.netscape.javascript.spi");
+ "jdk.internal.vm.ci/jdk.vm.ci.code");
static void checkExports(ModuleDescriptor md) {
// build a map of upgradeable module to Exports that are qualified to it
--- a/test/jdk/sanity/client/SwingSet/src/ColorChooserDemoTest.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sanity/client/SwingSet/src/ColorChooserDemoTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -68,261 +68,262 @@
@Listeners(GuiTestListener.class)
public class ColorChooserDemoTest {
-private static final String OK_BUTTON_TITLE = "OK";
-private static final String CANCEL_BUTTON_TITLE = "Cancel";
-private static final String RESET_BUTTON_TITLE = "Reset";
-private static final String HSV = "HSV";
-private static final String RGB = "RGB";
-private static final String HSL = "HSL";
-private static final String CMYK = "CMYK";
-private static final int HSV_NUMBER_OF_SLIDERS_AND_SPINNERS = 4;
-private static final int RGB_NUMBER_OF_SLIDERS_AND_SPINNERS = 4;
-private static final int HSL_NUMBER_OF_SLIDERS_AND_SPINNERS = 4;
-private static final int CMYK_NUMBER_OF_SLIDERS_AND_SPINNERS = 5;
-private static final int HSV_HUE_INDEX = 0;
-private static final int HSV_SATURATION_INDEX = 1;
-private static final int HSV_VALUE_INDEX = 2;
-private static final int HSV_TRANSPARENCY_INDEX = 3;
-private static final int HSL_HUE_INDEX = 0;
-private static final int HSL_SATURATION_INDEX = 1;
-private static final int HSL_LIGHTNESS_INDEX = 2;
-private static final int HSL_TRANSPARENCY_INDEX = 3;
-private static final int RGB_RED_INDEX = 0;
-private static final int RGB_GREEN_INDEX = 1;
-private static final int RGB_BLUE_INDEX = 2;
-private static final int RGB_ALPHA_INDEX = 3;
-private static final int RGB_COLORCODE_TEXT_FIELD_INDEX = 4;
-private static final int CMYK_CYAN_INDEX = 0;
-private static final int CMYK_MAGENTA_INDEX = 1;
-private static final int CMYK_YELLOW_INDEX = 2;
-private static final int CMYK_BLACK_INDEX = 3;
-private static final int CMYK_ALPHA_INDEX = 4;
+ private static final String OK_BUTTON_TITLE = "OK";
+ private static final String CANCEL_BUTTON_TITLE = "Cancel";
+ private static final String RESET_BUTTON_TITLE = "Reset";
+ private static final String HSV = "HSV";
+ private static final String RGB = "RGB";
+ private static final String HSL = "HSL";
+ private static final String CMYK = "CMYK";
+ private static final int HSV_NUMBER_OF_SLIDERS_AND_SPINNERS = 4;
+ private static final int RGB_NUMBER_OF_SLIDERS_AND_SPINNERS = 4;
+ private static final int HSL_NUMBER_OF_SLIDERS_AND_SPINNERS = 4;
+ private static final int CMYK_NUMBER_OF_SLIDERS_AND_SPINNERS = 5;
+ private static final int HSV_HUE_INDEX = 0;
+ private static final int HSV_SATURATION_INDEX = 1;
+ private static final int HSV_VALUE_INDEX = 2;
+ private static final int HSV_TRANSPARENCY_INDEX = 3;
+ private static final int HSL_HUE_INDEX = 0;
+ private static final int HSL_SATURATION_INDEX = 1;
+ private static final int HSL_LIGHTNESS_INDEX = 2;
+ private static final int HSL_TRANSPARENCY_INDEX = 3;
+ private static final int RGB_RED_INDEX = 0;
+ private static final int RGB_GREEN_INDEX = 1;
+ private static final int RGB_BLUE_INDEX = 2;
+ private static final int RGB_ALPHA_INDEX = 3;
+ private static final int RGB_COLORCODE_TEXT_FIELD_INDEX = 4;
+ private static final int CMYK_CYAN_INDEX = 0;
+ private static final int CMYK_MAGENTA_INDEX = 1;
+ private static final int CMYK_YELLOW_INDEX = 2;
+ private static final int CMYK_BLACK_INDEX = 3;
+ private static final int CMYK_ALPHA_INDEX = 4;
+
+ private final Color resetColor = new Color(125, 125, 125);
-private final Color resetColor = new Color(125, 125, 125);
+ private JDialogOperator colorChooserDialog;
+ private JButtonOperator okButton;
+ private JButtonOperator cancelButton;
+ private JButtonOperator resetButton;
+ private JColorChooserOperator colorChooser;
+ private JButtonOperator backgroundButton;
+ private JButtonOperator gradient1Button;
+ private JButtonOperator gradient2Button;
+ private JButtonOperator perimeterButton;
+ private JTabbedPaneOperator tabOperator;
+ private JComponentOperator bezierAnimationPanel;
+ private JSliderOperator[] sliders = new JSliderOperator[5];
+ private JSpinnerOperator[] spinners = new JSpinnerOperator[5];
+ private JButtonOperator lastFocusedButton;
+
+ @Test
+ public void test() throws Exception {
+ new ClassReference(ColorChooserDemo.class.getCanonicalName()).startApplication();
+ JFrameOperator frame = new JFrameOperator(DEMO_TITLE);
+ bezierAnimationPanel = new JComponentOperator(frame, new ByClassChooser(BezierAnimationPanel.class));
+ initializePanelButtons(frame);
+ checkBackgroundColorChooser();
+ checkGradient1ColorChooser();
+ checkGradient2ColorChooser();
+ checkPerimeterColorChooser();
+ }
+
+ private void checkBackgroundColorChooser() throws Exception {
+ basicCheck(backgroundButton, BezierColor.BACKGROUND);
+ checkAllColorChoosers(backgroundButton);
+ }
-private JDialogOperator colorChooserDialog;
-private JButtonOperator okButton;
-private JButtonOperator cancelButton;
-private JButtonOperator resetButton;
-private JColorChooserOperator colorChooser;
-private JButtonOperator backgroundButton;
-private JButtonOperator gradient1Button;
-private JButtonOperator gradient2Button;
-private JButtonOperator perimeterButton;
-private JTabbedPaneOperator tabOperator;
-private JComponentOperator bezierAnimationPanel;
-private JSliderOperator[] sliders = new JSliderOperator[5];
-private JSpinnerOperator[] spinners = new JSpinnerOperator[5];
-private JButtonOperator lastFocusedButton;
+ private void checkGradient1ColorChooser() throws Exception {
+ basicCheck(gradient1Button, BezierColor.GRADIENT_A);
+ }
+
+ private void checkGradient2ColorChooser() throws Exception {
+ basicCheck(gradient2Button, BezierColor.GRADIENT_B);
+ }
+
+ private void checkPerimeterColorChooser() throws Exception {
+ basicCheck(perimeterButton, BezierColor.OUTER);
+ }
+
+ private void pushButtonAndInitialize(JButtonOperator jbo) throws InterruptedException {
+ // Wait for focus to return to last focused button
+ lastFocusedButton.waitHasFocus();
+ jbo.pushNoBlock();
+ lastFocusedButton = jbo;
+ // Wait till the ColorChooserDemo Dialog Opens
+ new DialogWaiter().waitDialog(CHOOSER_TITLE, false, false);
+ initializeDialog();
+ }
+
+ private void initializePanelButtons(JFrameOperator frame) {
+ backgroundButton = new JButtonOperator(frame, BACKGROUND);
+ gradient1Button = new JButtonOperator(frame, GRADIENT_1);
+ gradient2Button = new JButtonOperator(frame, GRADIENT_2);
+ perimeterButton = new JButtonOperator(frame, PERIMETER);
+ lastFocusedButton = backgroundButton;
+ }
-@Test
-public void test() throws Exception {
-new ClassReference(ColorChooserDemo.class.getCanonicalName()).startApplication();
-JFrameOperator frame = new JFrameOperator(DEMO_TITLE);
-bezierAnimationPanel = new JComponentOperator(frame, new ByClassChooser(BezierAnimationPanel.class));
-initializePanelButtons(frame);
-checkBackgroundColorChooser();
-checkGradient1ColorChooser();
-checkGradient2ColorChooser();
-checkPerimeterColorChooser();
-}
+ private void initializeDialog() {
+ colorChooserDialog = new JDialogOperator(CHOOSER_TITLE);
+ tabOperator = new JTabbedPaneOperator(colorChooserDialog);
+ colorChooser = new JColorChooserOperator(colorChooserDialog);
+ okButton = new JButtonOperator(colorChooserDialog, OK_BUTTON_TITLE);
+ cancelButton = new JButtonOperator(colorChooserDialog, CANCEL_BUTTON_TITLE);
+ resetButton = new JButtonOperator(colorChooserDialog, RESET_BUTTON_TITLE);
+ }
+
+ private void basicCheck(JButtonOperator jbo, BezierColor bezierColor) throws Exception {
+ Color testColor = new Color(100, 26, 155);
+ Color testColor2 = new Color(10, 40, 50);
+ checkDefaultColorChooser(jbo, testColor, bezierColor);
+ checkCancelButton(jbo, testColor2);
+ checkResetButton(jbo, testColor2);
+ }
-private void checkBackgroundColorChooser() throws Exception {
-basicCheck(backgroundButton, BezierColor.BACKGROUND);
-checkAllColorChoosers(backgroundButton);
-}
+ private void checkDefaultColorChooser(JButtonOperator jbo, Color testColor, BezierColor bezierColor)
+ throws Exception {
+ BezierAnimationPanel bezierPanel;
+ pushButtonAndInitialize(jbo);
+ // Check ColorChooser color is being set and used accordingly
+ // in the animation panel
+ setAndWaitColor(testColor);
+ pushButtonAndWaitDialogClosed(okButton);
+ bezierPanel = (BezierAnimationPanel) bezierAnimationPanel.getSource();
+ colorChooser.waitStateOnQueue(jColorChooser -> (bezierPanel.getBezierColor(bezierColor).equals(testColor)));
+ }
+
+ private void checkCancelButton(JButtonOperator jbo, Color testColor) throws Exception {
+ pushButtonAndInitialize(jbo);
+ setAndWaitColor(testColor);
+ pushButtonAndWaitDialogClosed(cancelButton);
+ }
-private void checkGradient1ColorChooser() throws Exception {
-basicCheck(gradient1Button, BezierColor.GRADIENT_A);
-}
+ private void checkResetButton(JButtonOperator jbo, Color testColor) throws Exception {
+ pushButtonAndInitialize(jbo);
+ Color initialColor = colorChooser.getColor();
+ setAndWaitColor(testColor);
+ resetButton.push();
+ waitJColorChooserColor(initialColor);
+ pushButtonAndWaitDialogClosed(okButton);
+ }
-private void checkGradient2ColorChooser() throws Exception {
-basicCheck(gradient2Button, BezierColor.GRADIENT_B);
-}
+ private void checkAllColorChoosers(JButtonOperator jbo) throws Exception {
+ pushButtonAndInitialize(jbo);
+ checkHSV();
+ checkHSL();
+ checkRGB();
+ checkCMYK();
+ pushButtonAndWaitDialogClosed(okButton);
+ }
-private void checkPerimeterColorChooser() throws Exception {
-basicCheck(perimeterButton, BezierColor.OUTER);
-}
+ private void waitJColorChooserColor(Color expectedColor) {
+ colorChooser.waitStateOnQueue(jColorChooser -> colorChooser.getColor().equals(expectedColor));
+ }
+
+ private void setAndWaitColor(Color color) {
+ colorChooser.setColor(color);
+ // Wait for the Color to be set
+ waitJColorChooserColor(color);
+ }
-private void pushButtonAndInitialize(JButtonOperator jbo) throws InterruptedException {
-// Wait for focus to return to last focused button
-lastFocusedButton.waitHasFocus();
-jbo.pushNoBlock();
-lastFocusedButton = jbo;
-// Wait till the ColorChooserDemo Dialog Opens
-new DialogWaiter().waitDialog(CHOOSER_TITLE, false, false);
-initializeDialog();
-}
+ private void resetColor() {
+ colorChooser.setColor(resetColor);
+ // Wait for the Color to be reset
+ waitJColorChooserColor(resetColor);
+ }
-private void initializePanelButtons(JFrameOperator frame) {
-backgroundButton = new JButtonOperator(frame, BACKGROUND);
-gradient1Button = new JButtonOperator(frame, GRADIENT_1);
-gradient2Button = new JButtonOperator(frame, GRADIENT_2);
-perimeterButton = new JButtonOperator(frame, PERIMETER);
-lastFocusedButton = backgroundButton;
-}
+ private void checkHSV() {
+ tabOperator.selectPage(HSV);
+ initializeSliderAndSpinner(HSV_NUMBER_OF_SLIDERS_AND_SPINNERS);
+ resetColor();
+ setAndCheckSlider(sliders[HSV_SATURATION_INDEX], 50, new Color(125, 62, 62));
+ setAndCheckSlider(sliders[HSV_VALUE_INDEX], 80, new Color(204, 102, 102));
+ setAndCheckSlider(sliders[HSV_HUE_INDEX], 50, new Color(204, 187, 102));
+ setAndCheckSlider(sliders[HSV_TRANSPARENCY_INDEX], 50, new Color(204, 187, 102, 127));
+ setAndCheckSpinner(spinners[HSV_SATURATION_INDEX], 25, new Color(204, 195, 153, 127));
+ setAndCheckSpinner(spinners[HSV_VALUE_INDEX], 40, new Color(102, 97, 76, 127));
+ setAndCheckSpinner(spinners[HSV_HUE_INDEX], 25, new Color(102, 87, 76, 127));
+ setAndCheckSpinner(spinners[HSV_TRANSPARENCY_INDEX], 100, new Color(102, 87, 76, 0));
+ }
+
+ private void checkHSL() {
+ tabOperator.selectPage(HSL);
+ initializeSliderAndSpinner(HSL_NUMBER_OF_SLIDERS_AND_SPINNERS);
+ resetColor();
+ setAndCheckSlider(sliders[HSL_SATURATION_INDEX], 50, new Color(187, 62, 62));
+ setAndCheckSlider(sliders[HSL_LIGHTNESS_INDEX], 80, new Color(229, 178, 178));
+ setAndCheckSlider(sliders[HSL_HUE_INDEX], 180, new Color(178, 229, 229));
+ setAndCheckSlider(sliders[HSL_TRANSPARENCY_INDEX], 50, new Color(178, 229, 229, 127));
+ setAndCheckSpinner(spinners[HSL_SATURATION_INDEX], 25, new Color(191, 216, 216, 127));
+ setAndCheckSpinner(spinners[HSL_LIGHTNESS_INDEX], 40, new Color(76, 127, 127, 127));
+ setAndCheckSpinner(spinners[HSL_HUE_INDEX], 25, new Color(127, 97, 76, 127));
+ setAndCheckSpinner(spinners[HSL_TRANSPARENCY_INDEX], 50, new Color(127, 97, 76, 127));
+ }
-private void initializeDialog() {
-colorChooserDialog = new JDialogOperator(CHOOSER_TITLE);
-tabOperator = new JTabbedPaneOperator(colorChooserDialog);
-colorChooser = new JColorChooserOperator(colorChooserDialog);
-okButton = new JButtonOperator(colorChooserDialog, OK_BUTTON_TITLE);
-cancelButton = new JButtonOperator(colorChooserDialog, CANCEL_BUTTON_TITLE);
-resetButton = new JButtonOperator(colorChooserDialog, RESET_BUTTON_TITLE);
-}
+ private void checkRGB() {
+ String sampleColor = "111111";
+ tabOperator.selectPage(RGB);
+ initializeSliderAndSpinner(RGB_NUMBER_OF_SLIDERS_AND_SPINNERS);
+ JTextFieldOperator colorCode = new JTextFieldOperator(colorChooserDialog, RGB_COLORCODE_TEXT_FIELD_INDEX);
+ resetColor();
+ setAndCheckSlider(sliders[RGB_GREEN_INDEX], 50, new Color(125, 50, 125, 255));
+ setAndCheckSlider(sliders[RGB_BLUE_INDEX], 80, new Color(125, 50, 80, 255));
+ setAndCheckSlider(sliders[RGB_RED_INDEX], 50, new Color(50, 50, 80, 255));
+ setAndCheckSlider(sliders[RGB_ALPHA_INDEX], 125, new Color(50, 50, 80, 125));
+ setAndCheckSpinner(spinners[RGB_GREEN_INDEX], 25, new Color(50, 25, 80, 125));
+ setAndCheckSpinner(spinners[RGB_BLUE_INDEX], 40, new Color(50, 25, 40, 125));
+ setAndCheckSpinner(spinners[RGB_RED_INDEX], 25, new Color(25, 25, 40, 125));
+ setAndCheckSpinner(spinners[RGB_ALPHA_INDEX], 255, new Color(25, 25, 40, 255));
-private void basicCheck(JButtonOperator jbo, BezierColor bezierColor) throws Exception {
-Color testColor = new Color(100, 26, 155);
-Color testColor2 = new Color(10, 40, 50);
-checkDefaultColorChooser(jbo, testColor, bezierColor);
-checkCancelButton(jbo, testColor2);
-checkResetButton(jbo, testColor2);
-}
+ colorCode.setText(sampleColor);
+ // Wait for the sampleColor to be set in the color code text field.
+ colorCode.waitText(sampleColor);
+ colorCode.getFocus();
+ colorCode.pressKey(KeyEvent.VK_TAB);
+ // Wait for the color to be set
+ waitJColorChooserColor(new Color(17, 17, 17, 255));
+ }
-private void checkDefaultColorChooser(JButtonOperator jbo, Color testColor, BezierColor bezierColor)
-throws Exception {
-BezierAnimationPanel bezierPanel;
-pushButtonAndInitialize(jbo);
-// Check ColorChooser color is being set and used accordingly
-// in the animation panel
-setAndWaitColor(testColor);
-pushButtonAndWaitDialogClosed(okButton);
-bezierPanel = (BezierAnimationPanel) bezierAnimationPanel.getSource();
-colorChooser.waitStateOnQueue(jColorChooser -> (bezierPanel.getBezierColor(bezierColor).equals(testColor)));
-}
+ private void checkCMYK() {
+ tabOperator.selectPage(CMYK);
+ initializeSliderAndSpinner(CMYK_NUMBER_OF_SLIDERS_AND_SPINNERS);
+ resetColor();
+ setAndCheckSlider(sliders[CMYK_MAGENTA_INDEX], 50, new Color(125, 100, 125, 255));
+ setAndCheckSlider(sliders[CMYK_YELLOW_INDEX], 80, new Color(125, 100, 85, 255));
+ setAndCheckSlider(sliders[CMYK_CYAN_INDEX], 50, new Color(100, 100, 85, 255));
+ setAndCheckSlider(sliders[CMYK_BLACK_INDEX], 50, new Color(164, 164, 140, 255));
+ setAndCheckSlider(sliders[CMYK_ALPHA_INDEX], 125, new Color(164, 164, 140, 125));
+ setAndCheckSpinner(spinners[CMYK_MAGENTA_INDEX], 25, new Color(164, 184, 140, 125));
+ setAndCheckSpinner(spinners[CMYK_YELLOW_INDEX], 40, new Color(164, 184, 172, 125));
+ setAndCheckSpinner(spinners[CMYK_CYAN_INDEX], 25, new Color(184, 184, 172, 125));
+ setAndCheckSpinner(spinners[CMYK_BLACK_INDEX], 100, new Color(139, 139, 130, 125));
+ setAndCheckSpinner(spinners[CMYK_ALPHA_INDEX], 255, new Color(139, 139, 130, 255));
+ }
-private void checkCancelButton(JButtonOperator jbo, Color testColor) throws Exception {
-pushButtonAndInitialize(jbo);
-setAndWaitColor(testColor);
-pushButtonAndWaitDialogClosed(cancelButton);
+ private void setAndCheckSlider(JSliderOperator slider, int sliderValue, Color expectedColor) {
+ slider.setValue(sliderValue);
+ // Wait for slider to attain the specified value
+ slider.waitStateOnQueue(jSlider -> slider.getValue() == sliderValue);
+ colorChooser.waitStateOnQueue(jColorChooser -> (colorChooser.getColor().equals(expectedColor)));
+ }
+
+ private void setAndCheckSpinner(JSpinnerOperator spinner, int spinnerValue, Color expectedColor) {
+ spinner.setValue(spinnerValue);
+ // Wait for spinner to attain the specified value
+ spinner.waitStateOnQueue(jSpinner -> (int) spinner.getValue() == spinnerValue);
+ colorChooser.waitStateOnQueue(jColorChooser -> (colorChooser.getColor().equals(expectedColor)));
+ }
+
+ private void initializeSliderAndSpinner(int numberOfSlidersAndSpinners) {
+ for (int i = 0; i < numberOfSlidersAndSpinners; i++) {
+ sliders[i] = new JSliderOperator(colorChooserDialog, i);
+ spinners[i] = new JSpinnerOperator(colorChooserDialog, i);
+ }
+ }
+
+ private void pushButtonAndWaitDialogClosed(JButtonOperator button) {
+ button.push();
+ // Wait for the color chooser dialog to close.
+ colorChooserDialog.waitClosed();
+ }
}
-private void checkResetButton(JButtonOperator jbo, Color testColor) throws Exception {
-pushButtonAndInitialize(jbo);
-Color initialColor = colorChooser.getColor();
-setAndWaitColor(testColor);
-resetButton.push();
-waitJColorChooserColor(initialColor);
-pushButtonAndWaitDialogClosed(okButton);
-}
-
-private void checkAllColorChoosers(JButtonOperator jbo) throws Exception {
-pushButtonAndInitialize(jbo);
-checkHSV();
-checkHSL();
-checkRGB();
-checkCMYK();
-pushButtonAndWaitDialogClosed(okButton);
-}
-
-private void waitJColorChooserColor(Color expectedColor) {
-colorChooser.waitStateOnQueue(jColorChooser -> colorChooser.getColor().equals(expectedColor));
-}
-
-private void setAndWaitColor(Color color) {
-colorChooser.setColor(color);
-// Wait for the Color to be set
-waitJColorChooserColor(color);
-}
-
-private void resetColor() {
-colorChooser.setColor(resetColor);
-// Wait for the Color to be reset
-waitJColorChooserColor(resetColor);
-}
-
-private void checkHSV() {
-tabOperator.selectPage(HSV);
-initializeSliderAndSpinner(HSV_NUMBER_OF_SLIDERS_AND_SPINNERS);
-resetColor();
-setAndCheckSlider(sliders[HSV_SATURATION_INDEX], 50, new Color(125, 62, 62));
-setAndCheckSlider(sliders[HSV_VALUE_INDEX], 80, new Color(204, 102, 102));
-setAndCheckSlider(sliders[HSV_HUE_INDEX], 50, new Color(204, 187, 102));
-setAndCheckSlider(sliders[HSV_TRANSPARENCY_INDEX], 50, new Color(204, 187, 102, 127));
-setAndCheckSpinner(spinners[HSV_SATURATION_INDEX], 25, new Color(204, 195, 153, 127));
-setAndCheckSpinner(spinners[HSV_VALUE_INDEX], 40, new Color(102, 97, 76, 127));
-setAndCheckSpinner(spinners[HSV_HUE_INDEX], 25, new Color(102, 87, 76, 127));
-setAndCheckSpinner(spinners[HSV_TRANSPARENCY_INDEX], 100, new Color(102, 87, 76, 0));
-}
-
-private void checkHSL() {
-tabOperator.selectPage(HSL);
-initializeSliderAndSpinner(HSL_NUMBER_OF_SLIDERS_AND_SPINNERS);
-resetColor();
-setAndCheckSlider(sliders[HSL_SATURATION_INDEX], 50, new Color(187, 62, 62));
-setAndCheckSlider(sliders[HSL_LIGHTNESS_INDEX], 80, new Color(229, 178, 178));
-setAndCheckSlider(sliders[HSL_HUE_INDEX], 180, new Color(178, 229, 229));
-setAndCheckSlider(sliders[HSL_TRANSPARENCY_INDEX], 50, new Color(178, 229, 229, 127));
-setAndCheckSpinner(spinners[HSL_SATURATION_INDEX], 25, new Color(191, 216, 216, 127));
-setAndCheckSpinner(spinners[HSL_LIGHTNESS_INDEX], 40, new Color(76, 127, 127, 127));
-setAndCheckSpinner(spinners[HSL_HUE_INDEX], 25, new Color(127, 97, 76, 127));
-setAndCheckSpinner(spinners[HSL_TRANSPARENCY_INDEX], 50, new Color(127, 97, 76, 127));
-}
-
-private void checkRGB() {
-String sampleColor = "111111";
-tabOperator.selectPage(RGB);
-initializeSliderAndSpinner(RGB_NUMBER_OF_SLIDERS_AND_SPINNERS);
-JTextFieldOperator colorCode = new JTextFieldOperator(colorChooserDialog, RGB_COLORCODE_TEXT_FIELD_INDEX);
-resetColor();
-setAndCheckSlider(sliders[RGB_GREEN_INDEX], 50, new Color(125, 50, 125, 255));
-setAndCheckSlider(sliders[RGB_BLUE_INDEX], 80, new Color(125, 50, 80, 255));
-setAndCheckSlider(sliders[RGB_RED_INDEX], 50, new Color(50, 50, 80, 255));
-setAndCheckSlider(sliders[RGB_ALPHA_INDEX], 125, new Color(50, 50, 80, 125));
-setAndCheckSpinner(spinners[RGB_GREEN_INDEX], 25, new Color(50, 25, 80, 125));
-setAndCheckSpinner(spinners[RGB_BLUE_INDEX], 40, new Color(50, 25, 40, 125));
-setAndCheckSpinner(spinners[RGB_RED_INDEX], 25, new Color(25, 25, 40, 125));
-setAndCheckSpinner(spinners[RGB_ALPHA_INDEX], 255, new Color(25, 25, 40, 255));
-
-colorCode.setText(sampleColor);
-// Wait for the sampleColor to be set in the color code text field.
-colorCode.waitText(sampleColor);
-colorCode.getFocus();
-colorCode.pressKey(KeyEvent.VK_TAB);
-// Wait for the color to be set
-waitJColorChooserColor(new Color(17, 17, 17, 255));
-}
-
-private void checkCMYK() {
-tabOperator.selectPage(CMYK);
-initializeSliderAndSpinner(CMYK_NUMBER_OF_SLIDERS_AND_SPINNERS);
-resetColor();
-setAndCheckSlider(sliders[CMYK_MAGENTA_INDEX], 50, new Color(125, 100, 125, 255));
-setAndCheckSlider(sliders[CMYK_YELLOW_INDEX], 80, new Color(125, 100, 85, 255));
-setAndCheckSlider(sliders[CMYK_CYAN_INDEX], 50, new Color(100, 100, 85, 255));
-setAndCheckSlider(sliders[CMYK_BLACK_INDEX], 50, new Color(164, 164, 140, 255));
-setAndCheckSlider(sliders[CMYK_ALPHA_INDEX], 125, new Color(164, 164, 140, 125));
-setAndCheckSpinner(spinners[CMYK_MAGENTA_INDEX], 25, new Color(164, 184, 140, 125));
-setAndCheckSpinner(spinners[CMYK_YELLOW_INDEX], 40, new Color(164, 184, 172, 125));
-setAndCheckSpinner(spinners[CMYK_CYAN_INDEX], 25, new Color(184, 184, 172, 125));
-setAndCheckSpinner(spinners[CMYK_BLACK_INDEX], 100, new Color(139, 139, 130, 125));
-setAndCheckSpinner(spinners[CMYK_ALPHA_INDEX], 255, new Color(139, 139, 130, 255));
-}
-
-private void setAndCheckSlider(JSliderOperator slider, int sliderValue, Color expectedColor) {
-slider.setValue(sliderValue);
-// Wait for slider to attain the specified value
-slider.waitStateOnQueue(jSlider -> slider.getValue() == sliderValue);
-colorChooser.waitStateOnQueue(jColorChooser -> (colorChooser.getColor().equals(expectedColor)));
-}
-
-private void setAndCheckSpinner(JSpinnerOperator spinner, int spinnerValue, Color expectedColor) {
-spinner.setValue(spinnerValue);
-// Wait for spinner to attain the specified value
-spinner.waitStateOnQueue(jSpinner -> (int) spinner.getValue() == spinnerValue);
-colorChooser.waitStateOnQueue(jColorChooser -> (colorChooser.getColor().equals(expectedColor)));
-}
-
-private void initializeSliderAndSpinner(int numberOfSlidersAndSpinners) {
-for (int i = 0; i < numberOfSlidersAndSpinners; i++) {
-sliders[i] = new JSliderOperator(colorChooserDialog, i);
-spinners[i] = new JSpinnerOperator(colorChooserDialog, i);
-}
-}
-
-private void pushButtonAndWaitDialogClosed(JButtonOperator button) {
-button.push();
-// Wait for the color chooser dialog to close.
-colorChooserDialog.waitClosed();
-}
-}
--- a/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/colorchooser/BezierAnimationPanel.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/colorchooser/BezierAnimationPanel.java Thu Jul 25 08:24:01 2019 -0400
@@ -35,220 +35,221 @@
import static com.sun.swingset3.demos.colorchooser.BezierAnimationPanel.BezierColor.*;
/**
-* BezierAnimationPanel
-*
-* @author Jim Graham
-* @author Jeff Dinkins (removed dynamic setting changes, made swing friendly)
-* @version 1.16 11/17/05
-*/
+ * BezierAnimationPanel
+ *
+ * @author Jim Graham
+ * @author Jeff Dinkins (removed dynamic setting changes, made swing friendly)
+ * @version 1.16 11/17/05
+ */
public class BezierAnimationPanel extends JPanel implements Runnable {
-public static enum BezierColor {
-BACKGROUND, OUTER, GRADIENT_A, GRADIENT_B
-}
+ public static enum BezierColor {
+ BACKGROUND, OUTER, GRADIENT_A, GRADIENT_B
+ }
+
+ private final Map<BezierColor, Color> colors = new Hashtable<BezierColor, Color>();
+
+ private GradientPaint gradient = null;
+
+ private static final int NUMPTS = 6;
+
+ private final float[] animpts = new float[NUMPTS * 2];
+
+ private final float[] deltas = new float[NUMPTS * 2];
+
+ private BufferedImage img;
-private final Map<BezierColor, Color> colors = new Hashtable<BezierColor, Color>();
+ private Thread anim;
+
+ private final Object lock = new Object();
-private GradientPaint gradient = null;
-
-private static final int NUMPTS = 6;
+ /**
+ * BezierAnimationPanel Constructor
+ */
+ public BezierAnimationPanel() {
+ setOpaque(true);
-private final float[] animpts = new float[NUMPTS * 2];
+ colors.put(BACKGROUND, new Color(0, 0, 153));
+ colors.put(OUTER, new Color(255, 255, 255));
+ colors.put(GRADIENT_A, new Color(255, 0, 101));
+ colors.put(GRADIENT_B, new Color(255, 255, 0));
-private final float[] deltas = new float[NUMPTS * 2];
-
-private BufferedImage img;
+ addHierarchyListener(new HierarchyListener() {
+ public void hierarchyChanged(HierarchyEvent e) {
+ if (isShowing()) {
+ start();
+ } else {
+ stop();
+ }
+ }
+ });
+ }
-private Thread anim;
-
-private final Object lock = new Object();
+ public Color getBezierColor(BezierColor bezierColor) {
+ return colors.get(bezierColor);
+ }
-/**
-* BezierAnimationPanel Constructor
-*/
-public BezierAnimationPanel() {
-setOpaque(true);
+ public void setBezierColor(BezierColor bezierColor, Color value) {
+ if (value != null) {
+ colors.put(bezierColor, value);
+ }
+ }
-colors.put(BACKGROUND, new Color(0, 0, 153));
-colors.put(OUTER, new Color(255, 255, 255));
-colors.put(GRADIENT_A, new Color(255, 0, 101));
-colors.put(GRADIENT_B, new Color(255, 255, 0));
+ public void start() {
+ Dimension size = getSize();
+ for (int i = 0; i < animpts.length; i += 2) {
+ animpts[i] = (float) (Math.random() * size.width);
+ animpts[i + 1] = (float) (Math.random() * size.height);
+ deltas[i] = (float) (Math.random() * 4.0 + 2.0);
+ deltas[i + 1] = (float) (Math.random() * 4.0 + 2.0);
+ if (animpts[i] > size.width / 6.0f) {
+ deltas[i] = -deltas[i];
+ }
+ if (animpts[i + 1] > size.height / 6.0f) {
+ deltas[i + 1] = -deltas[i + 1];
+ }
+ }
+ anim = new Thread(this);
+ anim.setPriority(Thread.MIN_PRIORITY);
+ anim.start();
+ }
-addHierarchyListener(new HierarchyListener() {
-public void hierarchyChanged(HierarchyEvent e) {
-if (isShowing()) {
-start();
-} else {
-stop();
-}
-}
-});
-}
+ public synchronized void stop() {
+ anim = null;
+ notify();
+ }
+
+ private static void animate(float[] pts, float[] deltas, int index, int limit) {
+ float newpt = pts[index] + deltas[index];
+ if (newpt <= 0) {
+ newpt = -newpt;
+ deltas[index] = (float) (Math.random() * 3.0 + 2.0);
+ } else if (newpt >= (float) limit) {
+ newpt = 2.0f * limit - newpt;
+ deltas[index] = -(float) (Math.random() * 3.0 + 2.0);
+ }
+ pts[index] = newpt;
+ }
+
+ public void run() {
+ Thread me = Thread.currentThread();
+ while (getSize().width <= 0) {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
-public Color getBezierColor(BezierColor bezierColor) {
-return colors.get(bezierColor);
-}
+ Graphics2D g2d = null;
+ Graphics2D bufferG2D = null;
+ BasicStroke solid = new BasicStroke(9.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 9.0f);
+ GeneralPath gp = new GeneralPath(GeneralPath.WIND_NON_ZERO);
+ int rule = AlphaComposite.SRC_OVER;
+ AlphaComposite opaque = AlphaComposite.SrcOver;
+ AlphaComposite blend = AlphaComposite.getInstance(rule, 0.9f);
+ AlphaComposite set = AlphaComposite.Src;
+ Dimension oldSize = getSize();
+ Shape clippath = null;
+ while (anim == me) {
+ Dimension size = getSize();
+ if (size.width != oldSize.width || size.height != oldSize.height) {
+ img = null;
+ clippath = null;
+ if (bufferG2D != null) {
+ bufferG2D.dispose();
+ bufferG2D = null;
+ }
+ }
+ oldSize = size;
-public void setBezierColor(BezierColor bezierColor, Color value) {
-if (value != null) {
-colors.put(bezierColor, value);
-}
-}
+ if (img == null) {
+ img = (BufferedImage) createImage(size.width, size.height);
+ }
+
+ if (bufferG2D == null) {
+ bufferG2D = img.createGraphics();
+ bufferG2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_DEFAULT);
+ bufferG2D.setClip(clippath);
+ }
+ g2d = bufferG2D;
-public void start() {
-Dimension size = getSize();
-for (int i = 0; i < animpts.length; i += 2) {
-animpts[i] = (float) (Math.random() * size.width);
-animpts[i + 1] = (float) (Math.random() * size.height);
-deltas[i] = (float) (Math.random() * 4.0 + 2.0);
-deltas[i + 1] = (float) (Math.random() * 4.0 + 2.0);
-if (animpts[i] > size.width / 6.0f) {
-deltas[i] = -deltas[i];
-}
-if (animpts[i + 1] > size.height / 6.0f) {
-deltas[i + 1] = -deltas[i + 1];
-}
-}
-anim = new Thread(this);
-anim.setPriority(Thread.MIN_PRIORITY);
-anim.start();
-}
+ float[] ctrlpts;
+ for (int i = 0; i < animpts.length; i += 2) {
+ animate(animpts, deltas, i, size.width);
+ animate(animpts, deltas, i + 1, size.height);
+ }
+ ctrlpts = animpts;
+ int len = ctrlpts.length;
+ gp.reset();
+ float prevx = ctrlpts[len - 2];
+ float prevy = ctrlpts[len - 1];
+ float curx = ctrlpts[0];
+ float cury = ctrlpts[1];
+ float midx = (curx + prevx) / 2.0f;
+ float midy = (cury + prevy) / 2.0f;
+ gp.moveTo(midx, midy);
+ for (int i = 2; i <= ctrlpts.length; i += 2) {
+ float x1 = (midx + curx) / 2.0f;
+ float y1 = (midy + cury) / 2.0f;
+ prevx = curx;
+ prevy = cury;
+ if (i < ctrlpts.length) {
+ curx = ctrlpts[i];
+ cury = ctrlpts[i + 1];
+ } else {
+ curx = ctrlpts[0];
+ cury = ctrlpts[1];
+ }
+ midx = (curx + prevx) / 2.0f;
+ midy = (cury + prevy) / 2.0f;
+ float x2 = (prevx + midx) / 2.0f;
+ float y2 = (prevy + midy) / 2.0f;
+ gp.curveTo(x1, y1, x2, y2, midx, midy);
+ }
+ gp.closePath();
-public synchronized void stop() {
-anim = null;
-notify();
-}
+ synchronized (lock) {
+ g2d.setComposite(set);
+ g2d.setBackground(getBezierColor(BACKGROUND));
+ g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
+
+ // g2d.clearRect(bounds.x-5, bounds.y-5, bounds.x + bounds.width
+ // + 5, bounds.y + bounds.height + 5);
+ g2d.clearRect(0, 0, getWidth(), getHeight());
+
+ g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g2d.setColor(getBezierColor(OUTER));
+ g2d.setComposite(opaque);
+ g2d.setStroke(solid);
+ g2d.draw(gp);
+ g2d.setPaint(gradient);
+
+ Rectangle bounds = gp.getBounds();
+
+ gradient = new GradientPaint(bounds.x, bounds.y, getBezierColor(GRADIENT_A), bounds.x + bounds.width,
+ bounds.y + bounds.height, getBezierColor(GRADIENT_B), true);
-private static void animate(float[] pts, float[] deltas, int index, int limit) {
-float newpt = pts[index] + deltas[index];
-if (newpt <= 0) {
-newpt = -newpt;
-deltas[index] = (float) (Math.random() * 3.0 + 2.0);
-} else if (newpt >= (float) limit) {
-newpt = 2.0f * limit - newpt;
-deltas[index] = -(float) (Math.random() * 3.0 + 2.0);
-}
-pts[index] = newpt;
-}
+ g2d.setComposite(blend);
+ g2d.fill(gp);
+ }
+
+ repaint();
-public void run() {
-Thread me = Thread.currentThread();
-while (getSize().width <= 0) {
-try {
-Thread.sleep(500);
-} catch (InterruptedException e) {
-return;
-}
+ Thread.yield();
+ }
+ if (g2d != null) {
+ g2d.dispose();
+ }
+ }
+
+ public void paint(Graphics g) {
+ synchronized (lock) {
+ Graphics2D g2d = (Graphics2D) g;
+ if (img != null) {
+ g2d.setComposite(AlphaComposite.Src);
+ g2d.drawImage(img, null, 0, 0);
+ }
+ }
+ }
}
-Graphics2D g2d = null;
-Graphics2D bufferG2D = null;
-BasicStroke solid = new BasicStroke(9.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 9.0f);
-GeneralPath gp = new GeneralPath(GeneralPath.WIND_NON_ZERO);
-int rule = AlphaComposite.SRC_OVER;
-AlphaComposite opaque = AlphaComposite.SrcOver;
-AlphaComposite blend = AlphaComposite.getInstance(rule, 0.9f);
-AlphaComposite set = AlphaComposite.Src;
-Dimension oldSize = getSize();
-Shape clippath = null;
-while (anim == me) {
-Dimension size = getSize();
-if (size.width != oldSize.width || size.height != oldSize.height) {
-img = null;
-clippath = null;
-if (bufferG2D != null) {
-bufferG2D.dispose();
-bufferG2D = null;
-}
-}
-oldSize = size;
-
-if (img == null) {
-img = (BufferedImage) createImage(size.width, size.height);
-}
-
-if (bufferG2D == null) {
-bufferG2D = img.createGraphics();
-bufferG2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_DEFAULT);
-bufferG2D.setClip(clippath);
-}
-g2d = bufferG2D;
-
-float[] ctrlpts;
-for (int i = 0; i < animpts.length; i += 2) {
-animate(animpts, deltas, i, size.width);
-animate(animpts, deltas, i + 1, size.height);
-}
-ctrlpts = animpts;
-int len = ctrlpts.length;
-gp.reset();
-float prevx = ctrlpts[len - 2];
-float prevy = ctrlpts[len - 1];
-float curx = ctrlpts[0];
-float cury = ctrlpts[1];
-float midx = (curx + prevx) / 2.0f;
-float midy = (cury + prevy) / 2.0f;
-gp.moveTo(midx, midy);
-for (int i = 2; i <= ctrlpts.length; i += 2) {
-float x1 = (midx + curx) / 2.0f;
-float y1 = (midy + cury) / 2.0f;
-prevx = curx;
-prevy = cury;
-if (i < ctrlpts.length) {
-curx = ctrlpts[i];
-cury = ctrlpts[i + 1];
-} else {
-curx = ctrlpts[0];
-cury = ctrlpts[1];
-}
-midx = (curx + prevx) / 2.0f;
-midy = (cury + prevy) / 2.0f;
-float x2 = (prevx + midx) / 2.0f;
-float y2 = (prevy + midy) / 2.0f;
-gp.curveTo(x1, y1, x2, y2, midx, midy);
-}
-gp.closePath();
-
-synchronized (lock) {
-g2d.setComposite(set);
-g2d.setBackground(getBezierColor(BACKGROUND));
-g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
-
-// g2d.clearRect(bounds.x-5, bounds.y-5, bounds.x + bounds.width
-// + 5, bounds.y + bounds.height + 5);
-g2d.clearRect(0, 0, getWidth(), getHeight());
-
-g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
-g2d.setColor(getBezierColor(OUTER));
-g2d.setComposite(opaque);
-g2d.setStroke(solid);
-g2d.draw(gp);
-g2d.setPaint(gradient);
-
-Rectangle bounds = gp.getBounds();
-
-gradient = new GradientPaint(bounds.x, bounds.y, getBezierColor(GRADIENT_A), bounds.x + bounds.width,
-bounds.y + bounds.height, getBezierColor(GRADIENT_B), true);
-
-g2d.setComposite(blend);
-g2d.fill(gp);
-}
-
-repaint();
-
-Thread.yield();
-}
-if (g2d != null) {
-g2d.dispose();
-}
-}
-
-public void paint(Graphics g) {
-synchronized (lock) {
-Graphics2D g2d = (Graphics2D) g;
-if (img != null) {
-g2d.setComposite(AlphaComposite.Src);
-g2d.drawImage(img, null, 0, 0);
-}
-}
-}
-}
\ No newline at end of file
--- a/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/TreeDemo.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/TreeDemo.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2019, 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
@@ -83,7 +83,7 @@
private JTree createTree() {
DefaultMutableTreeNode top = new DefaultMutableTreeNode(resourceManager.getString("TreeDemo.music"));
- DefaultMutableTreeNode catagory = null;
+ DefaultMutableTreeNode category = null;
DefaultMutableTreeNode artist = null;
DefaultMutableTreeNode record = null;
@@ -103,12 +103,12 @@
char linetype = line.charAt(0);
switch (linetype) {
case 'C':
- catagory = new DefaultMutableTreeNode(line.substring(2));
- top.add(catagory);
+ category = new DefaultMutableTreeNode(line.substring(2));
+ top.add(category);
break;
case 'A':
- if (catagory != null) {
- catagory.add(artist = new DefaultMutableTreeNode(line.substring(2)));
+ if (category != null) {
+ category.add(artist = new DefaultMutableTreeNode(line.substring(2)));
}
break;
case 'R':
--- a/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/resources/tree.txt Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/resources/tree.txt Thu Jul 25 08:24:01 2019 -0400
@@ -8,7 +8,7 @@
# A = Artist / Composer #
# R = Record / Style #
# S = Song Name / Composition #
-# C = Catagory #
+# C = Category #
# #
################################################################################
C Classical
@@ -195,49 +195,49 @@
A The Beatles
R A Hard Day's Night
S A Hard Day's Night
-S I Should Have Known Better
-S If I Fell
-S I'm Happy Just To Dance With You
-S And I Love Her
-S Tell Me Why
-S Can't Buy Me Love
-S Any Time At All
-S I'll Cry Instead
-S Things We Said Today
-S When I Get Home
-S You Can't Do That
+S I Should Have Known Better
+S If I Fell
+S I'm Happy Just To Dance With You
+S And I Love Her
+S Tell Me Why
+S Can't Buy Me Love
+S Any Time At All
+S I'll Cry Instead
+S Things We Said Today
+S When I Get Home
+S You Can't Do That
R Beatles For Sale
-S No Reply
-S I'm a Loser
-S Baby's In Black
-S Rock And Roll Music
-S I'll Follow the Sun
-S Mr. Moonlight
-S Kansas City/Hey Hey Hey Hey
-S Eight Days a Week
-S Words Of Love
-S Honey Don't
-S Every Little Thing
-S I Don't Want To Spoil the Party
-S What You're Doing
-S Everybody's Trying To Be My Baby
+S No Reply
+S I'm a Loser
+S Baby's In Black
+S Rock And Roll Music
+S I'll Follow the Sun
+S Mr. Moonlight
+S Kansas City/Hey Hey Hey Hey
+S Eight Days a Week
+S Words Of Love
+S Honey Don't
+S Every Little Thing
+S I Don't Want To Spoil the Party
+S What You're Doing
+S Everybody's Trying To Be My Baby
R Help!
-S Help!
-S The Night Before
-S You've Got To Hide Your Love Away
-S I Need You
-S Another Girl
-S You're Going To Lose That Girl
-S Ticket To Ride
-S Act Naturally
-S It's Only Love
-S You Like Me Too Much
-S Tell Me What You See
-S I've Just Seen a Face
-S Yesterday
-S Dizzy Miss Lizzie
+S Help!
+S The Night Before
+S You've Got To Hide Your Love Away
+S I Need You
+S Another Girl
+S You're Going To Lose That Girl
+S Ticket To Ride
+S Act Naturally
+S It's Only Love
+S You Like Me Too Much
+S Tell Me What You See
+S I've Just Seen a Face
+S Yesterday
+S Dizzy Miss Lizzie
R Rubber Soul
-S Drive My Car
+S Drive My Car
S Norwegian Wood
S You Won't See Me
S Nowhere Man
@@ -245,27 +245,27 @@
S The Word
S Michelle
S What Goes On?
-S Girl
-S I'm Looking Through You
-S In My Life
-S Wait
-S If I Needed Someone
-S Run For Your Life
+S Girl
+S I'm Looking Through You
+S In My Life
+S Wait
+S If I Needed Someone
+S Run For Your Life
R Revolver
-S Taxman
-S Rigby
-S I'm Only Sleeping
-S For You To
-S Here There And Everywhere
+S Taxman
+S Rigby
+S I'm Only Sleeping
+S For You To
+S Here There And Everywhere
S Yellow Submarine
-S She Said She Said
-S Good Day Sunshine
-S And Your Bird Can Sing
-S For No One
-S Doctor Robert
-S I Want To Tell You
-S Got To Get You Into My Life
-S Tomorrow Never Knows
+S She Said She Said
+S Good Day Sunshine
+S And Your Bird Can Sing
+S For No One
+S Doctor Robert
+S I Want To Tell You
+S Got To Get You Into My Life
+S Tomorrow Never Knows
R Sgt. Pepper's Lonely Hearts Club Band
S Sgt. Pepper's Lonely Hearts Club Band
S With a Little Help From My Friends
@@ -554,35 +554,35 @@
S What A Life
A Komeda
R Plan 714 Till
-S Fuego De La Vida
-S Herbamore
-S Som I Fjol
+S Fuego De La Vida
+S Herbamore
+S Som I Fjol
S En Spricka I Taket
R Genius Of
-S More Is More
-S Fire
-S Rocket Plane (Music On The Moon)
-S Boogie Woogie/Rock 'N' Roll
-S Disko
-S Top Star
-S Light O' My Life
-S If
-S Frolic
-S In Orbit
-S Arbogast
+S More Is More
+S Fire
+S Rocket Plane (Music On The Moon)
+S Boogie Woogie/Rock 'N' Roll
+S Disko
+S Top Star
+S Light O' My Life
+S If
+S Frolic
+S In Orbit
+S Arbogast
S New New No
R What Makes It Go
-S Binario
-S It's Alright, Baby
-S Curious
-S Cul de Sac
-S Living Things
-S Flabbergast
-S Campfire
-S Happyment
-S Our Hospitality
-S Focus
-S A Simple Formality
+S Binario
+S It's Alright, Baby
+S Curious
+S Cul de Sac
+S Living Things
+S Flabbergast
+S Campfire
+S Happyment
+S Our Hospitality
+S Focus
+S A Simple Formality
A Steve Miller Band
R Circle Of Love
S Heart Like A Wheel
--- a/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -22,23 +22,21 @@
*/
import java.io.File;
+import java.io.PrintWriter;
import java.net.InetAddress;
-import java.net.NetworkInterface;
import java.net.UnknownHostException;
-import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
-import java.util.stream.Collectors;
+import java.util.concurrent.CountDownLatch;
-import jdk.test.lib.thread.ProcessThread;
+import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
/**
* @test
* @bug 6425769
* @summary Test JMX agent host address binding. Same ports but different
- * interfaces to bind to (selecting plain or SSL sockets at random
- * @key intermittent
+ * interfaces to bind to (selecting plain or SSL sockets at random)
*
* @library /test/lib
* @modules java.management.rmi
@@ -52,14 +50,9 @@
public static final int STOP_PROCESS_EXIT_VAL = 10;
public static final int JMX_PORT_RANGE_LOWER = 9100;
public static final int JMX_PORT_RANGE_UPPER = 9200;
- public static final int JMX_PORT = getRandomPortInRange(JMX_PORT_RANGE_LOWER,
- JMX_PORT_RANGE_UPPER);
public static final int JMX_PORT_RANGE_LOWER_SSL = 9201; // 9200 might be RMI Port
public static final int JMX_PORT_RANGE_UPPER_SSL = 9300;
- public static final int JMX_PORT_SSL = getRandomPortInRange(JMX_PORT_RANGE_LOWER_SSL,
- JMX_PORT_RANGE_UPPER_SSL);
- public static final int RMI_PORT = JMX_PORT + 1;
- public static final int RMI_PORT_SSL = JMX_PORT_SSL + 1;
+ private static final int MAX_RETRY_ATTEMTS = 10;
public static final String READY_MSG = "MainThread: Ready for connections";
public static final String TEST_CLASS = JMXAgentInterfaceBinding.class.getSimpleName();
public static final String KEYSTORE_LOC = System.getProperty("test.src", ".") +
@@ -82,98 +75,25 @@
}
private void runTests(List<InetAddress> addrs, boolean useSSL) {
- List<ProcessThread> jvms = new ArrayList<>(addrs.size());
- int i = 1;
+ List<TestProcessThread> testThreads = new ArrayList<>(addrs.size());
+ CountDownLatch latch = new CountDownLatch(addrs.size());
for (InetAddress addr : addrs) {
String address = JMXAgentInterfaceBinding.wrapAddress(addr.getHostAddress());
- System.out.println();
- String msg = String.format("DEBUG: Launching java tester for triplet (HOSTNAME,JMX_PORT,RMI_PORT) == (%s,%d,%d)",
- address,
- useSSL ? JMX_PORT_SSL : JMX_PORT,
- useSSL ? RMI_PORT_SSL : RMI_PORT);
- System.out.println(msg);
- ProcessThread jvm = runJMXBindingTest(address, useSSL);
- jvms.add(jvm);
- jvm.start();
- System.out.println("DEBUG: Started " + (i++) + " Process(es).");
- }
- int failedProcesses = 0;
- for (ProcessThread pt: jvms) {
- try {
- pt.sendMessage("Exit: " + STOP_PROCESS_EXIT_VAL);
- pt.join();
- } catch (Throwable e) {
- System.err.println("Failed to stop process: " + pt.getName());
- throw new RuntimeException("Test failed", e);
- }
- int exitValue = pt.getOutput().getExitValue();
- // If there is a communication error (the case we care about)
- // we get a exit code of 1
- if (exitValue == COMMUNICATION_ERROR_EXIT_VAL) {
- // Failure case since the java processes should still be
- // running.
- System.err.println("Test FAILURE on " + pt.getName());
- failedProcesses++;
- } else if (exitValue == STOP_PROCESS_EXIT_VAL) {
- System.out.println("DEBUG: OK. Spawned java process terminated with expected exit code of " + STOP_PROCESS_EXIT_VAL);
- } else {
- System.err.println("Test FAILURE on " + pt.getName() + " reason: Unexpected exit code => " + exitValue);
- failedProcesses++;
- }
+ TestProcessThread t = new TestProcessThread(address, useSSL, latch);
+ testThreads.add(t);
+ t.start();
}
- if (failedProcesses > 0) {
- throw new RuntimeException("Test FAILED. " + failedProcesses + " out of " + addrs.size() + " process(es) failed to start the JMX agent.");
- }
- }
-
- private ProcessThread runJMXBindingTest(String address, boolean useSSL) {
- List<String> args = new ArrayList<>();
- args.add("-classpath");
- args.add(TEST_CLASSPATH);
- args.add("-Dcom.sun.management.jmxremote.host=" + address);
- args.add("-Dcom.sun.management.jmxremote.port=" + (useSSL ? JMX_PORT_SSL : JMX_PORT));
- args.add("-Dcom.sun.management.jmxremote.rmi.port=" + (useSSL ? RMI_PORT_SSL : RMI_PORT));
- args.add("-Dcom.sun.management.jmxremote.authenticate=false");
- args.add("-Dcom.sun.management.jmxremote.ssl=" + Boolean.toString(useSSL));
- // This is needed for testing on loopback
- args.add("-Djava.rmi.server.hostname=" + address);
- if (useSSL) {
- args.add("-Dcom.sun.management.jmxremote.registry.ssl=true");
- args.add("-Djavax.net.ssl.keyStore=" + KEYSTORE_LOC);
- args.add("-Djavax.net.ssl.trustStore=" + TRUSTSTORE_LOC);
- args.add("-Djavax.net.ssl.keyStorePassword=password");
- args.add("-Djavax.net.ssl.trustStorePassword=trustword");
- }
- args.add(TEST_CLASS);
- args.add(address);
- args.add(Integer.toString(useSSL ? JMX_PORT_SSL : JMX_PORT));
- args.add(Integer.toString(useSSL ? RMI_PORT_SSL : RMI_PORT));
- args.add(Boolean.toString(useSSL));
try {
- ProcessBuilder builder = ProcessTools.createJavaProcessBuilder(args.toArray(new String[] {}));
- System.out.println(ProcessTools.getCommandLine(builder));
- ProcessThread jvm = new ProcessThread("JMX-Tester-" + address, JMXInterfaceBindingTest::isJMXAgentResponseAvailable, builder);
- return jvm;
- } catch (Exception e) {
+ latch.await();
+ } catch (InterruptedException e) {
+ System.err.println("Failed to wait for the test threads to complete");
throw new RuntimeException("Test failed", e);
}
- }
-
- private static boolean isJMXAgentResponseAvailable(String line) {
- if (line.equals(READY_MSG)) {
- System.out.println("DEBUG: Found expected READY_MSG.");
- return true;
- } else if (line.startsWith("Error:")) {
- // Allow for a JVM process that exits with
- // "Error: JMX connector server communication error: ..."
- // to continue as well since we handle that case elsewhere.
- // This has the effect that the test does not timeout and
- // fails with an exception in the test.
- System.err.println("PROBLEM: JMX agent of target JVM did not start as it should.");
- return true;
- } else {
- return false;
+ long failedProcesses = testThreads.stream().filter(TestProcessThread::isTestFailed).count();
+ if (failedProcesses > 0) {
+ throw new RuntimeException("Test FAILED. " + failedProcesses + " out of " + addrs.size() +
+ " process(es) failed to start the JMX agent.");
}
}
@@ -215,4 +135,128 @@
throw new RuntimeException("Test failed", e);
}
}
+
+ private static class TestProcessThread extends Thread {
+ private final String name;
+ private final String address;
+ private final boolean useSSL;
+ private final CountDownLatch latch;
+ private volatile boolean testFailed = false;
+ private OutputAnalyzer output;
+
+ public TestProcessThread(String address, boolean useSSL, CountDownLatch latch) {
+ this.address = address;
+ this.useSSL = useSSL;
+ this.name = "JMX-Tester-" + address;
+ this.latch = latch;
+ }
+
+ @Override
+ public void run() {
+ int attempts = 0;
+ boolean needRetry = false;
+ do {
+ if (needRetry) {
+ System.err.println("Retrying the test for " + name);
+ }
+ needRetry = runTest();
+ } while (needRetry && (attempts++ < MAX_RETRY_ATTEMTS));
+
+ if (testFailed) {
+ int exitValue = output.getExitValue();
+ if (needRetry) {
+ System.err.println("Test FAILURE on " + name + " reason: run out of retries to to pick free ports");
+ } else if (exitValue == COMMUNICATION_ERROR_EXIT_VAL) {
+ // Failure case since the java processes should still be
+ // running.
+ System.err.println("Test FAILURE on " + name);
+ } else if (exitValue == STOP_PROCESS_EXIT_VAL) {
+ System.out.println("Test FAILURE on " + name + " reason: The expected line \"" + READY_MSG
+ + "\" is not present in the process output");
+ } else {
+ System.err.println("Test FAILURE on " + name + " reason: Unexpected exit code => " + exitValue);
+ }
+ output.reportDiagnosticSummary();
+ }
+ latch.countDown();
+ }
+
+ public boolean isTestFailed() {
+ return testFailed;
+ }
+
+ private int getJMXPort() {
+ return useSSL ?
+ getRandomPortInRange(JMX_PORT_RANGE_LOWER_SSL, JMX_PORT_RANGE_UPPER_SSL) :
+ getRandomPortInRange(JMX_PORT_RANGE_LOWER, JMX_PORT_RANGE_UPPER);
+ }
+
+ private Process createTestProcess() {
+ int jmxPort = getJMXPort();
+ int rmiPort = jmxPort + 1;
+ String msg = String.format("DEBUG: Launching java tester for triplet (HOSTNAME,JMX_PORT,RMI_PORT)" +
+ " == (%s,%d,%d)", address, jmxPort, rmiPort);
+ System.out.println(msg);
+ List<String> args = new ArrayList<>();
+ args.add("-classpath");
+ args.add(TEST_CLASSPATH);
+ args.add("-Dcom.sun.management.jmxremote.host=" + address);
+ args.add("-Dcom.sun.management.jmxremote.port=" + jmxPort);
+ args.add("-Dcom.sun.management.jmxremote.rmi.port=" + rmiPort);
+ args.add("-Dcom.sun.management.jmxremote.authenticate=false");
+ args.add("-Dcom.sun.management.jmxremote.ssl=" + Boolean.toString(useSSL));
+ // This is needed for testing on loopback
+ args.add("-Djava.rmi.server.hostname=" + address);
+ if (useSSL) {
+ args.add("-Dcom.sun.management.jmxremote.registry.ssl=true");
+ args.add("-Djavax.net.ssl.keyStore=" + KEYSTORE_LOC);
+ args.add("-Djavax.net.ssl.trustStore=" + TRUSTSTORE_LOC);
+ args.add("-Djavax.net.ssl.keyStorePassword=password");
+ args.add("-Djavax.net.ssl.trustStorePassword=trustword");
+ }
+ args.add(TEST_CLASS);
+ args.add(address);
+ args.add(Integer.toString(jmxPort));
+ args.add(Integer.toString(rmiPort));
+ args.add(Boolean.toString(useSSL));
+
+ try {
+ ProcessBuilder builder = ProcessTools.createJavaProcessBuilder(args.toArray(new String[]{}));
+ System.out.println(ProcessTools.getCommandLine(builder));
+ Process process = builder.start();
+ output = new OutputAnalyzer(process);
+ return process;
+ } catch (Exception e) {
+ throw new RuntimeException("Test failed", e);
+ }
+ }
+
+ // Returns true if the test failed due to "Port already in use" error.
+ private boolean runTest() {
+ testFailed = true;
+ Process process = createTestProcess();
+ try {
+ sendMessageToProcess(process, "Exit: " + STOP_PROCESS_EXIT_VAL);
+ process.waitFor();
+ } catch (Throwable e) {
+ System.err.println("Failed to stop process: " + name);
+ throw new RuntimeException("Test failed", e);
+ }
+ if (output.getExitValue() == STOP_PROCESS_EXIT_VAL && output.getStdout().contains(READY_MSG)) {
+ testFailed = false;
+ } else if (output.getStderr().contains("Port already in use")) {
+ System.out.println("The test attempt for the test " + name +" failed due to the bind error");
+ // Need to retry
+ return true;
+ }
+ return false;
+ }
+
+ private static void sendMessageToProcess(Process process, String message) {
+ try (PrintWriter pw = new PrintWriter(process.getOutputStream())) {
+ pw.println(message);
+ pw.flush();
+ }
+ }
+ }
}
Binary file test/jdk/sun/misc/ClassLoaderUtil/test.jar has changed
--- a/test/jdk/sun/security/krb5/auto/KDC.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sun/security/krb5/auto/KDC.java Thu Jul 25 08:24:01 2019 -0400
@@ -808,8 +808,10 @@
PrincipalName cname = null;
boolean allowForwardable = true;
-
+ boolean isReferral = false;
if (body.kdcOptions.get(KDCOptions.CANONICALIZE)) {
+ System.out.println(realm + "> verifying referral for " +
+ body.sname.getNameString());
KDC referral = aliasReferrals.get(body.sname.getNameString());
if (referral != null) {
service = new PrincipalName(
@@ -817,6 +819,9 @@
PrincipalName.NAME_COMPONENT_SEPARATOR_STR +
referral.getRealm(), PrincipalName.KRB_NT_SRV_INST,
this.getRealm());
+ System.out.println(realm + "> referral to " +
+ referral.getRealm());
+ isReferral = true;
}
}
@@ -918,7 +923,8 @@
if (body.kdcOptions.get(KDCOptions.ALLOW_POSTDATE)) {
bFlags[Krb5.TKT_OPTS_MAY_POSTDATE] = true;
}
- if (body.kdcOptions.get(KDCOptions.CNAME_IN_ADDL_TKT)) {
+ if (body.kdcOptions.get(KDCOptions.CNAME_IN_ADDL_TKT) &&
+ !isReferral) {
if (!options.containsKey(Option.ALLOW_S4U2PROXY)) {
// Don't understand CNAME_IN_ADDL_TKT
throw new KrbException(Krb5.KDC_ERR_BADOPTION);
@@ -1074,8 +1080,7 @@
}
int eType = eTypes[0];
- if (body.kdcOptions.get(KDCOptions.CANONICALIZE) &&
- body.cname.getNameType() == PrincipalName.KRB_NT_ENTERPRISE) {
+ if (body.kdcOptions.get(KDCOptions.CANONICALIZE)) {
PrincipalName principal = alias2Principals.get(
body.cname.getNameString());
if (principal != null) {
--- a/test/jdk/sun/security/krb5/auto/ReferralsTest.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sun/security/krb5/auto/ReferralsTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -30,9 +30,18 @@
*/
import java.io.File;
-import sun.security.krb5.Credentials;
-import sun.security.krb5.internal.CredentialsUtil;
-import sun.security.krb5.KrbAsReqBuilder;
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.security.auth.kerberos.KerberosTicket;
+import javax.security.auth.Subject;
+
+import org.ietf.jgss.GSSName;
+
+import sun.security.jgss.GSSUtil;
import sun.security.krb5.PrincipalName;
public class ReferralsTest {
@@ -41,39 +50,32 @@
private static final String realmKDC1 = "RABBIT.HOLE";
private static final String realmKDC2 = "DEV.RABBIT.HOLE";
private static final char[] password = "123qwe@Z".toCharArray();
+
+ // Names
private static final String clientName = "test";
-
- private static final String clientAlias = clientName +
- PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1;
-
- private static final String clientKDC1QueryName = clientAlias.replaceAll(
- PrincipalName.NAME_REALM_SEPARATOR_STR, "\\\\" +
- PrincipalName.NAME_REALM_SEPARATOR_STR) +
- PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1;
- private static PrincipalName clientKDC1QueryPrincipal = null;
- static {
- try {
- clientKDC1QueryPrincipal = new PrincipalName(
- clientKDC1QueryName, PrincipalName.KRB_NT_ENTERPRISE,
- null);
- } catch (Throwable t) {}
- }
-
- private static final String clientKDC2Name = clientName +
- PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC2;
-
private static final String serviceName = "http" +
PrincipalName.NAME_COMPONENT_SEPARATOR_STR +
"server.dev.rabbit.hole";
- private static Credentials tgt;
- private static Credentials tgs;
+ // Alias
+ private static final String clientAlias = clientName +
+ PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1;
+
+ // Names + realms
+ private static final String clientKDC1Name = clientAlias.replaceAll(
+ PrincipalName.NAME_REALM_SEPARATOR_STR, "\\\\" +
+ PrincipalName.NAME_REALM_SEPARATOR_STR) +
+ PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1;
+ private static final String clientKDC2Name = clientName +
+ PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC2;
+ private static final String serviceKDC2Name = serviceName +
+ PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC2;
public static void main(String[] args) throws Exception {
try {
initializeKDCs();
- getTGT();
- getTGS();
+ testSubjectCredentials();
+ testDelegated();
} finally {
cleanup();
}
@@ -108,6 +110,11 @@
kdc1.registerAlias(serviceName, kdc2);
kdc2.registerAlias(clientAlias, clientKDC2Name);
+ Map<String,List<String>> mapKDC2 = new HashMap<>();
+ mapKDC2.put(serviceName + "@" + realmKDC2, Arrays.asList(
+ new String[]{serviceName + "@" + realmKDC2}));
+ kdc2.setOption(KDC.Option.ALLOW_S4U2PROXY, mapKDC2);
+
KDC.saveConfig(krbConfigName, kdc1, kdc2,
"forwardable=true");
System.setProperty("java.security.krb5.conf", krbConfigName);
@@ -120,50 +127,123 @@
}
}
- private static void getTGT() throws Exception {
- KrbAsReqBuilder builder = new KrbAsReqBuilder(clientKDC1QueryPrincipal,
- password);
- tgt = builder.action().getCreds();
- builder.destroy();
+ /*
+ * The client subject (whose principal is
+ * test@RABBIT.HOLE@RABBIT.HOLE) will obtain a TGT after
+ * realm referral and name canonicalization (TGT cname
+ * will be test@DEV.RABBIT.HOLE). With this TGT, the client will request
+ * a TGS for service http/server.dev.rabbit.hole@RABBIT.HOLE. After
+ * realm referral, a http/server.dev.rabbit.hole@DEV.RABBIT.HOLE TGS
+ * will be obtained.
+ *
+ * Assert that we get the proper TGT and TGS tickets, and that they are
+ * associated to the client subject.
+ *
+ * Assert that if we request a TGS for the same service again (based on the
+ * original service name), we don't get a new one but the previous,
+ * already in the subject credentials.
+ */
+ private static void testSubjectCredentials() throws Exception {
+ Subject clientSubject = new Subject();
+ Context clientContext = Context.fromUserPass(clientSubject,
+ clientKDC1Name, password, false);
+
+ Set<Principal> clientPrincipals = clientSubject.getPrincipals();
+ if (clientPrincipals.size() != 1) {
+ throw new Exception("Only one client subject principal expected");
+ }
+ Principal clientPrincipal = clientPrincipals.iterator().next();
if (DEBUG) {
- System.out.println("TGT");
- System.out.println("----------------------");
- System.out.println(tgt);
- System.out.println("----------------------");
+ System.out.println("Client subject principal: " +
+ clientPrincipal.getName());
+ }
+ if (!clientPrincipal.getName().equals(clientKDC1Name)) {
+ throw new Exception("Unexpected client subject principal.");
}
- if (tgt == null) {
- throw new Exception("TGT is null");
- }
- if (!tgt.getClient().getName().equals(clientKDC2Name)) {
- throw new Exception("Unexpected TGT client");
+
+ clientContext.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID);
+ clientContext.take(new byte[0]);
+ Set<KerberosTicket> clientTickets =
+ clientSubject.getPrivateCredentials(KerberosTicket.class);
+ boolean tgtFound = false;
+ boolean tgsFound = false;
+ for (KerberosTicket clientTicket : clientTickets) {
+ String cname = clientTicket.getClient().getName();
+ String sname = clientTicket.getServer().getName();
+ if (cname.equals(clientKDC2Name)) {
+ if (sname.equals(PrincipalName.TGS_DEFAULT_SRV_NAME +
+ PrincipalName.NAME_COMPONENT_SEPARATOR_STR +
+ realmKDC2 + PrincipalName.NAME_REALM_SEPARATOR_STR +
+ realmKDC2)) {
+ tgtFound = true;
+ } else if (sname.equals(serviceKDC2Name)) {
+ tgsFound = true;
+ }
+ }
+ if (DEBUG) {
+ System.out.println("Client subject KerberosTicket:");
+ System.out.println(clientTicket);
+ }
}
- String[] tgtServerNames = tgt.getServer().getNameStrings();
- if (tgtServerNames.length != 2 || !tgtServerNames[0].equals(
- PrincipalName.TGS_DEFAULT_SRV_NAME) ||
- !tgtServerNames[1].equals(realmKDC2) ||
- !tgt.getServer().getRealmString().equals(realmKDC2)) {
- throw new Exception("Unexpected TGT server");
+ if (!tgtFound || !tgsFound) {
+ throw new Exception("client subject tickets (TGT/TGS) not found.");
+ }
+ int numOfTickets = clientTickets.size();
+ clientContext.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID);
+ clientContext.take(new byte[0]);
+ clientContext.status();
+ int newNumOfTickets =
+ clientSubject.getPrivateCredentials(KerberosTicket.class).size();
+ if (DEBUG) {
+ System.out.println("client subject number of tickets: " +
+ numOfTickets);
+ System.out.println("client subject new number of tickets: " +
+ newNumOfTickets);
+ }
+ if (numOfTickets != newNumOfTickets) {
+ throw new Exception("Useless client subject TGS request because" +
+ " TGS was not found in private credentials.");
}
}
- private static void getTGS() throws Exception {
- tgs = CredentialsUtil.acquireServiceCreds(serviceName +
- PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1, tgt);
- if (DEBUG) {
- System.out.println("TGS");
- System.out.println("----------------------");
- System.out.println(tgs);
- System.out.println("----------------------");
+ /*
+ * The server (http/server.dev.rabbit.hole@DEV.RABBIT.HOLE)
+ * will authenticate on itself on behalf of the client
+ * (test@DEV.RABBIT.HOLE). Cross-realm referrals will occur
+ * when requesting different TGTs and TGSs (including the
+ * request for delegated credentials).
+ */
+ private static void testDelegated() throws Exception {
+ Context c = Context.fromUserPass(clientKDC2Name,
+ password, false);
+ c.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID);
+ Context s = Context.fromUserPass(serviceKDC2Name,
+ password, true);
+ s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
+ Context.handshake(c, s);
+ Context delegatedContext = s.delegated();
+ delegatedContext.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID);
+ delegatedContext.x().requestMutualAuth(false);
+ Context s2 = Context.fromUserPass(serviceKDC2Name,
+ password, true);
+ s2.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
+
+ // Test authentication
+ Context.handshake(delegatedContext, s2);
+ if (!delegatedContext.x().isEstablished() || !s2.x().isEstablished()) {
+ throw new Exception("Delegated authentication failed");
}
- if (tgs == null) {
- throw new Exception("TGS is null");
+
+ // Test identities
+ GSSName contextInitiatorName = delegatedContext.x().getSrcName();
+ GSSName contextAcceptorName = delegatedContext.x().getTargName();
+ if (DEBUG) {
+ System.out.println("Context initiator: " + contextInitiatorName);
+ System.out.println("Context acceptor: " + contextAcceptorName);
}
- if (!tgs.getClient().getName().equals(clientKDC2Name)) {
- throw new Exception("Unexpected TGS client");
- }
- if (!tgs.getServer().getNameString().equals(serviceName) ||
- !tgs.getServer().getRealmString().equals(realmKDC2)) {
- throw new Exception("Unexpected TGS server");
+ if (!contextInitiatorName.toString().equals(clientKDC2Name) ||
+ !contextAcceptorName.toString().equals(serviceName)) {
+ throw new Exception("Unexpected initiator or acceptor names");
}
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/SSLSessionImpl/ResumptionUpdateBoundValues.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/*
+ * @test
+ * @library /test/lib
+ * @summary Test that a New Session Ticket will be generated when a
+ * SSLSessionBindingListener is set (boundValues)
+ * @run main/othervm ResumptionUpdateBoundValues
+ */
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.ArrayBlockingQueue;
+
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionBindingEvent;
+import javax.net.ssl.SSLSessionBindingListener;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.Utils;
+
+public class ResumptionUpdateBoundValues {
+
+ static boolean separateServerThread = true;
+
+ /*
+ * Where do we find the keystores?
+ */
+ static String pathToStores = "../../../../javax/net/ssl/etc/";
+ static String keyStoreFile = "keystore";
+ static String trustStoreFile = "truststore";
+ static String passwd = "passphrase";
+
+ /*
+ * Is the server ready to serve?
+ */
+ volatile static boolean serverReady = false;
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+ SSLServerSocketFactory sslssf =
+ (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
+ SSLServerSocket sslServerSocket =
+ (SSLServerSocket) sslssf.createServerSocket(serverPort);
+ serverPort = sslServerSocket.getLocalPort();
+
+ /*
+ * Signal Client, we're ready for his connect.
+ */
+ serverReady = true;
+
+ while (serverReady) {
+ SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslIS.read();
+ sslOS.write(85);
+ sslOS.flush();
+ SSLSession sslSession = sslSocket.getSession();
+ SBListener sbListener = new SBListener(sslSession);
+ sslSession.putValue("x", sbListener);
+
+ sslIS.read();
+ sslOS.write(85);
+ sslOS.flush();
+
+ sslSocket.close();
+ }
+ }
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ SBListener doClientSide() throws Exception {
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory) SSLSocketFactory.getDefault();
+
+ try {
+ SSLSocket sslSocket = (SSLSocket)
+ sslsf.createSocket("localhost", serverPort);
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslOS.write(280);
+ sslOS.flush();
+ sslIS.read();
+
+ SSLSession sslSession = sslSocket.getSession();
+ System.out.printf(" sslSession: %s %n %s%n", sslSession, sslSession.getClass());
+ SBListener sbListener = new SBListener(sslSession);
+
+ sslOS.write(280);
+ sslOS.flush();
+ sslIS.read();
+
+ sslOS.write(280);
+ sslOS.flush();
+ sslIS.read();
+
+ sslOS.close();
+ sslIS.close();
+ sslSocket.close();
+
+ sslOS = null;
+ sslIS = null;
+ sslSession = null;
+ sslSocket = null;
+ Reference.reachabilityFence(sslOS);
+ Reference.reachabilityFence(sslIS);
+ Reference.reachabilityFence(sslSession);
+ Reference.reachabilityFence(sslSocket);
+
+ return sbListener;
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ throw ex;
+ }
+ }
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+ public static void main(String[] args) throws Exception {
+
+ if (args.length == 0) {
+ System.setProperty("test.java.opts",
+ "-Dtest.src=" + System.getProperty("test.src") +
+ " -Dtest.jdk=" + System.getProperty("test.jdk") +
+ " -Djavax.net.debug=ssl,handshake");
+
+ System.out.println("test.java.opts: " +
+ System.getProperty("test.java.opts"));
+
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true,
+ Utils.addTestJavaOpts("ResumptionUpdateBoundValues", "p"));
+
+ OutputAnalyzer output = ProcessTools.executeProcess(pb);
+ try {
+ output.shouldContain("trigger new session ticket");
+ System.out.println("Found NST in debugging");
+ } catch (Exception e) {
+ throw e;
+ } finally {
+ System.out.println("-- BEGIN Stdout:");
+ System.out.println(output.getStdout());
+ System.out.println("-- END Stdout");
+ System.out.println("-- BEGIN Stderr:");
+ System.out.println(output.getStderr());
+ System.out.println("-- END Stderr");
+ }
+ return;
+ }
+
+ String keyFilename =
+ System.getProperty("test.src", "./") + "/" + pathToStores +
+ "/" + keyStoreFile;
+ String trustFilename =
+ System.getProperty("test.src", "./") + "/" + pathToStores +
+ "/" + trustStoreFile;
+ System.setProperty("javax.net.ssl.keyStore", keyFilename);
+ System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+ System.setProperty("javax.net.ssl.trustStore", trustFilename);
+ System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+ if (debug)
+ System.setProperty("javax.net.debug", "all");
+
+ /*
+ * Start the tests.
+ */
+
+ new ResumptionUpdateBoundValues();
+ }
+
+ ArrayBlockingQueue<Thread> threads = new ArrayBlockingQueue<Thread>(100);
+
+ ArrayBlockingQueue<SBListener> sbListeners = new ArrayBlockingQueue<>(100);
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ ResumptionUpdateBoundValues() throws Exception {
+ final int count = 1;
+ if (separateServerThread) {
+ startServer(true);
+ startClients(true, count);
+ } else {
+ startClients(true, count);
+ startServer(true);
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ Thread t;
+ while ((t = threads.take()) != Thread.currentThread()) {
+ System.out.printf(" joining: %s%n", t);
+ t.join(1000L);
+ }
+ serverReady = false;
+ System.gc();
+ System.gc();
+
+
+ SBListener listener = null;
+ while ((listener = sbListeners.poll()) != null) {
+ if (!listener.check()) {
+ System.out.printf(" sbListener not called on finalize: %s%n",
+ listener);
+ }
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ *
+ * If the main thread excepted, that propagates back
+ * immediately. If the other thread threw an exception, we
+ * should report back.
+ */
+ if (serverException != null) {
+ System.out.print("Server Exception:");
+ throw serverException;
+ }
+ if (clientException != null) {
+ System.out.print("Client Exception:");
+ throw clientException;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ Thread t = new Thread("Server") {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died..." + e);
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ threads.add(t);
+ t.setDaemon(true);
+ t.start();
+ } else {
+ doServerSide();
+ }
+ }
+
+ void startClients(boolean newThread, int count) throws Exception {
+ for (int i = 0; i < count; i++) {
+ System.out.printf(" newClient: %d%n", i);
+ startClient(newThread);
+ }
+ serverReady = false;
+
+ threads.add(Thread.currentThread()); // add ourselves at the 'end'
+ }
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ Thread t = new Thread("Client") {
+ public void run() {
+ try {
+ sbListeners.add(doClientSide());
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died..." + e);
+ clientException = e;
+ }
+ }
+ };
+ System.out.printf(" starting: %s%n", t);
+ threads.add(t);
+ t.start();
+ } else {
+ sbListeners.add(doClientSide());
+ }
+ }
+
+
+ static class SBListener implements SSLSessionBindingListener {
+ private volatile int unboundNotified;
+ private final WeakReference<SSLSession> session;
+
+ SBListener(SSLSession session) {
+ this.unboundNotified = 0;
+ this.session = new WeakReference<SSLSession>(session);
+ }
+
+ boolean check() {
+ System.out.printf(" check: %s%n", this);
+ return unboundNotified > 0 && session.get() == null;
+ }
+
+ @Override
+ public void valueBound(SSLSessionBindingEvent event) {
+ System.out.printf(" valueBound: %s%n", event.getName());
+ }
+
+ @Override
+ public void valueUnbound(SSLSessionBindingEvent event) {
+ System.out.printf(" valueUnbound: %s%n", event.getName());
+ unboundNotified++;
+ }
+
+ public String toString() {
+ return "count: " + unboundNotified +
+ ", ref: " + session.get();
+ }
+ }
+}
+
--- a/test/jdk/sun/security/tools/jarsigner/DiffEnd.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sun/security/tools/jarsigner/DiffEnd.java Thu Jul 25 08:24:01 2019 -0400
@@ -23,10 +23,14 @@
/*
* @test
- * @bug 6948909
+ * @bug 6948909 8217375
* @summary Jarsigner removes MANIFEST.MF info for badly packages jar's
* @library /test/lib
*/
+/*
+ * See also InsufficientSectionDelimiter.java for similar tests including cases
+ * without or with different line breaks.
+ */
import jdk.test.lib.Asserts;
import jdk.test.lib.SecurityTools;
@@ -44,47 +48,47 @@
public class DiffEnd {
static void check() throws Exception {
- SecurityTools.jarsigner("-keystore "
- + Path.of(System.getProperty("test.src"), "JarSigning.keystore")
- .toString()
- + " -storepass bbbbbb -digestalg SHA1"
- + " -signedjar diffend.new.jar diffend.jar c");
+ String ksArgs = "-keystore " + Path.of(System.getProperty("test.src"))
+ .resolve("JarSigning.keystore") + " -storepass bbbbbb";
- try (JarFile jf = new JarFile("diffend.new.jar")) {
+ SecurityTools.jarsigner(ksArgs + " -digestalg SHA1 "
+ + "-signedjar diffend.signed.jar diffend.jar c")
+ .shouldHaveExitValue(0);
+ SecurityTools.jarsigner(" -verify " + ksArgs + " -verbose "
+ + "diffend.signed.jar c")
+ .stdoutShouldMatch("^smk .* 1$").shouldHaveExitValue(0);
+
+ try (JarFile jf = new JarFile("diffend.signed.jar")) {
Asserts.assertTrue(jf.getManifest().getMainAttributes()
.containsKey(new Attributes.Name("Today")));
}
}
public static void main(String[] args) throws Exception {
-
// A MANIFEST.MF using \n as newlines and no double newlines at the end
- byte[] manifest =
- ("Manifest-Version: 1.0\n"
+ byte[] manifest = ("Manifest-Version: 1.0\n"
+ "Created-By: 1.7.0-internal (Sun Microsystems Inc.)\n"
+ "Today: Monday\n").getBytes(StandardCharsets.UTF_8);
+ // Without the fake .RSA file, to trigger the if (wasSigned) else block
+ try (FileOutputStream fos = new FileOutputStream("diffend.jar");
+ ZipOutputStream zos = new ZipOutputStream(fos)) {
+ zos.putNextEntry(new ZipEntry(JarFile.MANIFEST_NAME));
+ zos.write(manifest);
+ zos.putNextEntry(new ZipEntry("1"));
+ zos.write(new byte[10]);
+ }
+ check();
+
// With the fake .RSA file, to trigger the if (wasSigned) block
try (FileOutputStream fos = new FileOutputStream("diffend.jar");
ZipOutputStream zos = new ZipOutputStream(fos)) {
- zos.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF"));
+ zos.putNextEntry(new ZipEntry(JarFile.MANIFEST_NAME));
zos.write(manifest);
- zos.putNextEntry(new ZipEntry("META-INF/x.RSA"));
+ zos.putNextEntry(new ZipEntry("META-INF/x.RSA")); // fake .RSA
zos.putNextEntry(new ZipEntry("1"));
zos.write(new byte[10]);
}
-
- check();
-
- // Without the fake .RSA file, to trigger the else block
- try (FileOutputStream fos = new FileOutputStream("diffend.jar");
- ZipOutputStream zos = new ZipOutputStream(fos)) {
- zos.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF"));
- zos.write(manifest);
- zos.putNextEntry(new ZipEntry("1"));
- zos.write(new byte[10]);
- }
-
check();
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/tools/jarsigner/DigestDontIgnoreCase.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.JarEntry;
+import jdk.test.lib.SecurityTools;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * @test
+ * @bug 8217375
+ * @library /test/lib
+ * @run testng DigestDontIgnoreCase
+ * @summary Check that existing manifest digest entries are taken for valid
+ * only if they match the actual digest value also taking upper and lower
+ * case of the base64 encoded form of the digests into account.
+ */
+/*
+ * <pre>mfDigest.equalsIgnoreCase(base64Digests[i])</pre>
+ * previously in JarSigner.java on on line 985
+ * @see jdk.security.jarsigner.JarSigner#updateDigests
+ */
+public class DigestDontIgnoreCase {
+
+ static final String KEYSTORE_FILENAME = "test.jks";
+
+ static final String DUMMY_FILE1 = "dummy1.txt";
+ static final byte[] DUMMY_CONTENTS1 = DUMMY_FILE1.getBytes(UTF_8);
+ static final String DUMMY_FILE2 = "dummy2.txt";
+ static final byte[] DUMMY_CONTENTS2 = DUMMY_FILE2.getBytes(UTF_8);
+
+ byte[] goodSignedManifest;
+
+ @BeforeClass
+ public void prepareCertificate() throws Exception {
+ SecurityTools.keytool("-genkeypair -keyalg DSA -keystore "
+ + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit"
+ + " -alias a -dname CN=X").shouldHaveExitValue(0);
+ }
+
+ void prepareJarFile(String filename, Map<String, byte[]> contents)
+ throws IOException {
+ try (OutputStream out = Files.newOutputStream(Path.of(filename));
+ JarOutputStream jos = new JarOutputStream(out)) {
+ for (Map.Entry<String, byte[]> entry : contents.entrySet()) {
+ JarEntry je = new JarEntry(entry.getKey());
+ jos.putNextEntry(je);
+ jos.write(entry.getValue());
+ jos.closeEntry();
+ }
+ }
+ }
+
+ @BeforeClass(dependsOnMethods = "prepareCertificate")
+ public void prepareGoodSignedManifest() throws Exception {
+ String filename = "prepare.jar";
+ prepareJarFile(filename, Map.of(DUMMY_FILE1, DUMMY_CONTENTS1));
+ SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -verbose -debug " + filename + " a")
+ .shouldHaveExitValue(0);
+ goodSignedManifest = Utils.readJarManifestBytes(filename);
+ Utils.echoManifest(goodSignedManifest,
+ "reference manifest with one file signed");
+ }
+
+ void testWithManifest(String filename, byte[] manifestBytes)
+ throws Exception {
+ Utils.echoManifest(manifestBytes,
+ "going to test " + filename + " with manifest");
+ prepareJarFile(filename, Map.of(
+ JarFile.MANIFEST_NAME, manifestBytes,
+ DUMMY_FILE1, DUMMY_CONTENTS1, // with digest already in manifest
+ DUMMY_FILE2, DUMMY_CONTENTS2)); // causes manifest update
+ Utils.echoManifest(Utils.readJarManifestBytes(filename),
+ filename + " created with manifest");
+ SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -debug -verbose " + filename + " a")
+ .shouldHaveExitValue(0);
+ Utils.echoManifest(Utils.readJarManifestBytes(filename),
+ filename + " signed resulting in manifest");
+ SecurityTools.jarsigner("-verify -strict -keystore " +
+ KEYSTORE_FILENAME + " -storepass changeit -debug -verbose " +
+ filename + " a").shouldHaveExitValue(0);
+ }
+
+ @Test
+ public void verifyDigestGoodCase() throws Exception {
+ testWithManifest("good.jar", goodSignedManifest);
+ }
+
+ @Test
+ public void testDigestHeaderNameCase() throws Exception {
+ byte[] mfBadHeader = new String(goodSignedManifest, UTF_8).
+ replace("SHA-256-Digest", "sha-256-dIGEST").getBytes(UTF_8);
+ testWithManifest("switch-header-name-case.jar", mfBadHeader);
+ }
+
+ @Test
+ public void testDigestWrongCase() throws Exception {
+ byte[] mfBadDigest = switchCase(goodSignedManifest, "Digest");
+ testWithManifest("switch-digest-case.jar", mfBadDigest);
+ }
+
+ byte[] switchCase(byte[] manifest, String attrName) {
+ byte[] wrongCase = Arrays.copyOf(manifest, manifest.length);
+ byte[] name = (attrName + ":").getBytes(UTF_8);
+ int matched = 0; // number of bytes before position i matching attrName
+ for (int i = 0; i < wrongCase.length; i++) {
+ if (wrongCase[i] == '\r' &&
+ (i == wrongCase.length - 1 || wrongCase[i + 1] == '\n')) {
+ continue;
+ } else if ((wrongCase[i] == '\r' || wrongCase[i] == '\n')
+ && (i == wrongCase.length - 1 || wrongCase[i + 1] != ' ')) {
+ matched = 0;
+ } else if (matched == name.length) {
+ wrongCase[i] = switchCase(wrongCase[i]);
+ } else if (name[matched] == wrongCase[i]) {
+ matched++;
+ } else {
+ matched = 0;
+ }
+ }
+ return wrongCase;
+ }
+
+ byte switchCase(byte c) {
+ if (c >= 'A' && c <= 'Z') {
+ return (byte) ('a' + (c - 'A'));
+ } else if (c >= 'a' && c <= 'z') {
+ return (byte) ('A' + (c - 'a'));
+ } else {
+ return c;
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/tools/jarsigner/EmptyIndividualSectionName.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.lang.reflect.Method;
+import java.nio.file.Path;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.jar.Attributes;
+import java.util.jar.Attributes.Name;
+
+import jdk.test.lib.util.JarUtils;
+import jdk.test.lib.SecurityTools;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @bug 8217375
+ * @library /test/lib
+ * @modules java.base/java.util.jar:+open
+ * @run testng/othervm EmptyIndividualSectionName
+ * @summary Check that an individual section with an empty name is digested
+ * and signed.
+ * <p>
+ * See also
+ * jdk/test/jdk/sun/security/util/ManifestDigester/FindSections.java
+ * for much more detailed api level tests
+ */
+public class EmptyIndividualSectionName {
+
+ static final String KEYSTORE_FILENAME = "test.jks";
+
+ @BeforeClass
+ public void prepareCertificate() throws Exception {
+ SecurityTools.keytool("-genkeypair -keyalg EC -keystore "
+ + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit "
+ + "-alias a -dname CN=X").shouldHaveExitValue(0);
+ }
+
+ /**
+ * Adds an additional section with name {@code sectionName} to the manifest
+ * of a JAR before signing it with {@code signOpts}.
+ * @return signature file {@code META-INF/A.SF} for further assertions
+ */
+ Manifest test(String sectionName, String signOpts) throws Exception {
+ Manifest mf = new Manifest();
+ mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
+ mf.getEntries().put(sectionName, new Attributes());
+ String jarFilename = "test" + sectionName +
+ (signOpts != null ? signOpts : "") + ".jar";
+ JarUtils.createJarFile(Path.of(jarFilename), mf, Path.of("."));
+ SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -verbose -debug " +
+ (signOpts != null ? signOpts + " " : "") + jarFilename + " a")
+ .shouldHaveExitValue(0);
+ SecurityTools.jarsigner("-verify -keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -debug -verbose " + jarFilename + " a")
+ .shouldHaveExitValue(0);
+
+ byte[] mfBytes = Utils.readJarManifestBytes(jarFilename);
+ Utils.echoManifest(mfBytes, "manifest");
+ mf = new Manifest(new ByteArrayInputStream(mfBytes));
+ assertNotNull(mf.getAttributes(sectionName));
+ byte[] sfBytes = Utils.readJarEntryBytes(jarFilename, "META-INF/A.SF");
+ Utils.echoManifest(sfBytes, "signature file META-INF/A.SF");
+ return new Manifest(new ByteArrayInputStream(sfBytes));
+ }
+
+ /**
+ * Verifies that it makes a difference if the name is empty or not
+ * by running the same test as {@link #testNameEmpty} with only a different
+ * section name.
+ */
+ @Test
+ public void testNameNotEmpty() throws Exception {
+ String sectionName = "X";
+ assertNotNull(test(sectionName, null).getAttributes(sectionName));
+ }
+
+ /**
+ * Verifies that individual sections are digested and signed also if the
+ * name of such a section is empty.
+ * An empty name of an individual section cannot be tested by adding a file
+ * with an empty name to a JAR because such a file name is invalid and
+ * cannot be used to add a file because it cannot be created or added to
+ * the JAR file in the first place. However, an individual section with an
+ * empty name can be added to the manifest.
+ * Expected is a corresponding digest in the signature file which was not
+ * present or produced before resolution of bug 8217375.
+ */
+ @Test
+ public void testNameEmpty() throws Exception {
+ String sectionName = "";
+ assertNotNull(test(sectionName, null).getAttributes(sectionName));
+ }
+
+ /**
+ * Similar to {@link #testNameEmpty} but tries to show a real difference
+ * rather than just some internals in a {@code .SF} file, but TODO
+ */
+ @Test(enabled = false, description = "TODO")
+ public void testNameEmptyTrusted() throws Exception {
+ String sectionName = "";
+ test(sectionName, "-sectionsonly");
+ String jarFilename = "test" + sectionName + "-sectionsonly.jar";
+ try (JarFile jar = new JarFile(jarFilename, true)) {
+ Manifest m = jar.getManifest();
+ Method getTrustedAttributes = m.getClass()
+ .getDeclaredMethod("getTrustedAttributes", String.class);
+ getTrustedAttributes.setAccessible(true);
+ assertThrows(SecurityException.class, () ->
+ getTrustedAttributes.invoke(m, sectionName));
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/tools/jarsigner/EmptyJar.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import java.nio.file.Path;
+import java.util.jar.Manifest;
+import java.util.jar.Attributes.Name;
+
+import jdk.test.lib.util.JarUtils;
+import jdk.test.lib.SecurityTools;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @bug 8217375
+ * @modules java.base/sun.security.util
+ * @library /test/lib /lib/testlibrary
+ * @run testng EmptyJar
+ * @summary Checks that signing an empty jar file does not result in an NPE or
+ * other error condition.
+ */
+public class EmptyJar {
+
+ static final String KEYSTORE_FILENAME = "test.jks";
+
+ @BeforeClass
+ public void prepareKeyStore() throws Exception {
+ SecurityTools.keytool("-genkeypair -keyalg EC -keystore "
+ + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit"
+ + " -alias a -dname CN=A").shouldHaveExitValue(0);
+ }
+
+ @Test
+ public void test() throws Exception {
+ String jarFilename = "test.jar";
+ JarUtils.createJarFile(Path.of(jarFilename), (Manifest) null,
+ Path.of("."));
+ SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -verbose -debug " + jarFilename + " a")
+ .shouldHaveExitValue(0);
+
+ // verify that jarsigner has added a default manifest
+ byte[] mfBytes = Utils.readJarManifestBytes(jarFilename);
+ Utils.echoManifest(mfBytes, "manifest");
+ assertTrue(new String(mfBytes, UTF_8).startsWith(
+ Name.MANIFEST_VERSION + ": 1.0\r\nCreated-By: "));
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/tools/jarsigner/FindHeaderEndVsManifestDigesterFindFirstSection.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.List;
+import sun.security.util.ManifestDigester;
+
+import org.testng.annotations.Test;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Factory;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @bug 8217375
+ * @modules java.base/sun.security.util
+ * @run testng FindHeaderEndVsManifestDigesterFindFirstSection
+ * @summary Checks that {@link JarSigner#findHeaderEnd} (moved to now
+ * {@link #findHeaderEnd} in this test) can be replaced with
+ * {@link ManifestDigester#findSection}
+ * (first invocation will identify main attributes)
+ * without making a difference.
+ */
+/*
+ * Note to future maintainer:
+ * While it might look at first glance like this test ensures backwards-
+ * compatibility between JarSigner.findHeaderEnd and
+ * ManifestDigester.findSection's first invocation that find the main
+ * attributes section, at the time of that change, this test continues to
+ * verify main attributes digestion now with ManifestDigester.findSection as
+ * opposed to previous implementation in JarSigner.findHeaderEnd.
+ * Before completely removing this test, make sure that main attributes
+ * digestion is covered appropriately with tests. After JarSigner.findHeaderEnd
+ * has been removed digests should still continue to match.
+ *
+ * See also
+ * - jdk/test/jdk/sun/security/tools/jarsigner/PreserveRawManifestEntryAndDigest.java
+ * for some end-to-end tests utilizing the jarsigner tool,
+ * - jdk/test/jdk/sun/security/util/ManifestDigester/FindSection.java and
+ * - jdk/test/jdk/sun/security/util/ManifestDigester/DigestInput.java
+ * for much more detailed tests at api level
+ *
+ * Both test mentioned above, however, originally were created when removing
+ * confusion of "Manifest-Main-Attributes" individual section with actual main
+ * attributes whereas the test here is about changes related to raw manifest
+ * reproduction and in the end test pretty much the same behavior.
+ */
+public class FindHeaderEndVsManifestDigesterFindFirstSection {
+
+ static final boolean FIXED_8217375 = true; // FIXME
+
+ /**
+ * from former {@link JarSigner#findHeaderEnd}, subject to verification if
+ * it can be replaced with {@link ManifestDigester#findSection}
+ */
+ @SuppressWarnings("fallthrough")
+ private int findHeaderEnd(byte[] bs) {
+ // Initial state true to deal with empty header
+ boolean newline = true; // just met a newline
+ int len = bs.length;
+ for (int i = 0; i < len; i++) {
+ switch (bs[i]) {
+ case '\r':
+ if (i < len - 1 && bs[i + 1] == '\n') i++;
+ // fallthrough
+ case '\n':
+ if (newline) return i + 1; //+1 to get length
+ newline = true;
+ break;
+ default:
+ newline = false;
+ }
+ }
+ // If header end is not found, it means the MANIFEST.MF has only
+ // the main attributes section and it does not end with 2 newlines.
+ // Returns the whole length so that it can be completely replaced.
+ return len;
+ }
+
+ @DataProvider(name = "parameters")
+ public static Object[][] parameters() {
+ List<Object[]> tests = new ArrayList<>();
+ for (String lineBreak : new String[] { "\n", "\r", "\r\n" }) {
+ if ("\r".equals(lineBreak) && !FIXED_8217375) continue;
+ for (int numLBs = 0; numLBs <= 3; numLBs++) {
+ for (String addSection : new String[] { null, "Ignore" }) {
+ tests.add(new Object[] { lineBreak, numLBs, addSection });
+ }
+ }
+ }
+ return tests.toArray(new Object[tests.size()][]);
+ }
+
+ @Factory(dataProvider = "parameters")
+ public static Object[] createTests(String lineBreak, int numLineBreaks,
+ String individualSectionName) {
+ return new Object[]{new FindHeaderEndVsManifestDigesterFindFirstSection(
+ lineBreak, numLineBreaks, individualSectionName
+ )};
+ }
+
+ final String lineBreak;
+ final int numLineBreaks; // number of line breaks after main attributes
+ final String individualSectionName; // null means only main attributes
+ final byte[] rawBytes;
+
+ FindHeaderEndVsManifestDigesterFindFirstSection(String lineBreak,
+ int numLineBreaks, String individualSectionName) {
+ this.lineBreak = lineBreak;
+ this.numLineBreaks = numLineBreaks;
+ this.individualSectionName = individualSectionName;
+
+ rawBytes = (
+ "oldStyle: trailing space " + lineBreak +
+ "newStyle: no trailing space" + lineBreak.repeat(numLineBreaks) +
+ // numLineBreaks < 2 will not properly delimit individual section
+ // but it does not hurt to test that anyway
+ (individualSectionName != null ?
+ "Name: " + individualSectionName + lineBreak +
+ "Ignore: nothing here" + lineBreak +
+ lineBreak
+ : "")
+ ).getBytes(UTF_8);
+ }
+
+ @BeforeMethod
+ public void verbose() {
+ System.out.println("lineBreak = " + stringToIntList(lineBreak));
+ System.out.println("numLineBreaks = " + numLineBreaks);
+ System.out.println("individualSectionName = " + individualSectionName);
+ }
+
+ @FunctionalInterface
+ interface Callable {
+ void call() throws Exception;
+ }
+
+ void catchNoLineBreakAfterMainAttributes(Callable test) throws Exception {
+ // manifests cannot be parsed and digested if the main attributes do
+ // not end in a blank line (double line break) or one line break
+ // immediately before eof.
+ boolean failureExpected = numLineBreaks == 0
+ && individualSectionName == null;
+ try {
+ test.call();
+ if (failureExpected) fail("expected an exception");
+ } catch (NullPointerException | IllegalStateException e) {
+ if (!failureExpected) fail("unexpected " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Checks that the beginning of the manifest until position<ol>
+ * <li>{@code Jarsigner.findHeaderEnd} in the previous version
+ * and</li>
+ * <li>{@code ManifestDigester.getMainAttsEntry().sections[0].
+ * lengthWithBlankLine} in the new version</li>
+ * </ol>produce the same offset (TODO: or the same error).
+ * The beginning of the manifest until that offset (range
+ * <pre>0 .. (offset - 1)</pre>) will be reproduced if the manifest has
+ * not changed.
+ * <p>
+ * Getting {@code startOfNext} of {@link ManifestDigester#findSection}'s
+ * first invokation returned {@link ManifestDigester.Position} which
+ * identifies the end offset of the main attributes is difficulted by
+ * {@link ManifestDigester#findSection} being private and therefore not
+ * directly accessible.
+ */
+ @Test
+ public void startOfNextLengthWithBlankLine() throws Exception {
+ catchNoLineBreakAfterMainAttributes(() ->
+ assertEquals(lengthWithBlankLine(), findHeaderEnd(rawBytes))
+ );
+ }
+
+ /**
+ * Due to its private visibility,
+ * {@link ManifestDigester.Section#lengthWithBlankLine} is not directly
+ * accessible. However, calling {@link ManifestDigester.Entry#digest}
+ * reveals {@code lengthWithBlankLine} as third parameter in
+ * <pre>md.update(sec.rawBytes, sec.offset, sec.lengthWithBlankLine);</pre>
+ * on line ManifestDigester.java:212.
+ * <p>
+ * This value is the same as {@code startOfNext} of
+ * {@link ManifestDigester#findSection}'s first invocation returned
+ * {@link ManifestDigester.Position} identifying the end offset of the
+ * main attributes because<ol>
+ * <li>the end offset of the main attributes is assigned to
+ * {@code startOfNext} in
+ * <pre>pos.startOfNext = i+1;</pre> in ManifestDigester.java:98</li>
+ * <li>which is then passed on as the third parameter to the constructor
+ * of a new {@link ManifestDigester.Section#Section} by
+ * <pre>new Section(0, pos.endOfSection + 1, pos.startOfNext, rawBytes)));</pre>
+ * in in ManifestDigester.java:128</li>
+ * <li>where it is assigned to
+ * {@link ManifestDigester.Section#lengthWithBlankLine} by
+ * <pre>this.lengthWithBlankLine = lengthWithBlankLine;</pre>
+ * in ManifestDigester.java:241</li>
+ * <li>from where it is picked up by {@link ManifestDigester.Entry#digest}
+ * in
+ * <pre>md.update(sec.rawBytes, sec.offset, sec.lengthWithBlankLine);</pre>
+ * in ManifestDigester.java:212</li>
+ * </ol>
+ * all of which without any modification.
+ */
+ int lengthWithBlankLine() {
+ int[] lengthWithBlankLine = new int[] { 0 };
+ new ManifestDigester(rawBytes).get(ManifestDigester.MF_MAIN_ATTRS,
+ false).digest(new MessageDigest("lengthWithBlankLine") {
+ @Override protected void engineReset() {
+ lengthWithBlankLine[0] = 0;
+ }
+ @Override protected void engineUpdate(byte b) {
+ lengthWithBlankLine[0]++;
+ }
+ @Override protected void engineUpdate(byte[] b, int o, int l) {
+ lengthWithBlankLine[0] += l;
+ }
+ @Override protected byte[] engineDigest() {
+ return null;
+ }
+ });
+ return lengthWithBlankLine[0];
+ }
+
+ /**
+ * Checks that the replacement of {@link JarSigner#findHeaderEnd} is
+ * actually used to reproduce manifest main attributes.
+ * <p>
+ * {@link #startOfNextLengthWithBlankLine} demonstrates that
+ * {@link JarSigner#findHeaderEnd} has been replaced successfully with
+ * {@link ManifestDigester#findSection} but does not also show that the
+ * main attributes are reproduced with the same offset as before.
+ * {@link #startOfNextLengthWithBlankLine} uses
+ * {@link ManifestDigester.Entry#digest} to demonstrate an equal offset
+ * calculated but {@link ManifestDigester.Entry#digest} is not necessarily
+ * the same as reproducing, especially when considering
+ * {@link ManifestDigester.Entry#oldStyle}.
+ */
+ @Test(enabled = FIXED_8217375)
+ public void reproduceMainAttributes() throws Exception {
+ catchNoLineBreakAfterMainAttributes(() -> {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ ManifestDigester md = new ManifestDigester(rawBytes);
+ // without 8217375 fixed the following line will not even compile
+ // so just remove it and skip the test for regression
+ md.getMainAttsEntry().reproduceRaw(buf); // FIXME
+
+ assertEquals(buf.size(), findHeaderEnd(rawBytes));
+ });
+ }
+
+ static List<Integer> stringToIntList(String string) {
+ byte[] bytes = string.getBytes(UTF_8);
+ List<Integer> list = new ArrayList<>();
+ for (int i = 0; i < bytes.length; i++) {
+ list.add((int) bytes[i]);
+ }
+ return list;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/tools/jarsigner/InsufficientSectionDelimiter.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.stream.Stream;
+import java.util.jar.Attributes.Name;
+import java.util.jar.Manifest;
+import jdk.test.lib.util.JarUtils;
+import jdk.test.lib.SecurityTools;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Factory;
+import org.testng.annotations.Test;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * @test
+ * @bug 8217375
+ * @library /test/lib
+ * @run testng InsufficientSectionDelimiter
+ * @summary Checks some cases signing a jar the manifest of which has no or
+ * only one line break at the end and no proper delimiting blank line does not
+ * result in an invalid signed jar without jarsigner noticing and failing.
+ *
+ * <p>See also<ul>
+ * <li>{@link PreserveRawManifestEntryAndDigest} with an update of a signed
+ * jar with a different signer whereas this test just signs with one signer
+ * </li>
+ * <li>{@link WasSignedByOtherSigner} for a test that detects if
+ * {@code wasSigned} in {@link jdk.security.jarsigner.JarSigner#sign0} was set
+ * correctly determining whether or not to re-write the manifest, and</li>
+ * <li>{@code diffend.sh} for another similar test</li></ul>
+ */
+public class InsufficientSectionDelimiter {
+
+ static final String KEYSTORE_FILENAME = "test.jks";
+
+ @BeforeTest
+ public void prepareCertificate() throws Exception {
+ SecurityTools.keytool("-genkeypair -keyalg EC -keystore "
+ + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit"
+ + " -alias a -dname CN=A").shouldHaveExitValue(0);
+ }
+
+ @BeforeTest
+ public void prepareFakeSfFile() throws IOException {
+ new File("META-INF").mkdir();
+ Files.write(Path.of("META-INF/.SF"), (
+ Name.SIGNATURE_VERSION + ": 1.0\r\n" +
+ "-Digest-Manifest: \r\n\r\n").getBytes(UTF_8));
+ }
+
+ @DataProvider(name = "parameters")
+ public static Object[][] parameters() {
+ return new String[][] { { "" }, { "\n" }, { "\r" }, { "\r\n" } };
+ }
+
+ @Factory(dataProvider = "parameters")
+ public static Object[] createTests(String lineBreak) {
+ return new Object[] { new InsufficientSectionDelimiter(lineBreak) };
+ }
+
+ final String lineBreak;
+ final String jarFilenameSuffix;
+
+ InsufficientSectionDelimiter(String lineBreak) {
+ this.lineBreak = lineBreak;
+ jarFilenameSuffix = Utils.escapeStringWithNumbers(lineBreak);
+ }
+
+ @BeforeMethod
+ public void verbose() {
+ System.out.println("lineBreak = "
+ + Utils.escapeStringWithNumbers(lineBreak));
+ }
+
+ void test(String jarFilenamePrefix, String... files) throws Exception {
+ String jarFilename = jarFilenamePrefix + jarFilenameSuffix + ".jar";
+ JarUtils.createJarFile(Path.of(jarFilename), new Manifest() {
+ @Override public void write(OutputStream out) throws IOException {
+ out.write((Name.MANIFEST_VERSION + ": 1.0" +
+ lineBreak).getBytes(UTF_8));
+ }
+ }, Path.of("."), Stream.of(files).map(Path::of).toArray(Path[]::new)
+ );
+ Utils.echoManifest(Utils.readJarManifestBytes(
+ jarFilename), "unsigned jar");
+ try {
+ SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -verbose -debug " + jarFilename +
+ " a").shouldHaveExitValue(0);
+ Utils.echoManifest(Utils.readJarManifestBytes(
+ jarFilename), "signed jar");
+ } catch (Exception e) {
+ if (lineBreak.isEmpty()) {
+ return; // invalid manifest without trailing line break
+ }
+ throw e;
+ }
+
+ // remove META-INF/.SF from signed jar which would not validate
+ // (not added in all the test cases)
+ JarUtils.updateJar(jarFilename, "verify-" + jarFilename,
+ Map.of("META-INF/.SF", false));
+ SecurityTools.jarsigner("-verify -strict -keystore " +
+ KEYSTORE_FILENAME + " -storepass changeit -debug -verbose " +
+ "verify-" + jarFilename + " a").shouldHaveExitValue(0);
+ }
+
+ /**
+ * Test that signing a jar which has never been signed yet and contains
+ * no signature related files with a manifest that ends immediately after
+ * the last main attributes value byte or only one line break and no blank
+ * line produces a valid signed jar or an error if the manifest ends
+ * without line break.
+ */
+ @Test
+ public void testOnlyMainAttrs() throws Exception {
+ test("testOnlyMainAttrs");
+ }
+
+ /**
+ * Test that signing a jar with a manifest that ends immediately after
+ * the last main attributes value byte or with too few line break
+ * characters to properly delimit an individual section and has a fake
+ * signing related file to trigger a signature update or more specifically
+ * wasSigned in JarSigner.sign0 to become true produces a valid signed jar
+ * or an error if the manifest ends without line break.
+ * <p>
+ * Only one line break and hence no blank line ('\r', '\n', or '\r\n')
+ * after last main attributes value byte is too little to delimit an
+ * individual section to hold a file's digest but acceptable if no
+ * individual section has to be added because no contained file has to be
+ * signed as is the case in this test.
+ *
+ * @see #testMainAttrsWasSignedAddFile
+ */
+ @Test
+ public void testMainAttrsWasSigned() throws Exception {
+ test("testMainAttrsWasSigned", "META-INF/.SF");
+ }
+
+ /**
+ * Test that signing a jar with a manifest that ends immediately after
+ * the last main attributes value byte or with too few line break
+ * characters to properly delimit an individual section and has a fake
+ * signing related file to trigger a signature update or more specifically
+ * wasSigned in JarSigner.sign0 to become true produces no invalid signed
+ * jar or an error if the manifest ends without line break.
+ * <p>
+ * Only one line break and hence no blank line ('\r', '\n', or '\r\n')
+ * after the last main attributes value byte is too little to delimit an
+ * individual section which would be required here to save the digest of a
+ * contained file to be signed.
+ * <p>
+ * Changing the delimiters after the main attributes changes the main
+ * attributes digest but
+ * {@link SignatureFileVerifier#verifyManifestMainAttrs} and
+ * {@link ManifestDigester#digestWorkaround} work around it.
+ */
+ @Test
+ public void testMainAttrsWasSignedAddFile() throws Exception {
+ Files.write(Path.of("test.txt"), "test.txt".getBytes(UTF_8));
+ test("testMainAttrsWasSignedAddFile", "META-INF/.SF", "test.txt");
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/tools/jarsigner/MainAttributesConfused.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.jar.Attributes;
+import java.util.jar.Attributes.Name;
+import sun.security.util.ManifestDigester;
+import jdk.test.lib.util.JarUtils;
+import jdk.test.lib.SecurityTools;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @bug 8217375
+ * @modules java.base/sun.security.util
+ * @library /test/lib /lib/testlibrary
+ * @run testng MainAttributesConfused
+ * @summary Check that manifest individual section "Manifest-Main-Attributes"
+ * does not interfere and is not confused with ManifestDigester internals.
+ *
+ * See also
+ * jdk/test/jdk/sun/security/util/ManifestDigester/ManifestMainAttributes.java
+ * for much more detailed api level tests
+ */
+public class MainAttributesConfused {
+
+ static final String KEYSTORE_FILENAME = "test.jks";
+ static final String MAIN_ATTRIBUTES_MARKER = null;
+
+ @BeforeClass
+ void prepareKeyStore() throws Exception {
+ SecurityTools.keytool("-genkeypair -keyalg EC -keystore "
+ + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit"
+ + " -alias a -dname CN=X").shouldHaveExitValue(0);
+ }
+
+ void testAddManifestSection(String sectionName) throws Exception {
+ // create a signed jar
+ Manifest manifest = new Manifest();
+ manifest.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
+ String testFile = "test-" + sectionName;
+ Files.write(Path.of(testFile), testFile.getBytes(UTF_8));
+ String jarFilename = sectionName + ".jar";
+ JarUtils.createJarFile(Path.of(jarFilename), manifest,
+ Path.of("."), Path.of(testFile));
+ SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -verbose -debug " + jarFilename + " a")
+ .shouldHaveExitValue(0);
+
+ // get the manifest of the signed jar with the signature digests, add
+ // a new individual section, and write it back
+ try (JarFile jar = new JarFile(jarFilename)) {
+ manifest = jar.getManifest();
+ }
+ Attributes attrs = sectionName == MAIN_ATTRIBUTES_MARKER
+ ? manifest.getMainAttributes()
+ : manifest.getEntries().computeIfAbsent(sectionName,
+ n -> new Attributes());
+ attrs.put(new Name("Some-Key"), "Some-Value");
+ String jarFilenameAttrs = sectionName + "-attrs.jar";
+ JarUtils.updateManifest(jarFilename, jarFilenameAttrs, manifest);
+
+ // having just added another manifest entry (individual section) not
+ // modifying existing digests or main attributes should not invalidate
+ // the existing signature.
+ SecurityTools.jarsigner("-verify -keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -debug -verbose " + jarFilenameAttrs +
+ " a").shouldHaveExitValue(0);
+ }
+
+ @Test
+ public void testAddOtherThanManifestMainAttributes() throws Exception {
+ // any value but "Manifest-Main-Attributes", even lower case works
+ testAddManifestSection("manifest-main-attributes");
+ }
+
+ @Test
+ public void testAddMainAttributesHeader() throws Exception {
+ // adding or changing existing attributes of the main section, however,
+ // will invalidate the signature
+ assertThrows(() -> testAddManifestSection(MAIN_ATTRIBUTES_MARKER));
+ }
+
+ @Test
+ public void testAddManifestMainAttributesSection() throws Exception {
+ testAddManifestSection(ManifestDigester.MF_MAIN_ATTRS);
+ }
+
+}
--- a/test/jdk/sun/security/tools/jarsigner/OldSig.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sun/security/tools/jarsigner/OldSig.java Thu Jul 25 08:24:01 2019 -0400
@@ -23,10 +23,15 @@
/*
* @test
- * @bug 6543940 6868865
+ * @bug 6543940 6868865 8217375
* @summary Exception thrown when signing a jarfile in java 1.5
* @library /test/lib
*/
+/*
+ * See also PreserveRawManifestEntryAndDigest.java for tests with arbitrarily
+ * formatted individual sections in addition the the main attributes tested
+ * here.
+ */
import jdk.test.lib.SecurityTools;
import jdk.test.lib.util.JarUtils;
@@ -44,8 +49,11 @@
JarUtils.updateJarFile(Path.of("B.jar"), Path.of("."),
Path.of("B.class"));
- SecurityTools.jarsigner("-keystore " + src.resolve("JarSigning.keystore")
- + " -storepass bbbbbb -digestalg SHA1 B.jar c");
- SecurityTools.jarsigner("-verify B.jar");
+ String ksArgs = "-keystore " + src.resolve("JarSigning.keystore")
+ + " -storepass bbbbbb";
+ SecurityTools.jarsigner(ksArgs + " -digestalg SHA1 B.jar c");
+ SecurityTools.jarsigner("-verify B.jar").shouldHaveExitValue(0);
+ SecurityTools.jarsigner("-verify " + ksArgs + " -verbose B.jar c")
+ .stdoutShouldMatch("^smk .* B[.]class$").shouldHaveExitValue(0);
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/tools/jarsigner/PreserveRawManifestEntryAndDigest.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,1016 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Collections;
+import java.util.stream.Collectors;
+import java.util.function.Function;
+import java.util.jar.Attributes;
+import java.util.jar.Attributes.Name;
+import java.util.jar.Manifest;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipEntry;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.Platform;
+import jdk.test.lib.SecurityTools;
+import jdk.test.lib.util.JarUtils;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @bug 8217375
+ * @library /test/lib
+ * @modules jdk.jartool/sun.security.tools.jarsigner
+ * @run testng/timeout=1200 PreserveRawManifestEntryAndDigest
+ * @summary Verifies that JarSigner does not change manifest file entries
+ * in a binary view if its decoded map view does not change so that an
+ * unchanged (individual section) entry continues to produce the same digest.
+ * The same manifest (in terms of {@link Manifest#equals}) could be encoded
+ * with different line breaks ("{@code \r}", "{@code \n}", or "{@code \r\n}")
+ * or with arbitrary line break positions (as is also the case with the change
+ * of the default line width in JDK 11, bug 6372077) resulting in a different
+ * digest for manifest entries with identical values.
+ *
+ * <p>See also:<ul>
+ * <li>{@code oldsig.sh} and {@code diffend.sh} in
+ * {@code /test/jdk/sun/security/tools/jarsigner/}</li>
+ * <li>{@code Compatibility.java} in
+ * {@code /test/jdk/sun/security/tools/jarsigner/compatibility}</li>
+ * <li>{@link ReproduceRaw} testing relevant
+ * {@sun.security.util.ManifestDigester} api in much more detail</li>
+ * </ul>
+ */
+/*
+ * debug with "run testng" += "/othervm -Djava.security.debug=jar"
+ */
+public class PreserveRawManifestEntryAndDigest {
+
+ static final String KEYSTORE_FILENAME = "test.jks";
+ static final String FILENAME_INITIAL_CONTENTS = "initial-contents";
+ static final String FILENAME_UPDATED_CONTENTS = "updated-contents";
+
+ /**
+ * @see sun.security.tools.jarsigner.Main#run
+ */
+ static final int NOTSIGNEDBYALIASORALIASNOTINSTORE = 32;
+
+ @BeforeTest
+ public void prepareContentFiles() throws IOException {
+ Files.write(Path.of(FILENAME_INITIAL_CONTENTS),
+ FILENAME_INITIAL_CONTENTS.getBytes(UTF_8));
+ Files.write(Path.of(FILENAME_UPDATED_CONTENTS),
+ FILENAME_UPDATED_CONTENTS.getBytes(UTF_8));
+ }
+
+ @BeforeTest
+ public void prepareCertificates() throws Exception {
+ SecurityTools.keytool("-genkeypair -keyalg DSA -keystore "
+ + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit"
+ + " -alias a -dname CN=A").shouldHaveExitValue(0);
+ SecurityTools.keytool("-genkeypair -keyalg DSA -keystore "
+ + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit"
+ + " -alias b -dname CN=B").shouldHaveExitValue(0);
+ }
+
+ static class TeeOutputStream extends FilterOutputStream {
+ final OutputStream tee; // don't flush or close
+
+ public TeeOutputStream(OutputStream out, OutputStream tee) {
+ super(out);
+ this.tee = tee;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ super.write(b);
+ tee.write(b);
+ }
+ }
+
+ /**
+ * runs jarsigner in its own child process and captures exit code and the
+ * output of stdout and stderr, as opposed to {@link #karsignerMain}
+ */
+ OutputAnalyzer jarsignerProc(String args) throws Exception {
+ long start = System.currentTimeMillis();
+ try {
+ return SecurityTools.jarsigner(args);
+ } finally {
+ long end = System.currentTimeMillis();
+ System.out.println("jarsignerProc duration [ms]: " + (end - start));
+ }
+ }
+
+ /**
+ * assume non-zero exit code would call System.exit but is faster than
+ * {@link #jarsignerProc}
+ */
+ void jarsignerMain(String args) throws Exception {
+ long start = System.currentTimeMillis();
+ try {
+ new sun.security.tools.jarsigner.Main().run(args.split("\\s+"));
+ } finally {
+ long end = System.currentTimeMillis();
+ System.out.println("jarsignerMain duration [ms]: " + (end - start));
+ }
+ }
+
+ void createSignedJarA(String jarFilename, Manifest manifest,
+ String additionalJarsignerOptions, String dummyContentsFilename)
+ throws Exception {
+ JarUtils.createJarFile(Path.of(jarFilename), manifest, Path.of("."),
+ dummyContentsFilename == null ? new Path[]{} :
+ new Path[] { Path.of(dummyContentsFilename) });
+ jarsignerMain("-keystore " + KEYSTORE_FILENAME + " -storepass changeit"
+ + (additionalJarsignerOptions == null ? "" :
+ " " + additionalJarsignerOptions) +
+ " -verbose -debug " + jarFilename + " a");
+ Utils.echoManifest(Utils.readJarManifestBytes(
+ jarFilename), "original signed jar by signer a");
+ // check assumption that jar is valid at this point
+ jarsignerMain("-verify -keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -verbose -debug " + jarFilename + " a");
+ }
+
+ void manipulateManifestSignAgainA(String srcJarFilename, String tmpFilename,
+ String dstJarFilename, String additionalJarsignerOptions,
+ Function<Manifest, byte[]> manifestManipulation) throws Exception {
+ Manifest mf;
+ try (JarFile jar = new JarFile(srcJarFilename)) {
+ mf = jar.getManifest();
+ }
+ byte[] manipulatedManifest = manifestManipulation.apply(mf);
+ Utils.echoManifest(manipulatedManifest, "manipulated manifest");
+ JarUtils.updateJar(srcJarFilename, tmpFilename, Map.of(
+ JarFile.MANIFEST_NAME, manipulatedManifest,
+ // add a fake sig-related file to trigger wasSigned in JarSigner
+ "META-INF/.SF", Name.SIGNATURE_VERSION + ": 1.0\r\n"));
+ jarsignerMain("-keystore " + KEYSTORE_FILENAME + " -storepass changeit"
+ + (additionalJarsignerOptions == null ? "" :
+ " " + additionalJarsignerOptions) +
+ " -verbose -debug " + tmpFilename + " a");
+ // remove META-INF/.SF from signed jar again which would not validate
+ JarUtils.updateJar(tmpFilename, dstJarFilename,
+ Map.of("META-INF/.SF", false));
+
+ Utils.echoManifest(Utils.readJarManifestBytes(
+ dstJarFilename), "manipulated jar signed again with a");
+ // check assumption that jar is valid at this point
+ jarsignerMain("-verify -keystore " + KEYSTORE_FILENAME + " " +
+ "-storepass changeit -verbose -debug " + dstJarFilename + " a");
+ }
+
+ OutputAnalyzer signB(String jarFilename, String additionalJarsignerOptions,
+ int updateExitCodeVerifyA) throws Exception {
+ jarsignerMain("-keystore " + KEYSTORE_FILENAME + " -storepass changeit"
+ + (additionalJarsignerOptions == null ? "" :
+ " " + additionalJarsignerOptions)
+ + " -verbose -debug " + jarFilename + " b");
+ Utils.echoManifest(Utils.readJarManifestBytes(
+ jarFilename), "signed again with signer b");
+ // check assumption that jar is valid at this point with any alias
+ jarsignerMain("-verify -strict -keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -debug -verbose " + jarFilename);
+ // check assumption that jar is valid at this point with b just signed
+ jarsignerMain("-verify -strict -keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -debug -verbose " + jarFilename + " b");
+ // return result of verification of signature by a before update
+ return jarsignerProc("-verify -strict " + "-keystore " +
+ KEYSTORE_FILENAME + " -storepass changeit " + "-debug " +
+ "-verbose " + jarFilename + " a")
+ .shouldHaveExitValue(updateExitCodeVerifyA);
+ }
+
+ String[] fromFirstToSecondEmptyLine(String[] lines) {
+ int from = 0;
+ for (int i = 0; i < lines.length; i++) {
+ if ("".equals(lines[i])) {
+ from = i + 1;
+ break;
+ }
+ }
+
+ int to = lines.length - 1;
+ for (int i = from; i < lines.length; i++) {
+ if ("".equals(lines[i])) {
+ to = i - 1;
+ break;
+ }
+ }
+
+ return Arrays.copyOfRange(lines, from, to + 1);
+ }
+
+ /**
+ * @see "concise_jarsigner.sh"
+ */
+ String[] getExpectedJarSignerOutputUpdatedContentNotValidatedBySignerA(
+ String jarFilename, String digestalg,
+ String firstAddedFilename, String secondAddedFilename) {
+ final String TS = ".{28,29}"; // matches a timestamp
+ List<String> expLines = new ArrayList<>();
+ expLines.add("s k *\\d+ " + TS + " META-INF/MANIFEST[.]MF");
+ expLines.add(" *\\d+ " + TS + " META-INF/B[.]SF");
+ expLines.add(" *\\d+ " + TS + " META-INF/B[.]DSA");
+ expLines.add(" *\\d+ " + TS + " META-INF/A[.]SF");
+ expLines.add(" *\\d+ " + TS + " META-INF/A[.]DSA");
+ if (firstAddedFilename != null) {
+ expLines.add("smk *\\d+ " + TS + " " + firstAddedFilename);
+ }
+ if (secondAddedFilename != null) {
+ expLines.add("smkX *\\d+ " + TS + " " + secondAddedFilename);
+ }
+ return expLines.toArray(new String[expLines.size()]);
+ }
+
+ void assertMatchByLines(String[] actLines, String[] expLines) {
+ for (int i = 0; i < actLines.length && i < expLines.length; i++) {
+ String actLine = actLines[i];
+ String expLine = expLines[i];
+ assertTrue(actLine.matches("^" + expLine + "$"),
+ "\"" + actLine + "\" should have matched \"" + expLine + "\"");
+ }
+ assertEquals(actLines.length, expLines.length);
+ }
+
+ String test(String name, Function<Manifest, byte[]> mm) throws Exception {
+ return test(name, FILENAME_INITIAL_CONTENTS, FILENAME_UPDATED_CONTENTS,
+ mm);
+ }
+
+ String test(String name,
+ String firstAddedFilename, String secondAddedFilename,
+ Function<Manifest, byte[]> mm) throws Exception {
+ return test(name, firstAddedFilename, secondAddedFilename, mm, null,
+ true, true);
+ }
+
+ /**
+ * Essentially, creates a first signed JAR file with a single contained
+ * file or without and a manipulation applied to its manifest signed by
+ * signer a and then signes it again with a different signer b.
+ * The jar file is signed twice with signer a in order to make the digests
+ * available to the manipulation function that might use it.
+ *
+ * @param name Prefix for the JAR filenames used throughout the test.
+ * @param firstAddedFilename Name of a file to add before the first
+ * signature by signer a or null. The name will also become the contents
+ * if not null.
+ * @param secondAddedFilename Name of a file to add after the first
+ * signature by signer a and before the second signature by signer b or
+ * null. The name will also become the contents if not null.
+ * @param manifestManipulation A callback hook to manipulate the manifest
+ * after the first signature by signer a and before the second signature by
+ * signer b.
+ * @param digestalg The digest algorithm name to be used or null for
+ * default.
+ * @param assertMainAttrsDigestsUnchanged Assert that the
+ * manifest main attributes digests have not changed. In any case the test
+ * also checks that the digests are still valid whether changed or not
+ * by {@code jarsigner -verify} which might use
+ * {@link ManifestDigester.Entry#digestWorkaround}
+ * @param assertFirstAddedFileDigestsUnchanged Assert that the
+ * digest of the file firstAddedFilename has not changed with the second
+ * signature. In any case the test checks that the digests are valid whether
+ * changed or not by {@code jarsigner -verify} which might use
+ * {@link ManifestDigester.Entry#digestWorkaround}
+ * @return The name of the resulting JAR file that has passed the common
+ * assertions ready for further examination
+ */
+ String test(String name,
+ String firstAddedFilename, String secondAddedFilename,
+ Function<Manifest, byte[]> manifestManipulation,
+ String digestalg, boolean assertMainAttrsDigestsUnchanged,
+ boolean assertFirstAddedFileDigestsUnchanged)
+ throws Exception {
+ String digOpts = (digestalg != null ? "-digestalg " + digestalg : "");
+ String jarFilename1 = "test-" + name + "-step1.jar";
+ createSignedJarA(jarFilename1,
+ /* no manifest will let jarsigner create a default one */ null,
+ digOpts, firstAddedFilename);
+
+ // manipulate the manifest, write it back, and sign the jar again with
+ // the same certificate a as before overwriting the first signature
+ String jarFilename2 = "test-" + name + "-step2.jar";
+ String jarFilename3 = "test-" + name + "-step3.jar";
+ manipulateManifestSignAgainA(jarFilename1, jarFilename2, jarFilename3,
+ digOpts, manifestManipulation);
+
+ // add another file, sign it with the other certificate, and verify it
+ String jarFilename4 = "test-" + name + "-step4.jar";
+ JarUtils.updateJar(jarFilename3, jarFilename4,
+ secondAddedFilename != null ?
+ Map.of(secondAddedFilename, secondAddedFilename)
+ : Collections.EMPTY_MAP);
+ OutputAnalyzer o = signB(jarFilename4, digOpts,
+ secondAddedFilename != null ? NOTSIGNEDBYALIASORALIASNOTINSTORE : 0);
+ // check that secondAddedFilename is the only entry which is not signed
+ // by signer with alias "a" unless secondAddedFilename is null
+ assertMatchByLines(
+ fromFirstToSecondEmptyLine(o.getStdout().split("\\R")),
+ getExpectedJarSignerOutputUpdatedContentNotValidatedBySignerA(
+ jarFilename4, digestalg,
+ firstAddedFilename, secondAddedFilename));
+
+ // double-check reading the files with a verifying JarFile
+ try (JarFile jar = new JarFile(jarFilename4, true)) {
+ if (firstAddedFilename != null) {
+ JarEntry je1 = jar.getJarEntry(firstAddedFilename);
+ jar.getInputStream(je1).readAllBytes();
+ assertTrue(je1.getCodeSigners().length > 0);
+ }
+ if (secondAddedFilename != null) {
+ JarEntry je2 = jar.getJarEntry(secondAddedFilename);
+ jar.getInputStream(je2).readAllBytes();
+ assertTrue(je2.getCodeSigners().length > 0);
+ }
+ }
+
+ // assert that the signature of firstAddedFilename signed by signer
+ // with alias "a" is not lost and its digest remains the same
+ try (ZipFile zip = new ZipFile(jarFilename4)) {
+ ZipEntry ea = zip.getEntry("META-INF/A.SF");
+ Manifest sfa = new Manifest(zip.getInputStream(ea));
+ ZipEntry eb = zip.getEntry("META-INF/B.SF");
+ Manifest sfb = new Manifest(zip.getInputStream(eb));
+ if (assertMainAttrsDigestsUnchanged) {
+ String mainAttrsDigKey =
+ (digestalg != null ? digestalg : "SHA-256") +
+ "-Digest-Manifest-Main-Attributes";
+ assertEquals(sfa.getMainAttributes().getValue(mainAttrsDigKey),
+ sfb.getMainAttributes().getValue(mainAttrsDigKey));
+ }
+ if (assertFirstAddedFileDigestsUnchanged) {
+ assertEquals(sfa.getAttributes(firstAddedFilename),
+ sfb.getAttributes(firstAddedFilename));
+ }
+ }
+
+ return jarFilename4;
+ }
+
+ /**
+ * Test that signing a jar with manifest entries with arbitrary line break
+ * positions in individual section headers does not destroy an existing
+ * signature<ol>
+ * <li>create two self-signed certificates</li>
+ * <li>sign a jar with at least one non-META-INF file in it with a JDK
+ * before 11 or place line breaks not at 72 bytes in an individual section
+ * header</li>
+ * <li>add a new file to the jar</li>
+ * <li>sign the jar with a JDK 11, 12, or 13 with bug 8217375 not yet
+ * resolved with a different signer</li>
+ * </ol>→ first signature will not validate anymore even though it
+ * should.
+ */
+ @Test
+ public void arbitraryLineBreaksSectionName() throws Exception {
+ test("arbitraryLineBreaksSectionName", m -> {
+ return (
+ Name.MANIFEST_VERSION + ": 1.0\r\n" +
+ "Created-By: " +
+ m.getMainAttributes().getValue("Created-By") + "\r\n" +
+ "\r\n" +
+ "Name: Test\r\n" +
+ " -\r\n" +
+ " Section\r\n" +
+ "Key: Value \r\n" +
+ "\r\n" +
+ "Name: " + FILENAME_INITIAL_CONTENTS.substring(0, 1) + "\r\n" +
+ " " + FILENAME_INITIAL_CONTENTS.substring(1, 8) + "\r\n" +
+ " " + FILENAME_INITIAL_CONTENTS.substring(8) + "\r\n" +
+ "SHA-256-Digest: " + m.getAttributes(FILENAME_INITIAL_CONTENTS)
+ .getValue("SHA-256-Digest") + "\r\n" +
+ "\r\n"
+ ).getBytes(UTF_8);
+ });
+ }
+
+ /**
+ * Test that signing a jar with manifest entries with arbitrary line break
+ * positions in individual section headers does not destroy an existing
+ * signature<ol>
+ * <li>create two self-signed certificates</li>
+ * <li>sign a jar with at least one non-META-INF file in it with a JDK
+ * before 11 or place line breaks not at 72 bytes in an individual section
+ * header</li>
+ * <li>add a new file to the jar</li>
+ * <li>sign the jar with a JDK 11 or 12 with a different signer</li>
+ * </ol>→ first signature will not validate anymore even though it
+ * should.
+ */
+ @Test
+ public void arbitraryLineBreaksHeader() throws Exception {
+ test("arbitraryLineBreaksHeader", m -> {
+ String digest = m.getAttributes(FILENAME_INITIAL_CONTENTS)
+ .getValue("SHA-256-Digest");
+ return (
+ Name.MANIFEST_VERSION + ": 1.0\r\n" +
+ "Created-By: " +
+ m.getMainAttributes().getValue("Created-By") + "\r\n" +
+ "\r\n" +
+ "Name: Test-Section\r\n" +
+ "Key: Value \r\n" +
+ " with\r\n" +
+ " strange \r\n" +
+ " line breaks.\r\n" +
+ "\r\n" +
+ "Name: " + FILENAME_INITIAL_CONTENTS + "\r\n" +
+ "SHA-256-Digest: " + digest.substring(0, 11) + "\r\n" +
+ " " + digest.substring(11) + "\r\n" +
+ "\r\n"
+ ).getBytes(UTF_8);
+ });
+ }
+
+ /**
+ * Breaks {@code line} at 70 bytes even though the name says 72 but when
+ * also counting the line delimiter ("{@code \r\n}") the line totals to 72
+ * bytes.
+ * Borrowed from {@link Manifest#make72Safe} before JDK 11
+ *
+ * @see Manifest#make72Safe
+ */
+ static void make72Safe(StringBuffer line) {
+ int length = line.length();
+ if (length > 72) {
+ int index = 70;
+ while (index < length - 2) {
+ line.insert(index, "\r\n ");
+ index += 72;
+ length += 3;
+ }
+ }
+ return;
+ }
+
+ /**
+ * Test that signing a jar with manifest entries with line breaks at
+ * position where Manifest would not place them now anymore (72 instead of
+ * 70 bytes after line starts) does not destroy an existing signature<ol>
+ * <li>create two self-signed certificates</li>
+ * <li>simulate a manifest as it would have been written by a JDK before 11
+ * by re-positioning line breaks at 70 bytes (which makes a difference by
+ * digests that grow headers longer than 70 characters such as SHA-512 as
+ * opposed to default SHA-256, long file names, or manual editing)</li>
+ * <li>add a new file to the jar</li>
+ * <li>sign the jar with a JDK 11 or 12 with a different signer</li>
+ * </ol><p>→
+ * The first signature will not validate anymore even though it should.
+ */
+ public void lineWidth70(String name, String digestalg) throws Exception {
+ Files.write(Path.of(name), name.getBytes(UTF_8));
+ test(name, name, FILENAME_UPDATED_CONTENTS, m -> {
+ // force a line break with a header exceeding line width limit
+ m.getEntries().put("Test-Section", new Attributes());
+ m.getAttributes("Test-Section").put(
+ Name.IMPLEMENTATION_VERSION, "1" + "0".repeat(100));
+
+ StringBuilder sb = new StringBuilder();
+ StringBuffer[] buf = new StringBuffer[] { null };
+ manifestToString(m).lines().forEach(line -> {
+ if (line.startsWith(" ")) {
+ buf[0].append(line.substring(1));
+ } else {
+ if (buf[0] != null) {
+ make72Safe(buf[0]);
+ sb.append(buf[0].toString());
+ sb.append("\r\n");
+ }
+ buf[0] = new StringBuffer();
+ buf[0].append(line);
+ }
+ });
+ make72Safe(buf[0]);
+ sb.append(buf[0].toString());
+ sb.append("\r\n");
+ return sb.toString().getBytes(UTF_8);
+ }, digestalg, false, false);
+ }
+
+ @Test
+ public void lineWidth70Filename() throws Exception {
+ lineWidth70(
+ "lineWidth70".repeat(6) /* 73 chars total with "Name: " */, null);
+ }
+
+ @Test
+ public void lineWidth70Digest() throws Exception {
+ lineWidth70("lineWidth70digest", "SHA-512");
+ }
+
+ /**
+ * Test that signing a jar with a manifest with line delimiter other than
+ * "{@code \r\n}" does not destroy an existing signature<ol>
+ * <li>create two self-signed certificates</li>
+ * <li>sign a jar with at least one non-META-INF file in it</li>
+ * <li>extract the manifest, and change its line delimiters
+ * (for example dos2unix)</li>
+ * <li>update the jar with the updated manifest</li>
+ * <li>sign it again with the same signer as before</li>
+ * <li>add a new file to the jar</li>
+ * <li>sign the jar with a JDK before 13 with a different signer<li>
+ * </ol><p>→
+ * The first signature will not validate anymore even though it should.
+ */
+ public void lineBreak(String lineBreak) throws Exception {
+ test("lineBreak" + byteArrayToIntList(lineBreak.getBytes(UTF_8)).stream
+ ().map(i -> "" + i).collect(Collectors.joining("")), m -> {
+ StringBuilder sb = new StringBuilder();
+ manifestToString(m).lines().forEach(l -> {
+ sb.append(l);
+ sb.append(lineBreak);
+ });
+ return sb.toString().getBytes(UTF_8);
+ });
+ }
+
+ @Test
+ public void lineBreakCr() throws Exception {
+ lineBreak("\r");
+ }
+
+ @Test
+ public void lineBreakLf() throws Exception {
+ lineBreak("\n");
+ }
+
+ @Test
+ public void lineBreakCrLf() throws Exception {
+ lineBreak("\r\n");
+ }
+
+ @Test
+ public void testAdjacentRepeatedSection() throws Exception {
+ test("adjacent", m -> {
+ return (manifestToString(m) +
+ "Name: " + FILENAME_INITIAL_CONTENTS + "\r\n" +
+ "Foo: Bar\r\n" +
+ "\r\n"
+ ).getBytes(UTF_8);
+ });
+ }
+
+ @Test
+ public void testIntermittentRepeatedSection() throws Exception {
+ test("intermittent", m -> {
+ return (manifestToString(m) +
+ "Name: don't know\r\n" +
+ "Foo: Bar\r\n" +
+ "\r\n" +
+ "Name: " + FILENAME_INITIAL_CONTENTS + "\r\n" +
+ "Foo: Bar\r\n" +
+ "\r\n"
+ ).getBytes(UTF_8);
+ });
+ }
+
+ @Test
+ public void testNameImmediatelyContinued() throws Exception {
+ test("testNameImmediatelyContinued", m -> {
+ // places a continuation line break and space at the first allowed
+ // position after ": " and before the first character of the value
+ return (manifestToString(m).replaceAll(FILENAME_INITIAL_CONTENTS,
+ "\r\n " + FILENAME_INITIAL_CONTENTS + "\r\nFoo: Bar")
+ ).getBytes(UTF_8);
+ });
+ }
+
+ /*
+ * "malicious" '\r' after continuation line continued
+ */
+ @Test
+ public void testNameContinuedContinuedWithCr() throws Exception {
+ test("testNameContinuedContinuedWithCr", m -> {
+ return (manifestToString(m).replaceAll(FILENAME_INITIAL_CONTENTS,
+ FILENAME_INITIAL_CONTENTS.substring(0, 1) + "\r\n " +
+ FILENAME_INITIAL_CONTENTS.substring(1, 4) + "\r " +
+ FILENAME_INITIAL_CONTENTS.substring(4) + "\r\n" +
+ "Foo: Bar")
+ ).getBytes(UTF_8);
+ });
+ }
+
+ /*
+ * "malicious" '\r' after continued continuation line
+ */
+ @Test
+ public void testNameContinuedContinuedEndingWithCr() throws Exception {
+ test("testNameContinuedContinuedEndingWithCr", m -> {
+ return (manifestToString(m).replaceAll(FILENAME_INITIAL_CONTENTS,
+ FILENAME_INITIAL_CONTENTS.substring(0, 1) + "\r\n " +
+ FILENAME_INITIAL_CONTENTS.substring(1, 4) + "\r\n " +
+ FILENAME_INITIAL_CONTENTS.substring(4) + "\r" + // no '\n'
+ "Foo: Bar")
+ ).getBytes(UTF_8);
+ });
+ }
+
+ @DataProvider(name = "trailingSeqParams", parallel = true)
+ public static Object[][] trailingSeqParams() {
+ return new Object[][] {
+ {""},
+ {"\r"},
+ {"\n"},
+ {"\r\n"},
+ {"\r\r"},
+ {"\n\n"},
+ {"\n\r"},
+ {"\r\r\r"},
+ {"\r\r\n"},
+ {"\r\n\r"},
+ {"\r\n\n"},
+ {"\n\r\r"},
+ {"\n\r\n"},
+ {"\n\n\r"},
+ {"\n\n\n"},
+ {"\r\r\r\n"},
+ {"\r\r\n\r"},
+ {"\r\r\n\n"},
+ {"\r\n\r\r"},
+ {"\r\n\r\n"},
+ {"\r\n\n\r"},
+ {"\r\n\n\n"},
+ {"\n\r\r\n"},
+ {"\n\r\n\r"},
+ {"\n\r\n\n"},
+ {"\n\n\r\n"},
+ {"\r\r\n\r\n"},
+ {"\r\n\r\r\n"},
+ {"\r\n\r\n\r"},
+ {"\r\n\r\n\n"},
+ {"\r\n\n\r\n"},
+ {"\n\r\n\r\n"},
+ {"\r\n\r\n\r\n"},
+ {"\r\n\r\n\r\n\r\n"}
+ };
+ }
+
+ boolean isSufficientSectionDelimiter(String trailingSeq) {
+ if (trailingSeq.length() < 2) return false;
+ if (trailingSeq.startsWith("\r\n")) {
+ trailingSeq = trailingSeq.substring(2);
+ } else if (trailingSeq.startsWith("\r") ||
+ trailingSeq.startsWith("\n")) {
+ trailingSeq = trailingSeq.substring(1);
+ } else {
+ return false;
+ }
+ if (trailingSeq.startsWith("\r\n")) {
+ return true;
+ } else if (trailingSeq.startsWith("\r") ||
+ trailingSeq.startsWith("\n")) {
+ return true;
+ }
+ return false;
+ }
+
+ Function<Manifest, byte[]> replaceTrailingLineBreaksManipulation(
+ String trailingSeq) {
+ return m -> {
+ StringBuilder sb = new StringBuilder(manifestToString(m));
+ // cut off default trailing line break characters
+ while ("\r\n".contains(sb.substring(sb.length() - 1))) {
+ sb.deleteCharAt(sb.length() - 1);
+ }
+ // and instead add another trailing sequence
+ sb.append(trailingSeq);
+ return sb.toString().getBytes(UTF_8);
+ };
+ }
+
+ boolean abSigFilesEqual(String jarFilename,
+ Function<Manifest,Object> getter) throws IOException {
+ try (ZipFile zip = new ZipFile(jarFilename)) {
+ ZipEntry ea = zip.getEntry("META-INF/A.SF");
+ Manifest sfa = new Manifest(zip.getInputStream(ea));
+ ZipEntry eb = zip.getEntry("META-INF/B.SF");
+ Manifest sfb = new Manifest(zip.getInputStream(eb));
+ return getter.apply(sfa).equals(getter.apply(sfb));
+ }
+ }
+
+ /**
+ * Create a signed JAR file with a strange sequence of line breaks after
+ * the main attributes and no individual section and hence no file contained
+ * within the JAR file in order not to produce an individual section,
+ * then add no other file and sign it with a different signer.
+ * The manifest is not expected to be changed during the second signature.
+ */
+ @Test(dataProvider = "trailingSeqParams")
+ public void emptyJarTrailingSeq(String trailingSeq) throws Exception {
+ String trailingSeqEscaped = byteArrayToIntList(trailingSeq.getBytes(
+ UTF_8)).stream().map(i -> "" + i).collect(Collectors.joining(""));
+ System.out.println("trailingSeq = " + trailingSeqEscaped);
+ if (trailingSeq.isEmpty()) {
+ return; // invalid manifest without trailing line break
+ }
+
+ test("emptyJarTrailingSeq" + trailingSeqEscaped, null, null,
+ replaceTrailingLineBreaksManipulation(trailingSeq));
+
+ // test called above already asserts by default that the main attributes
+ // digests have not changed.
+ }
+
+ /**
+ * Create a signed JAR file with a strange sequence of line breaks after
+ * the main attributes and no individual section and hence no file contained
+ * within the JAR file in order not to produce an individual section,
+ * then add another file and sign it with a different signer so that the
+ * originally trailing sequence after the main attributes might have to be
+ * completed to a full section delimiter or reproduced only partially
+ * before the new individual section with the added file digest can be
+ * appended. The main attributes digests are expected to change if the
+ * first signed trailing sequence did not contain a blank line and are not
+ * expected to change if superfluous parts of the trailing sequence were
+ * not reproduced. All digests are expected to validate either with digest
+ * or with digestWorkaround.
+ */
+ @Test(dataProvider = "trailingSeqParams")
+ public void emptyJarTrailingSeqAddFile(String trailingSeq) throws Exception{
+ String trailingSeqEscaped = byteArrayToIntList(trailingSeq.getBytes(
+ UTF_8)).stream().map(i -> "" + i).collect(Collectors.joining(""));
+ System.out.println("trailingSeq = " + trailingSeqEscaped);
+ if (!isSufficientSectionDelimiter(trailingSeq)) {
+ return; // invalid manifest without trailing blank line
+ }
+ boolean expectUnchangedDigests =
+ isSufficientSectionDelimiter(trailingSeq);
+ System.out.println("expectUnchangedDigests = " + expectUnchangedDigests);
+ String jarFilename = test("emptyJarTrailingSeqAddFile" +
+ trailingSeqEscaped, null, FILENAME_UPDATED_CONTENTS,
+ replaceTrailingLineBreaksManipulation(trailingSeq),
+ null, expectUnchangedDigests, false);
+
+ // Check that the digests have changed only if another line break had
+ // to be added before a new individual section. That both also are valid
+ // with either digest or digestWorkaround has been checked by test
+ // before.
+ assertEquals(abSigFilesEqual(jarFilename, sf -> sf.getMainAttributes()
+ .getValue("SHA-256-Digest-Manifest-Main-Attributes")),
+ expectUnchangedDigests);
+ }
+
+ /**
+ * Create a signed JAR file with a strange sequence of line breaks after
+ * the only individual section holding the digest of the only file contained
+ * within the JAR file,
+ * then add no other file and sign it with a different signer.
+ * The manifest is expected to be changed during the second signature only
+ * by removing superfluous line break characters which are not digested
+ * and the manifest entry digest is expected not to change.
+ * The individual section is expected to be reproduced without additional
+ * line breaks even if the trailing sequence does not properly delimit
+ * another section.
+ */
+ @Test(dataProvider = "trailingSeqParams")
+ public void singleIndividualSectionTrailingSeq(String trailingSeq)
+ throws Exception {
+ String trailingSeqEscaped = byteArrayToIntList(trailingSeq.getBytes(
+ UTF_8)).stream().map(i -> "" + i).collect(Collectors.joining(""));
+ System.out.println("trailingSeq = " + trailingSeqEscaped);
+ if (trailingSeq.isEmpty()) {
+ return; // invalid manifest without trailing line break
+ }
+ String jarFilename = test("singleIndividualSectionTrailingSeq"
+ + trailingSeqEscaped, FILENAME_INITIAL_CONTENTS, null,
+ replaceTrailingLineBreaksManipulation(trailingSeq));
+
+ assertTrue(abSigFilesEqual(jarFilename, sf -> sf.getAttributes(
+ FILENAME_INITIAL_CONTENTS).getValue("SHA-256-Digest")));
+ }
+
+ /**
+ * Create a signed JAR file with a strange sequence of line breaks after
+ * the first individual section holding the digest of the only file
+ * contained within the JAR file and a second individual section with the
+ * same name to be both digested into the same entry digest,
+ * then add no other file and sign it with a different signer.
+ * The manifest is expected to be changed during the second signature
+ * by removing superfluous line break characters which are not digested
+ * anyway or if the trailingSeq is not a sufficient delimiter that both
+ * intially provided sections are treated as only one which is maybe not
+ * perfect but does at least not result in an invalid signed jar file.
+ */
+ @Test(dataProvider = "trailingSeqParams")
+ public void firstIndividualSectionTrailingSeq(String trailingSeq)
+ throws Exception {
+ String trailingSeqEscaped = byteArrayToIntList(trailingSeq.getBytes(
+ UTF_8)).stream().map(i -> "" + i).collect(Collectors.joining(""));
+ System.out.println("trailingSeq = " + trailingSeqEscaped);
+ String jarFilename;
+ jarFilename = test("firstIndividualSectionTrailingSeq"
+ + trailingSeqEscaped, FILENAME_INITIAL_CONTENTS, null, m -> {
+ StringBuilder sb = new StringBuilder(manifestToString(m));
+ // cut off default trailing line break characters
+ while ("\r\n".contains(sb.substring(sb.length() - 1))) {
+ sb.deleteCharAt(sb.length() - 1);
+ }
+ // and instead add another trailing sequence
+ sb.append(trailingSeq);
+ // now add another section with the same name assuming sb
+ // already contains one entry for FILENAME_INITIAL_CONTENTS
+ sb.append("Name: " + FILENAME_INITIAL_CONTENTS + "\r\n");
+ sb.append("Foo: Bar\r\n");
+ sb.append("\r\n");
+ return sb.toString().getBytes(UTF_8);
+ });
+
+ assertTrue(abSigFilesEqual(jarFilename, sf -> sf.getAttributes(
+ FILENAME_INITIAL_CONTENTS).getValue("SHA-256-Digest")));
+ }
+
+ /**
+ * Create a signed JAR file with two individual sections for the same
+ * contained file (corresponding by name) the first of which properly
+ * delimited and the second of which followed by a strange sequence of
+ * line breaks both digested into the same entry digest,
+ * then add no other file and sign it with a different signer.
+ * The manifest is expected to be changed during the second signature
+ * by removing superfluous line break characters which are not digested
+ * anyway.
+ */
+ @Test(dataProvider = "trailingSeqParams")
+ public void secondIndividualSectionTrailingSeq(String trailingSeq)
+ throws Exception {
+ String trailingSeqEscaped = byteArrayToIntList(trailingSeq.getBytes(
+ UTF_8)).stream().map(i -> "" + i).collect(Collectors.joining(""));
+ System.out.println("trailingSeq = " + trailingSeqEscaped);
+ String jarFilename = test("secondIndividualSectionTrailingSeq" +
+ trailingSeqEscaped, FILENAME_INITIAL_CONTENTS, null, m -> {
+ StringBuilder sb = new StringBuilder(manifestToString(m));
+ sb.append("Name: " + FILENAME_INITIAL_CONTENTS + "\r\n");
+ sb.append("Foo: Bar");
+ sb.append(trailingSeq);
+ return sb.toString().getBytes(UTF_8);
+ });
+
+ assertTrue(abSigFilesEqual(jarFilename, sf -> sf.getAttributes(
+ FILENAME_INITIAL_CONTENTS).getValue("SHA-256-Digest")));
+ }
+
+ /**
+ * Create a signed JAR file with a strange sequence of line breaks after
+ * the only individual section holding the digest of the only file contained
+ * within the JAR file,
+ * then add another file and sign it with a different signer.
+ * The manifest is expected to be changed during the second signature by
+ * removing superfluous line break characters which are not digested
+ * anyway or adding another line break to complete to a proper section
+ * delimiter blank line.
+ * The first file entry digest is expected to change only if another
+ * line break has been added.
+ */
+ @Test(dataProvider = "trailingSeqParams")
+ public void singleIndividualSectionTrailingSeqAddFile(String trailingSeq)
+ throws Exception {
+ String trailingSeqEscaped = byteArrayToIntList(trailingSeq.getBytes(
+ UTF_8)).stream().map(i -> "" + i).collect(Collectors.joining(""));
+ System.out.println("trailingSeq = " + trailingSeqEscaped);
+ if (!isSufficientSectionDelimiter(trailingSeq)) {
+ return; // invalid manifest without trailing blank line
+ }
+ String jarFilename = test("singleIndividualSectionTrailingSeqAddFile"
+ + trailingSeqEscaped,
+ FILENAME_INITIAL_CONTENTS, FILENAME_UPDATED_CONTENTS,
+ replaceTrailingLineBreaksManipulation(trailingSeq),
+ null, true, true);
+
+ assertTrue(abSigFilesEqual(jarFilename, sf -> sf.getAttributes(
+ FILENAME_INITIAL_CONTENTS).getValue("SHA-256-Digest")));
+ }
+
+ /**
+ * Create a signed JAR file with a strange sequence of line breaks after
+ * the first individual section holding the digest of the only file
+ * contained within the JAR file and a second individual section with the
+ * same name to be both digested into the same entry digest,
+ * then add another file and sign it with a different signer.
+ * The manifest is expected to be changed during the second signature
+ * by removing superfluous line break characters which are not digested
+ * anyway or if the trailingSeq is not a sufficient delimiter that both
+ * intially provided sections are treated as only one which is maybe not
+ * perfect but does at least not result in an invalid signed jar file.
+ */
+ @Test(dataProvider = "trailingSeqParams")
+ public void firstIndividualSectionTrailingSeqAddFile(String trailingSeq)
+ throws Exception {
+ String trailingSeqEscaped = byteArrayToIntList(trailingSeq.getBytes(
+ UTF_8)).stream().map(i -> "" + i).collect(Collectors.joining(""));
+ System.out.println("trailingSeq = " + trailingSeqEscaped);
+ String jarFilename = test("firstIndividualSectionTrailingSeqAddFile"
+ + trailingSeqEscaped,
+ FILENAME_INITIAL_CONTENTS, FILENAME_UPDATED_CONTENTS, m -> {
+ StringBuilder sb = new StringBuilder(manifestToString(m));
+ // cut off default trailing line break characters
+ while ("\r\n".contains(sb.substring(sb.length() - 1))) {
+ sb.deleteCharAt(sb.length() - 1);
+ }
+ // and instead add another trailing sequence
+ sb.append(trailingSeq);
+ // now add another section with the same name assuming sb
+ // already contains one entry for FILENAME_INITIAL_CONTENTS
+ sb.append("Name: " + FILENAME_INITIAL_CONTENTS + "\r\n");
+ sb.append("Foo: Bar\r\n");
+ sb.append("\r\n");
+ return sb.toString().getBytes(UTF_8);
+ });
+
+ assertTrue(abSigFilesEqual(jarFilename, sf -> sf.getAttributes(
+ FILENAME_INITIAL_CONTENTS).getValue("SHA-256-Digest")));
+ }
+
+ /**
+ * Create a signed JAR file with two individual sections for the same
+ * contained file (corresponding by name) the first of which properly
+ * delimited and the second of which followed by a strange sequence of
+ * line breaks both digested into the same entry digest,
+ * then add another file and sign it with a different signer.
+ * The manifest is expected to be changed during the second signature
+ * by removing superfluous line break characters which are not digested
+ * anyway or by adding a proper section delimiter.
+ * The digests are expected to be changed only if another line break is
+ * added to properly delimit the next section both digests of which are
+ * expected to validate with either digest or digestWorkaround.
+ */
+ @Test(dataProvider = "trailingSeqParams")
+ public void secondIndividualSectionTrailingSeqAddFile(String trailingSeq)
+ throws Exception {
+ String trailingSeqEscaped = byteArrayToIntList(trailingSeq.getBytes(
+ UTF_8)).stream().map(i -> "" + i).collect(Collectors.joining(""));
+ System.out.println("trailingSeq = " + trailingSeqEscaped);
+ if (!isSufficientSectionDelimiter(trailingSeq)) {
+ return; // invalid manifest without trailing blank line
+ }
+ String jarFilename = test("secondIndividualSectionTrailingSeqAddFile" +
+ trailingSeqEscaped,
+ FILENAME_INITIAL_CONTENTS, FILENAME_UPDATED_CONTENTS, m -> {
+ StringBuilder sb = new StringBuilder(manifestToString(m));
+ sb.append("Name: " + FILENAME_INITIAL_CONTENTS + "\r\n");
+ sb.append("Foo: Bar");
+ sb.append(trailingSeq);
+ return sb.toString().getBytes(UTF_8);
+ }, null, true, true);
+
+ assertTrue(abSigFilesEqual(jarFilename, sf -> sf.getAttributes(
+ FILENAME_INITIAL_CONTENTS).getValue("SHA-256-Digest")));
+ }
+
+ String manifestToString(Manifest mf) {
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ mf.write(out);
+ return new String(out.toByteArray(), UTF_8);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static List<Integer> byteArrayToIntList(byte[] bytes) {
+ List<Integer> list = new ArrayList<>();
+ for (int i = 0; i < bytes.length; i++) {
+ list.add((int) bytes[i]);
+ }
+ return list;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/tools/jarsigner/RemoveDifferentKeyAlgBlockFile.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import java.nio.file.Path;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.jar.Attributes.Name;
+import jdk.test.lib.util.JarUtils;
+import jdk.test.lib.SecurityTools;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+/**
+ * @test
+ * @bug 8217375
+ * @library /test/lib
+ * @run testng RemoveDifferentKeyAlgBlockFile
+ * @summary Checks that if a signed jar file is signed again with the same
+ * signer name and a different algorithm that the signature block file for
+ * the previous signature is removed. Example: the jar had META-INF/A.SF and
+ * META-INF/A.RSA files and is now signed with DSA. So it should contain
+ * an updated META-INF/A.SF and META-INF/A.DSA and the META-INF/A.RSA should
+ * be removed because not valid any longer.
+ */
+public class RemoveDifferentKeyAlgBlockFile {
+
+ static final String KEYSTORE_FILENAME = "test.jks";
+
+ @BeforeClass
+ public void prepareCertificates() throws Exception {
+ SecurityTools.keytool("-genkeypair -keyalg RSA -keystore "
+ + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit"
+ + " -alias RSA -dname CN=RSA").shouldHaveExitValue(0);
+ SecurityTools.keytool("-genkeypair -keyalg DSA -keystore "
+ + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit"
+ + " -alias DSA -dname CN=DSA").shouldHaveExitValue(0);
+ }
+
+ @Test
+ public void testOtherAlgSigBlockFileRemoved() throws Exception {
+ String jarFilename = "test.jar";
+ JarUtils.createJarFile(Path.of(jarFilename), (Manifest) null,
+ Path.of("."));
+ SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -verbose -debug -sigfile A " +
+ jarFilename + " RSA").shouldHaveExitValue(0);
+
+ // change the jar file to invalidate the first signature with RSA
+ String jarFilenameModified = "modified.jar";
+ try (JarFile jar = new JarFile(jarFilename)) {
+ Manifest manifest = jar.getManifest();
+ manifest.getMainAttributes().put(
+ new Name("Some-Key"), "Some-Value");
+ JarUtils.updateManifest(jarFilename, jarFilenameModified, manifest);
+ }
+
+ SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -verbose -debug -sigfile A " +
+ jarFilenameModified + " DSA").shouldHaveExitValue(0);
+ SecurityTools.jarsigner("-verify -keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -debug -verbose " + jarFilenameModified)
+ .shouldHaveExitValue(0);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/tools/jarsigner/SectionNameContinuedVsLineBreak.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.jar.Manifest;
+import java.util.jar.JarFile;
+import jdk.test.lib.util.JarUtils;
+import jdk.test.lib.SecurityTools;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * @test
+ * @bug 8217375
+ * @library /test/lib
+ * @run testng SectionNameContinuedVsLineBreak
+ * @summary Checks some specific line break character sequences in section name
+ * continuation line breaks.
+ */
+public class SectionNameContinuedVsLineBreak {
+
+ static final String KEYSTORE_FILENAME = "test.jks";
+
+ @BeforeTest
+ public void prepareCertificate() throws Exception {
+ SecurityTools.keytool("-genkeypair -keyalg EC -keystore "
+ + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit"
+ + " -alias a -dname CN=A").shouldHaveExitValue(0);
+ }
+
+ void manipulateManifestSignAgainA(
+ String srcJarFilename, String dstJarFilename,
+ Function<Manifest, byte[]> manifestManipulation) throws Exception {
+ byte[] manipulatedManifest = manifestManipulation.apply(
+ new Manifest(new ByteArrayInputStream(
+ Utils.readJarManifestBytes(srcJarFilename))));
+ Utils.echoManifest(manipulatedManifest, "manipulated manifest");
+ JarUtils.updateJar(srcJarFilename, dstJarFilename, Map.of(
+ JarFile.MANIFEST_NAME, manipulatedManifest));
+ SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -verbose -debug " + dstJarFilename + " a")
+ .shouldHaveExitValue(0);
+ Utils.echoManifest(Utils.readJarManifestBytes(
+ dstJarFilename), "manipulated jar signed again with a");
+ // check assumption that jar is valid at this point
+ SecurityTools.jarsigner("-verify -keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -verbose -debug " + dstJarFilename + " a")
+ .shouldHaveExitValue(0);
+ }
+
+ void test(String name, Function<Manifest, byte[]> manifestManipulation,
+ String jarContentFilename) throws Exception {
+ String jarFilename1 = "test-" + name + "-step1.jar";
+ Files.write(Path.of(jarContentFilename),
+ jarContentFilename.getBytes(UTF_8));
+ JarUtils.createJarFile(Path.of(jarFilename1), (Manifest)
+ /* no manifest will let jarsigner create a default one */ null,
+ Path.of("."), Path.of(jarContentFilename));
+ SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -verbose -debug " + jarFilename1 +
+ " a").shouldHaveExitValue(0);
+ Utils.echoManifest(Utils.readJarManifestBytes(
+ jarFilename1), "signed jar");
+ String jarFilename2 = "test-" + name + "-step2.jar";
+ manipulateManifestSignAgainA(jarFilename1, jarFilename2,
+ manifestManipulation);
+
+ SecurityTools.jarsigner("-verify -strict -keystore " +
+ KEYSTORE_FILENAME + " -storepass changeit -debug -verbose " +
+ jarFilename2 + " a").shouldHaveExitValue(0);
+ }
+
+ /**
+ * Test signing a jar with a manifest that has an entry the name of
+ * which continued on a continuation line with '\r' as line break before
+ * the continuation line space ' ' on the line the name starts.
+ */
+ @Test
+ public void testContinueNameAfterCr() throws Exception {
+ String filename = "abc";
+ test("testContinueNameAfterCr", m -> {
+ String digest = m.getAttributes("abc").getValue("SHA-256-Digest");
+ m.getEntries().remove("abc");
+ return (manifestToString(m)
+ + "Name: a\r"
+ + " bc\r\n"
+ + "SHA-256-Digest: " + digest + "\r\n"
+ + "\r\n").getBytes(UTF_8);
+ }, filename);
+ }
+
+ /**
+ * Test signing a jar with a manifest that has an entry the name of
+ * which continued on a continuation line with '\r' as line break before
+ * the continuation line space ' ' after a first continuation.
+ */
+ @Test
+ public void testContinueNameAfterCrOnContinuationLine() throws Exception {
+ String filename = "abc";
+ test("testContinueNameAfterCr", m -> {
+ String digest = m.getAttributes("abc").getValue("SHA-256-Digest");
+ m.getEntries().remove("abc");
+ return (manifestToString(m)
+ + "Name: a\r\n"
+ + " b\r"
+ + " c\r\n"
+ + "SHA-256-Digest: " + digest + "\r\n"
+ + "\r\n").getBytes(UTF_8);
+ }, filename);
+ }
+
+ /**
+ * Test signing a jar with a manifest that has an entry the name of
+ * which continued on a continuation line and terminated with '\r' as line
+ * break after the name.
+ */
+ @Test
+ public void testEndNameWithCrOnContinuationLine() throws Exception {
+ String filename = "abc";
+ test("testContinueNameAfterCr", m -> {
+ String digest = m.getAttributes("abc").getValue("SHA-256-Digest");
+ m.getEntries().remove("abc");
+ return (manifestToString(m)
+ + "Name: a\r\n"
+ + " bc\r"
+ + "SHA-256-Digest: " + digest + "\r\n"
+ + "\r\n").getBytes(UTF_8);
+ }, filename);
+ }
+
+ String manifestToString(Manifest mf) {
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ mf.write(out);
+ return out.toString(UTF_8);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
--- a/test/jdk/sun/security/tools/jarsigner/Utils.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sun/security/tools/jarsigner/Utils.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -21,8 +21,15 @@
* questions.
*/
+import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.InputStream;
import java.io.IOException;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.zip.ZipFile;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
/**
* Helper class.
@@ -35,5 +42,66 @@
}
}
+ static void printNonPrintableCharactersEscaped(byte[] manifest)
+ throws IOException {
+ // keep byte sequences encoding multi-byte UTF-8 encoded and composite
+ // characters together as much as possible before decoding into a String
+ ByteArrayOutputStream lineBuf = new ByteArrayOutputStream();
+ for (int i = 0; i < manifest.length; i++) {
+ switch (manifest[i]) {
+ case '\t':
+ lineBuf.write("\\t".getBytes(UTF_8));
+ break;
+ case '\r':
+ lineBuf.write("\\r".getBytes(UTF_8));
+ if (i + 1 >= manifest.length || manifest[i + 1] != '\n') {
+ System.out.println(lineBuf.toString(UTF_8));
+ lineBuf.reset();
+ }
+ break;
+ case '\n':
+ lineBuf.write("\\n".getBytes(UTF_8));
+ System.out.println(lineBuf.toString(UTF_8));
+ lineBuf.reset();
+ break;
+ default:
+ lineBuf.write(manifest[i]);
+ }
+ }
+ if (lineBuf.size() > 0) {
+ System.out.println(lineBuf.toString(UTF_8));
+ }
+ }
+
+ static void echoManifest(byte[] manifest, String msg) throws IOException {
+ System.out.println("-".repeat(72));
+ System.out.println(msg);
+ System.out.println("-".repeat(72));
+ printNonPrintableCharactersEscaped(manifest);
+ System.out.println("-".repeat(72));
+ }
+
+ static byte[] readJarManifestBytes(String jarFilename) throws IOException {
+ return readJarEntryBytes(jarFilename, JarFile.MANIFEST_NAME);
+ }
+
+ static byte[] readJarEntryBytes(String jarFilename, String jarEntryname)
+ throws IOException {
+ try (
+ ZipFile jar = new ZipFile(jarFilename);
+ InputStream is = jar.getInputStream(jar.getEntry(jarEntryname));
+ ) {
+ return is.readAllBytes();
+ }
+ }
+
+ static String escapeStringWithNumbers(String lineBreak) {
+ String escaped = "";
+ byte[] bytes = lineBreak.getBytes(UTF_8);
+ for (int i = 0; i < bytes.length; i++) {
+ escaped += bytes[i];
+ }
+ return escaped;
+ }
+
}
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/tools/jarsigner/WasSignedByOtherSigner.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.jar.Attributes.Name;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import jdk.test.lib.util.JarUtils;
+import jdk.test.lib.SecurityTools;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @bug 8217375
+ * @library /test/lib
+ * @run testng WasSignedByOtherSigner
+ * @summary Checks that {@code wasSigned} in
+ * {@link jdk.security.jarsigner.JarSigner#sign0} is set true if the jar to sign
+ * contains a signature that will not be overwritten with the current one.
+ */
+public class WasSignedByOtherSigner {
+
+ static final String KEYSTORE_FILENAME = "test.jks";
+
+ @BeforeClass
+ public void prepareKeyStore() throws Exception {
+ SecurityTools.keytool("-genkeypair -keyalg EC -keystore "
+ + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit"
+ + " -alias a -dname CN=A").shouldHaveExitValue(0);
+ }
+
+ void test(String secondSigner, boolean expRrewritten) throws Exception {
+ String jarFilename1 = "test" + secondSigner + "-1.jar";
+ JarUtils.createJarFile(Path.of(jarFilename1), (Manifest) null,
+ Path.of("."));
+ // TODO: use jarsigner here only to create a default manifest...
+ SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -verbose -debug " + jarFilename1 + " a")
+ .shouldHaveExitValue(0);
+ Utils.echoManifest(Utils.readJarManifestBytes(
+ jarFilename1), "initial manifest");
+
+ // replace manifest with a non-standard one that can later be checked
+ String jarFilename2 = "test" + secondSigner + "-2.jar";
+ JarUtils.updateJar(jarFilename1, jarFilename2, Map.of(
+ // add a fake sig-related file to trigger wasSigned in JarSigner
+ "META-INF/.SF", Name.SIGNATURE_VERSION + ": 1.0\r\n"));
+ Utils.echoManifest(Utils.readJarManifestBytes(
+ jarFilename2), "with fake META-INF.SF file");
+ String jarFilename3 = "test" + secondSigner + "-3.jar";
+ JarUtils.updateManifest(jarFilename2, jarFilename3, new Manifest() {
+ @Override public void write(OutputStream out) throws IOException {
+ // no trailing blank line
+ out.write((Name.MANIFEST_VERSION + ": 1.0\r\n").getBytes(UTF_8));
+ }
+ });
+ Utils.echoManifest(Utils.readJarManifestBytes(
+ jarFilename3), "with manifest manipulated");
+ SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -verbose -debug " + jarFilename3 + " a")
+ .shouldHaveExitValue(0);
+ Utils.echoManifest(Utils.readJarManifestBytes(
+ jarFilename3), "signed");
+ String jarFilename4 = "test" + secondSigner + "-4.jar";
+ JarUtils.updateJar(jarFilename3, jarFilename4,
+ Map.of("META-INF/.SF", false));
+ Utils.echoManifest(Utils.readJarManifestBytes(
+ jarFilename4), "with fake META-INF.SF file removed");
+
+ // re-sign the jar with signer named secondSigner (same or different)
+ SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -verbose -debug -sigfile " +
+ secondSigner + " " + jarFilename4 + " a")
+ .shouldHaveExitValue(0);
+ Utils.echoManifest(Utils.readJarManifestBytes(
+ jarFilename4), "signed again");
+ // remove META-INF/.SF from signed jar again which would not validate
+
+ // in any case verify that the resulting jar file is valid
+ SecurityTools.jarsigner("-verify -keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -debug -verbose " + jarFilename4)
+ .shouldHaveExitValue(0);
+ SecurityTools.jarsigner("-verify -keystore " + KEYSTORE_FILENAME +
+ " -storepass changeit -debug -verbose " + jarFilename4 +
+ " a").shouldHaveExitValue(0);
+
+ // if wasSigned was true in JarSigner#sign0 the manifest (only main
+ // attributes present and tested here but same consideration applies
+ // to individual sections just the same) should be reproduced with
+ // unchanged binary form. Otherwise, if there were no previous
+ // signatures or only one being replaced, the manifest is kind of
+ // "normalized" by re-writing it thereby replacing all line breaks
+ // (from cr or lf to crlf) and replacing all line breaks onto
+ // continuation lines and also writing all section delimiting blank
+ // lines.
+ // if that "normalization" has took place the test here can conclude
+ // whether wasSigned was true or was not.
+ try (ZipFile jar = new ZipFile(jarFilename4)) {
+ ZipEntry ze = jar.getEntry(JarFile.MANIFEST_NAME);
+ byte[] manifestBytes = jar.getInputStream(ze).readAllBytes();
+ Utils.echoManifest(manifestBytes, "manifest");
+ String manifestString = new String(manifestBytes, UTF_8);
+ boolean actRewritten = manifestString.endsWith("\r\n\r\n");
+ assertEquals(actRewritten, expRrewritten);
+ }
+ }
+
+ @Test
+ public void reSignSameSigner() throws Exception {
+ test("A", true);
+ }
+
+ @Test
+ public void reSignOtherSigner() throws Exception {
+ test("B", false);
+ }
+
+}
--- a/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,55 +23,59 @@
/*
* @test
- * @summary This test is used to verify the compatibility on jarsigner cross
+ * @bug 8217375
+ * @summary This test is used to verify the compatibility of jarsigner across
* different JDK releases. It also can be used to check jar signing (w/
- * and w/o TSA) and verifying on some specific key algorithms and digest
- * algorithms.
- * Note that, this is a manual test. For more details about the test and
- * its usages, please look through README.
+ * and w/o TSA) and to verify some specific signing and digest algorithms.
+ * Note that this is a manual test. For more details about the test and
+ * its usages, please look through the README.
*
- * @modules java.base/sun.security.pkcs
- * java.base/sun.security.timestamp
- * java.base/sun.security.tools.keytool
- * java.base/sun.security.util
- * java.base/sun.security.x509
- * @library /test/lib /lib/testlibrary ../warnings
+ * @library /test/lib ../warnings
* @compile -source 1.7 -target 1.7 JdkUtils.java
* @run main/manual/othervm Compatibility
*/
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.OutputStream;
import java.io.PrintStream;
+import java.nio.file.Path;
+import java.nio.file.Files;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.jar.Manifest;
+import java.util.jar.Attributes.Name;
import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.util.JarUtils;
-public class Compatibility {
+import static java.nio.charset.StandardCharsets.UTF_8;
- private static final String TEST_JAR_NAME = "test.jar";
+public class Compatibility {
private static final String TEST_SRC = System.getProperty("test.src");
private static final String TEST_CLASSES = System.getProperty("test.classes");
private static final String TEST_JDK = System.getProperty("test.jdk");
- private static final String TEST_JARSIGNER = jarsignerPath(TEST_JDK);
+ private static JdkInfo TEST_JDK_INFO;
private static final String PROXY_HOST = System.getProperty("proxyHost");
private static final String PROXY_PORT = System.getProperty("proxyPort", "80");
@@ -84,29 +88,43 @@
"javaSecurityFile", TEST_SRC + "/java.security");
private static final String PASSWORD = "testpass";
- private static final String KEYSTORE = "testKeystore";
+ private static final String KEYSTORE = "testKeystore.jks";
private static final String RSA = "RSA";
private static final String DSA = "DSA";
private static final String EC = "EC";
- private static final String[] KEY_ALGORITHMS = new String[] {
+ private static String[] KEY_ALGORITHMS;
+ private static final String[] DEFAULT_KEY_ALGORITHMS = new String[] {
RSA,
DSA,
EC};
private static final String SHA1 = "SHA-1";
private static final String SHA256 = "SHA-256";
+ private static final String SHA384 = "SHA-384";
private static final String SHA512 = "SHA-512";
private static final String DEFAULT = "DEFAULT";
- private static final String[] DIGEST_ALGORITHMS = new String[] {
+ private static String[] DIGEST_ALGORITHMS;
+ private static final String[] DEFAULT_DIGEST_ALGORITHMS = new String[] {
SHA1,
SHA256,
- SHA512,
+ SHA384,
+ SHA512, // note: digests break onto continuation line in manifest
DEFAULT};
- private static final boolean[] EXPIRED = new boolean[] {
- false,
- true};
+ private static final boolean[] EXPIRED =
+ Boolean.valueOf(System.getProperty("expired", "true")) ?
+ new boolean[] { false, true } : new boolean[] { false };
+
+ private static final boolean TEST_COMPREHENSIVE_JAR_CONTENTS =
+ Boolean.valueOf(System.getProperty(
+ "testComprehensiveJarContents", "false"));
+
+ private static final boolean TEST_JAR_UPDATE =
+ Boolean.valueOf(System.getProperty("testJarUpdate", "false"));
+
+ private static final boolean STRICT =
+ Boolean.valueOf(System.getProperty("strict", "false"));
private static final Calendar CALENDAR = Calendar.getInstance();
private static final DateFormat DATE_FORMAT
@@ -119,7 +137,7 @@
static {
if (CERT_VALIDITY < 1 || CERT_VALIDITY > 1440) {
throw new RuntimeException(
- "certValidity if out of range [1, 1440]: " + CERT_VALIDITY);
+ "certValidity out of range [1, 1440]: " + CERT_VALIDITY);
}
}
@@ -132,27 +150,34 @@
private static DetailsOutputStream detailsOutput;
- public static void main(String[] args) throws Throwable {
+ private static int sigfileCounter;
+
+ private static String nextSigfileName(String alias, String u, String s) {
+ String sigfileName = "" + (++sigfileCounter);
+ System.out.println("using sigfile " + sigfileName + " for alias "
+ + alias + " signing " + u + ".jar to " + s + ".jar");
+ return sigfileName;
+ }
+
+ public static void main(String... args) throws Throwable {
// Backups stdout and stderr.
PrintStream origStdOut = System.out;
PrintStream origStdErr = System.err;
- detailsOutput = new DetailsOutputStream();
+ detailsOutput = new DetailsOutputStream(outfile());
// Redirects the system output to a custom one.
PrintStream printStream = new PrintStream(detailsOutput);
System.setOut(printStream);
System.setErr(printStream);
+ TEST_JDK_INFO = new JdkInfo(TEST_JDK);
+
List<TsaInfo> tsaList = tsaInfoList();
- if (tsaList.size() == 0) {
- throw new RuntimeException("TSA service is mandatory.");
- }
-
List<JdkInfo> jdkInfoList = jdkInfoList();
List<CertInfo> certList = createCertificates(jdkInfoList);
- createJar();
- List<SignItem> signItems = test(jdkInfoList, tsaList, certList);
+ List<SignItem> signItems =
+ test(jdkInfoList, tsaList, certList, createJars());
boolean failed = generateReport(tsaList, signItems);
@@ -167,24 +192,100 @@
}
}
- // Creates a jar file that contains an empty file.
- private static void createJar() throws IOException {
- String testFile = "test";
- new File(testFile).createNewFile();
- JarUtils.createJar(TEST_JAR_NAME, testFile);
+ private static SignItem createJarFile(String jar, Manifest m,
+ String... files) throws IOException {
+ JarUtils.createJarFile(Path.of(jar), m, Path.of("."),
+ Arrays.stream(files).map(Path::of).toArray(Path[]::new));
+ return SignItem.build()
+ .signedJar(jar.replaceAll("[.]jar$", ""))
+ .addContentFiles(Arrays.stream(files).collect(Collectors.toList()));
+ }
+
+ private static String createDummyFile(String name) throws IOException {
+ if (name.contains("/")) new File(name).getParentFile().mkdir();
+ try (OutputStream fos = new FileOutputStream(name)) {
+ fos.write(name.getBytes(UTF_8));
+ }
+ return name;
+ }
+
+ // Creates one or more jar files to test
+ private static List<SignItem> createJars() throws IOException {
+ List<SignItem> jarList = new ArrayList<>();
+
+ Manifest m = new Manifest();
+ m.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
+
+ // creates a jar file that contains a dummy file
+ jarList.add(createJarFile("test.jar", m, createDummyFile("dummy")));
+
+ if (TEST_COMPREHENSIVE_JAR_CONTENTS) {
+
+ // empty jar file so that jarsigner will add a default manifest
+ jarList.add(createJarFile("empty.jar", m));
+
+ // jar file that contains only an empty manifest with empty main
+ // attributes (due to missing "Manifest-Version" header)
+ JarUtils.createJar("nomainatts.jar");
+ jarList.add(SignItem.build().signedJar("nomainatts"));
+
+ // creates a jar file that contains several files.
+ jarList.add(createJarFile("files.jar", m,
+ IntStream.range(1, 9).boxed().map(i -> {
+ try {
+ return createDummyFile("dummy" + i);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }).toArray(String[]::new)
+ ));
+
+ // forces a line break by exceeding the line width limit of 72 bytes
+ // in the filename and hence manifest entry name
+ jarList.add(createJarFile("longfilename.jar", m,
+ createDummyFile("test".repeat(20))));
+
+ // another interesting case is with different digest algorithms
+ // resulting in digests broken across line breaks onto continuation
+ // lines. these however are set with the 'digestAlgs' option or
+ // include all digest algorithms by default, see SignTwice.java.
+ }
+
+ return jarList;
+ }
+
+ // updates a signed jar file by adding another file
+ private static List<SignItem> updateJar(SignItem prev) throws IOException {
+ List<SignItem> jarList = new ArrayList<>();
+
+ // sign unmodified jar again
+ Files.copy(Path.of(prev.signedJar + ".jar"),
+ Path.of(prev.signedJar + "-signagainunmodified.jar"));
+ jarList.add(SignItem.build(prev)
+ .signedJar(prev.signedJar + "-signagainunmodified"));
+
+ String oldJar = prev.signedJar;
+ String newJar = oldJar + "-addfile";
+ String triggerUpdateFile = "addfile";
+ JarUtils.updateJar(oldJar + ".jar", newJar + ".jar", triggerUpdateFile);
+ jarList.add(SignItem.build(prev).signedJar(newJar)
+ .addContentFiles(Arrays.asList(triggerUpdateFile)));
+
+ return jarList;
}
// Creates a key store that includes a set of valid/expired certificates
// with various algorithms.
private static List<CertInfo> createCertificates(List<JdkInfo> jdkInfoList)
throws Throwable {
- List<CertInfo> certList = new ArrayList<CertInfo>();
- Set<String> expiredCertFilter = new HashSet<String>();
+ List<CertInfo> certList = new ArrayList<>();
+ Set<String> expiredCertFilter = new HashSet<>();
- for(JdkInfo jdkInfo : jdkInfoList) {
- for(String keyAlgorithm : KEY_ALGORITHMS) {
- for(String digestAlgorithm : DIGEST_ALGORITHMS) {
- for(int keySize : keySizes(keyAlgorithm)) {
+ for (JdkInfo jdkInfo : jdkInfoList) {
+ for (String keyAlgorithm : keyAlgs()) {
+ if (!jdkInfo.supportsKeyAlg(keyAlgorithm)) continue;
+ for (int keySize : keySizes(keyAlgorithm)) {
+ for (String digestAlgorithm : digestAlgs()) {
for(boolean expired : EXPIRED) {
// It creates only one expired certificate for one
// key algorithm.
@@ -194,41 +295,60 @@
}
CertInfo certInfo = new CertInfo(
- jdkInfo.version,
+ jdkInfo,
keyAlgorithm,
digestAlgorithm,
keySize,
expired);
- if (!certList.contains(certInfo)) {
- String alias = createCertificate(
- jdkInfo.jdkPath, certInfo);
- if (alias != null) {
- certList.add(certInfo);
- }
+ // If the signature algorithm is not supported by the
+ // JDK, it cannot try to sign jar with this algorithm.
+ String sigalg = certInfo.sigalg();
+ if (sigalg != null &&
+ !jdkInfo.isSupportedSigalg(sigalg)) {
+ continue;
}
+ createCertificate(jdkInfo, certInfo);
+ certList.add(certInfo);
}
}
}
}
}
+ System.out.println("the keystore contents:");
+ for (JdkInfo jdkInfo : jdkInfoList) {
+ execTool(jdkInfo.jdkPath + "/bin/keytool", new String[] {
+ "-v",
+ "-storetype",
+ "jks",
+ "-storepass",
+ PASSWORD,
+ "-keystore",
+ KEYSTORE,
+ "-list"
+ });
+ }
+
return certList;
}
// Creates/Updates a key store that adds a certificate with specific algorithm.
- private static String createCertificate(String jdkPath, CertInfo certInfo)
+ private static void createCertificate(JdkInfo jdkInfo, CertInfo certInfo)
throws Throwable {
- String alias = certInfo.alias();
-
- List<String> arguments = new ArrayList<String>();
+ List<String> arguments = new ArrayList<>();
arguments.add("-J-Djava.security.properties=" + JAVA_SECURITY);
arguments.add("-v");
+ arguments.add("-debug");
arguments.add("-storetype");
arguments.add("jks");
- arguments.add("-genkey");
+ arguments.add("-keystore");
+ arguments.add(KEYSTORE);
+ arguments.add("-storepass");
+ arguments.add(PASSWORD);
+ arguments.add(jdkInfo.majorVersion < 6 ? "-genkey" : "-genkeypair");
arguments.add("-keyalg");
arguments.add(certInfo.keyAlgorithm);
- String sigalg = sigalg(certInfo.digestAlgorithm, certInfo.keyAlgorithm);
+ String sigalg = certInfo.sigalg();
if (sigalg != null) {
arguments.add("-sigalg");
arguments.add(sigalg);
@@ -238,41 +358,29 @@
arguments.add(certInfo.keySize + "");
}
arguments.add("-dname");
- arguments.add("CN=Test");
+ arguments.add("CN=" + certInfo);
arguments.add("-alias");
- arguments.add(alias);
+ arguments.add(certInfo.alias());
arguments.add("-keypass");
arguments.add(PASSWORD);
- arguments.add("-storepass");
- arguments.add(PASSWORD);
arguments.add("-startdate");
arguments.add(startDate(certInfo.expired));
arguments.add("-validity");
+// arguments.add(DELAY_VERIFY ? "1" : "222"); // > six months no warn
arguments.add("1");
- arguments.add("-keystore");
- arguments.add(KEYSTORE);
OutputAnalyzer outputAnalyzer = execTool(
- jdkPath + "/bin/keytool",
+ jdkInfo.jdkPath + "/bin/keytool",
arguments.toArray(new String[arguments.size()]));
- if (outputAnalyzer.getExitValue() == 0
- && !outputAnalyzer.getOutput().matches("[Ee]xception")) {
- return alias;
- } else {
- return null;
+ if (outputAnalyzer.getExitValue() != 0
+ || outputAnalyzer.getOutput().matches("[Ee]xception")
+ || outputAnalyzer.getOutput().matches(Test.ERROR + " ?")) {
+ System.out.println(outputAnalyzer.getOutput());
+ throw new Exception("error generating a key pair: " + arguments);
}
}
- private static String sigalg(String digestAlgorithm, String keyAlgorithm) {
- if (digestAlgorithm == DEFAULT) {
- return null;
- }
-
- String keyName = keyAlgorithm == EC ? "ECDSA" : keyAlgorithm;
- return digestAlgorithm.replace("-", "") + "with" + keyName;
- }
-
// The validity period of a certificate always be 1 day. For creating an
// expired certificate, the start date is the time before 1 day, then the
// certificate expires immediately. And for creating a valid certificate,
@@ -280,26 +388,36 @@
// the certificate will expires in CERT_VALIDITY minutes.
private static String startDate(boolean expiredCert) {
CALENDAR.setTime(new Date());
- CALENDAR.add(Calendar.DAY_OF_MONTH, -1);
- if (!expiredCert) {
+ if (DELAY_VERIFY || expiredCert) {
+ // corresponds to '-validity 1'
+ CALENDAR.add(Calendar.DAY_OF_MONTH, -1);
+ }
+ if (DELAY_VERIFY && !expiredCert) {
CALENDAR.add(Calendar.MINUTE, CERT_VALIDITY);
}
Date startDate = CALENDAR.getTime();
- lastCertStartTime = startDate.getTime();
+ if (!expiredCert) {
+ lastCertStartTime = startDate.getTime();
+ }
return DATE_FORMAT.format(startDate);
}
- // Retrieves JDK info from the file which is specified by property jdkListFile,
- // or from property jdkList if jdkListFile is not available.
+ private static String outfile() {
+ return System.getProperty("o");
+ }
+
+ // Retrieves JDK info from the file which is specified by property
+ // jdkListFile, or from property jdkList if jdkListFile is not available.
private static List<JdkInfo> jdkInfoList() throws Throwable {
String[] jdkList = list("jdkList");
if (jdkList.length == 0) {
- jdkList = new String[] { TEST_JDK };
+ jdkList = new String[] { "TEST_JDK" };
}
- List<JdkInfo> jdkInfoList = new ArrayList<JdkInfo>();
+ List<JdkInfo> jdkInfoList = new ArrayList<>();
for (String jdkPath : jdkList) {
- JdkInfo jdkInfo = new JdkInfo(jdkPath);
+ JdkInfo jdkInfo = "TEST_JDK".equalsIgnoreCase(jdkPath) ?
+ TEST_JDK_INFO : new JdkInfo(jdkPath);
// The JDK version must be unique.
if (!jdkInfoList.contains(jdkInfo)) {
jdkInfoList.add(jdkInfo);
@@ -310,12 +428,51 @@
return jdkInfoList;
}
+ private static List<String> keyAlgs() throws IOException {
+ if (KEY_ALGORITHMS == null) KEY_ALGORITHMS = list("keyAlgs");
+ if (KEY_ALGORITHMS.length == 0)
+ return Arrays.asList(DEFAULT_KEY_ALGORITHMS);
+ return Arrays.stream(KEY_ALGORITHMS).map(a -> a.split(";")[0])
+ .collect(Collectors.toList());
+ }
+
+ // Return key sizes according to the specified key algorithm.
+ private static int[] keySizes(String keyAlgorithm) throws IOException {
+ if (KEY_ALGORITHMS == null) KEY_ALGORITHMS = list("keyAlgs");
+ for (String keyAlg : KEY_ALGORITHMS) {
+ String[] split = (keyAlg + " ").split(";");
+ if (keyAlgorithm.equals(split[0].trim()) && split.length > 1) {
+ int sizes[] = new int[split.length - 1];
+ for (int i = 1; i <= sizes.length; i++)
+ sizes[i - 1] = split[i].isBlank() ? 0 : // default
+ Integer.parseInt(split[i].trim());
+ return sizes;
+ }
+ }
+
+ // defaults
+ if (RSA.equals(keyAlgorithm) || DSA.equals(keyAlgorithm)) {
+ return new int[] { 1024, 2048, 0 }; // 0 is no keysize specified
+ } else if (EC.equals(keyAlgorithm)) {
+ return new int[] { 384, 571, 0 }; // 0 is no keysize specified
+ } else {
+ throw new RuntimeException("problem determining key sizes");
+ }
+ }
+
+ private static List<String> digestAlgs() throws IOException {
+ if (DIGEST_ALGORITHMS == null) DIGEST_ALGORITHMS = list("digestAlgs");
+ if (DIGEST_ALGORITHMS.length == 0)
+ return Arrays.asList(DEFAULT_DIGEST_ALGORITHMS);
+ return Arrays.asList(DIGEST_ALGORITHMS);
+ }
+
// Retrieves TSA info from the file which is specified by property tsaListFile,
// or from property tsaList if tsaListFile is not available.
private static List<TsaInfo> tsaInfoList() throws IOException {
String[] tsaList = list("tsaList");
- List<TsaInfo> tsaInfoList = new ArrayList<TsaInfo>();
+ List<TsaInfo> tsaInfoList = new ArrayList<>();
for (int i = 0; i < tsaList.length; i++) {
String[] values = tsaList[i].split(";digests=");
@@ -324,25 +481,30 @@
digests = values[1].split(",");
}
- TsaInfo bufTsa = new TsaInfo(i, values[0]);
-
+ String tsaUrl = values[0];
+ if (tsaUrl.isEmpty() || tsaUrl.equalsIgnoreCase("notsa")) {
+ tsaUrl = null;
+ }
+ TsaInfo bufTsa = new TsaInfo(i, tsaUrl);
for (String digest : digests) {
- bufTsa.addDigest(digest);
+ bufTsa.addDigest(digest.toUpperCase());
}
-
tsaInfoList.add(bufTsa);
}
+ if (tsaInfoList.size() == 0) {
+ throw new RuntimeException("TSA service is mandatory unless "
+ + "'notsa' specified explicitly.");
+ }
return tsaInfoList;
}
- private static String[] list(String listProp)
- throws IOException {
+ private static String[] list(String listProp) throws IOException {
String listFileProp = listProp + "File";
String listFile = System.getProperty(listFileProp);
if (!isEmpty(listFile)) {
System.out.println(listFileProp + "=" + listFile);
- List<String> list = new ArrayList<String>();
+ List<String> list = new ArrayList<>();
BufferedReader reader = new BufferedReader(
new FileReader(listFile));
String line;
@@ -369,26 +531,47 @@
// JDKs (verifiers), including the signer itself, try to verify the signed
// jars respectively.
private static List<SignItem> test(List<JdkInfo> jdkInfoList,
- List<TsaInfo> tsaInfoList, List<CertInfo> certList)
- throws Throwable {
+ List<TsaInfo> tsaInfoList, List<CertInfo> certList,
+ List<SignItem> jars) throws Throwable {
detailsOutput.transferPhase();
- List<SignItem> signItems = signing(jdkInfoList, tsaInfoList, certList);
+ List<SignItem> signItems = new ArrayList<>();
+ signItems.addAll(signing(jdkInfoList, tsaInfoList, certList, jars));
+ if (TEST_JAR_UPDATE) {
+ signItems.addAll(signing(jdkInfoList, tsaInfoList, certList,
+ updating(signItems.stream().filter(
+ x -> x.status != Status.ERROR)
+ .collect(Collectors.toList()))));
+ }
detailsOutput.transferPhase();
for (SignItem signItem : signItems) {
for (JdkInfo verifierInfo : jdkInfoList) {
- // JDK 6 doesn't support EC
- if (!verifierInfo.isJdk6()
- || signItem.certInfo.keyAlgorithm != EC) {
- verifying(signItem, VerifyItem.build(verifierInfo));
- }
+ if (!verifierInfo.supportsKeyAlg(
+ signItem.certInfo.keyAlgorithm)) continue;
+ VerifyItem verifyItem = VerifyItem.build(verifierInfo);
+ verifyItem.addSignerCertInfos(signItem);
+ signItem.addVerifyItem(verifyItem);
+ verifying(signItem, verifyItem);
}
}
+ // if lastCertExpirationTime passed already now, probably some
+ // certificate was already expired during jar signature verification
+ // (jarsigner -verify) and the test should probably be repeated with an
+ // increased validity period -DcertValidity CERT_VALIDITY
+ long lastCertExpirationTime = lastCertStartTime + 24 * 60 * 60 * 1000;
+ if (lastCertExpirationTime < System.currentTimeMillis()) {
+ throw new AssertionError("CERT_VALIDITY (" + CERT_VALIDITY
+ + " [minutes]) was too short. "
+ + "Creating and signing the jars took longer, "
+ + "presumably at least "
+ + ((lastCertExpirationTime - System.currentTimeMillis())
+ / 60 * 1000 + CERT_VALIDITY) + " [minutes].");
+ }
+
if (DELAY_VERIFY) {
detailsOutput.transferPhase();
System.out.print("Waiting for delay verifying");
- long lastCertExpirationTime = lastCertStartTime + 24 * 60 * 60 * 1000;
while (System.currentTimeMillis() < lastCertExpirationTime) {
TimeUnit.SECONDS.sleep(30);
System.out.print(".");
@@ -404,128 +587,98 @@
}
detailsOutput.transferPhase();
-
return signItems;
}
private static List<SignItem> signing(List<JdkInfo> jdkInfos,
- List<TsaInfo> tsaList, List<CertInfo> certList) throws Throwable {
- List<SignItem> signItems = new ArrayList<SignItem>();
-
- Set<String> signFilter = new HashSet<String>();
+ List<TsaInfo> tsaList, List<CertInfo> certList,
+ List<SignItem> unsignedJars) throws Throwable {
+ List<SignItem> signItems = new ArrayList<>();
- for (JdkInfo signerInfo : jdkInfos) {
- for (String keyAlgorithm : KEY_ALGORITHMS) {
- // JDK 6 doesn't support EC
- if (signerInfo.isJdk6() && keyAlgorithm == EC) {
- continue;
+ for (CertInfo certInfo : certList) {
+ JdkInfo signerInfo = certInfo.jdkInfo;
+ String keyAlgorithm = certInfo.keyAlgorithm;
+ String sigDigestAlgorithm = certInfo.digestAlgorithm;
+ int keySize = certInfo.keySize;
+ boolean expired = certInfo.expired;
+
+ for (String jarDigestAlgorithm : digestAlgs()) {
+ if (DEFAULT.equals(jarDigestAlgorithm)) {
+ jarDigestAlgorithm = null;
}
- for (String digestAlgorithm : DIGEST_ALGORITHMS) {
- String sigalg = sigalg(digestAlgorithm, keyAlgorithm);
- // If the signature algorithm is not supported by the JDK,
- // it cannot try to sign jar with this algorithm.
- if (sigalg != null && !signerInfo.isSupportedSigalg(sigalg)) {
- continue;
- }
-
- // If the JDK doesn't support option -tsadigestalg, the
- // associated cases just be ignored.
- if (digestAlgorithm != DEFAULT
- && !signerInfo.supportsTsadigestalg) {
- continue;
- }
+ for (TsaInfo tsaInfo : tsaList) {
+ String tsaUrl = tsaInfo.tsaUrl;
- for (int keySize : keySizes(keyAlgorithm)) {
- for (boolean expired : EXPIRED) {
- CertInfo certInfo = new CertInfo(
- signerInfo.version,
- keyAlgorithm,
- digestAlgorithm,
- keySize,
- expired);
- if (!certList.contains(certInfo)) {
- continue;
- }
-
- String tsadigestalg = digestAlgorithm != DEFAULT
- ? digestAlgorithm
- : null;
+ List<String> tsaDigestAlgs = digestAlgs();
+ // no point in specifying a tsa digest algorithm
+ // for no TSA, except maybe it would issue a warning.
+ if (tsaUrl == null) tsaDigestAlgs = Arrays.asList(DEFAULT);
+ // If the JDK doesn't support option -tsadigestalg, the
+ // associated cases can just be ignored.
+ if (!signerInfo.supportsTsadigestalg) {
+ tsaDigestAlgs = Arrays.asList(DEFAULT);
+ }
+ for (String tsaDigestAlg : tsaDigestAlgs) {
+ if (DEFAULT.equals(tsaDigestAlg)) {
+ tsaDigestAlg = null;
+ } else if (!tsaInfo.isDigestSupported(tsaDigestAlg)) {
+ // It has to ignore the digest algorithm, which
+ // is not supported by the TSA server.
+ continue;
+ }
- for (TsaInfo tsaInfo : tsaList) {
- // It has to ignore the digest algorithm, which
- // is not supported by the TSA server.
- if(!tsaInfo.isDigestSupported(tsadigestalg)) {
- continue;
- }
+ if (tsaUrl != null && TsaFilter.filter(
+ signerInfo.version,
+ tsaDigestAlg,
+ expired,
+ tsaInfo.index)) {
+ continue;
+ }
- String tsaUrl = tsaInfo.tsaUrl;
- if (TsaFilter.filter(
- signerInfo.version,
- digestAlgorithm,
- expired,
- tsaInfo.index)) {
- tsaUrl = null;
- }
+ for (SignItem prevSign : unsignedJars) {
+ String unsignedJar = prevSign.signedJar;
- String signedJar = "JDK_"
- + signerInfo.version + "-CERT_"
- + certInfo
- + (tsaUrl == null
- ? ""
- : "-TSA_" + tsaInfo.index);
-
- // It has to ignore the same jar signing.
- if (!signFilter.add(signedJar)) {
- continue;
- }
-
- SignItem signItem = SignItem.build()
- .certInfo(certInfo)
- .version(signerInfo.version)
- .signatureAlgorithm(sigalg)
- .tsaDigestAlgorithm(
- tsaUrl == null
- ? null
- : tsadigestalg)
- .tsaIndex(
- tsaUrl == null
- ? -1
- : tsaInfo.index)
- .signedJar(signedJar);
- String signingId = signingId(signItem);
- detailsOutput.writeAnchorName(signingId,
- "Signing: " + signingId);
+ SignItem signItem = SignItem.build(prevSign)
+ .certInfo(certInfo)
+ .jdkInfo(signerInfo);
+ String signedJar = unsignedJar + "-" + "JDK_" + (
+ signerInfo.version + "-CERT_" + certInfo).
+ replaceAll("[^a-z_0-9A-Z.]+", "-");
- OutputAnalyzer signOA = signJar(
- signerInfo.jarsignerPath,
- sigalg,
- tsadigestalg,
- tsaUrl,
- certInfo.alias(),
- signedJar);
- Status signingStatus = signingStatus(signOA);
- signItem.status(signingStatus);
+ if (jarDigestAlgorithm != null) {
+ signedJar += "-DIGESTALG_" + jarDigestAlgorithm;
+ signItem.digestAlgorithm(jarDigestAlgorithm);
+ }
+ if (tsaUrl == null) {
+ signItem.tsaIndex(-1);
+ } else {
+ signedJar += "-TSA_" + tsaInfo.index;
+ signItem.tsaIndex(tsaInfo.index);
+ if (tsaDigestAlg != null) {
+ signedJar += "-TSADIGALG_" + tsaDigestAlg;
+ signItem.tsaDigestAlgorithm(tsaDigestAlg);
+ }
+ }
+ signItem.signedJar(signedJar);
- if (signingStatus != Status.ERROR) {
- // Using the testing JDK, which is specified
- // by jtreg option "-jdk", to verify the
- // signed jar and extract the signature
- // algorithm and timestamp digest algorithm.
- String output = verifyJar(TEST_JARSIGNER,
- signedJar).getOutput();
- signItem.extractedSignatureAlgorithm(
- extract(output,
- " *Signature algorithm.*",
- ".*: |,.*"));
- signItem.extractedTsaDigestAlgorithm(
- extract(output,
- " *Timestamp digest algorithm.*",
- ".*: "));
- }
+ String signingId = signingId(signItem);
+ detailsOutput.writeAnchorName(signingId,
+ "Signing: " + signingId);
- signItems.add(signItem);
- }
+ OutputAnalyzer signOA = signJar(
+ signerInfo.jarsignerPath,
+ certInfo.sigalg(),
+ jarDigestAlgorithm,
+ tsaDigestAlg,
+ tsaUrl,
+ certInfo.alias(),
+ unsignedJar,
+ signedJar);
+ Status signingStatus = signingStatus(signOA,
+ tsaUrl != null);
+ signItem.status(signingStatus);
+ signItems.add(signItem);
}
}
}
@@ -535,94 +688,164 @@
return signItems;
}
+ private static List<SignItem> updating(List<SignItem> prevSignItems)
+ throws IOException {
+ List<SignItem> updateItems = new ArrayList<>();
+ for (SignItem prevSign : prevSignItems) {
+ updateItems.addAll(updateJar(prevSign));
+ }
+ return updateItems;
+ }
+
private static void verifying(SignItem signItem, VerifyItem verifyItem)
throws Throwable {
- boolean delayVerify = verifyItem.status == Status.NONE;
- String verifyingId = verifyingId(signItem, verifyItem, !delayVerify);
+ // TODO: how will be ensured that the first verification is not after valid period expired which is only one minute?
+ boolean delayVerify = verifyItem.status != Status.NONE;
+ String verifyingId = verifyingId(signItem, verifyItem, delayVerify);
detailsOutput.writeAnchorName(verifyingId, "Verifying: " + verifyingId);
-
OutputAnalyzer verifyOA = verifyJar(verifyItem.jdkInfo.jarsignerPath,
- signItem.signedJar);
- Status verifyingStatus = verifyingStatus(verifyOA);
+ signItem.signedJar, verifyItem.certInfo == null ? null :
+ verifyItem.certInfo.alias());
+ Status verifyingStatus = verifyingStatus(signItem, verifyItem, verifyOA);
- // It checks if the default timestamp digest algorithm is SHA-256.
- if (verifyingStatus != Status.ERROR
- && signItem.tsaDigestAlgorithm == null) {
- verifyingStatus = signItem.extractedTsaDigestAlgorithm != null
- && !signItem.extractedTsaDigestAlgorithm.matches("SHA-?256")
- ? Status.ERROR
- : verifyingStatus;
- if (verifyingStatus == Status.ERROR) {
- System.out.println("The default tsa digest is not SHA-256: "
- + signItem.extractedTsaDigestAlgorithm);
- }
+ try {
+ String match = "^ ("
+ + " Signature algorithm: " + signItem.certInfo.
+ expectedSigalg() + ", " + signItem.certInfo.
+ expectedKeySize() + "-bit key"
+ + ")|("
+ + " Digest algorithm: " + signItem.expectedDigestAlg()
+ + (signItem.tsaIndex < 0 ? "" :
+ ")|("
+ + "Timestamped by \".+\" on .*"
+ + ")|("
+ + " Timestamp digest algorithm: "
+ + signItem.expectedTsaDigestAlg()
+ + ")|("
+ + " Timestamp signature algorithm: .*"
+ )
+ + ")$";
+ verifyOA.stdoutShouldMatchByLine(
+ "^- Signed by \"CN=" + signItem.certInfo.toString()
+ .replaceAll("[.]", "[.]") + "\"$",
+ "^(- Signed by \"CN=.+\")?$",
+ match);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ verifyingStatus = Status.ERROR;
}
- if (delayVerify) {
- signItem.addVerifyItem(verifyItem.status(verifyingStatus));
+ if (!delayVerify) {
+ verifyItem.status(verifyingStatus);
} else {
verifyItem.delayStatus(verifyingStatus);
}
+
+ if (verifyItem.prevVerify != null) {
+ verifying(signItem, verifyItem.prevVerify);
+ }
}
- // Return key sizes according to the specified key algorithm.
- private static int[] keySizes(String keyAlgorithm) {
- if (keyAlgorithm == RSA || keyAlgorithm == DSA) {
- return new int[] { 1024, 2048, 0 };
- } else if (keyAlgorithm == EC) {
- return new int[] { 384, 571, 0 };
+ // Determines the status of signing.
+ private static Status signingStatus(OutputAnalyzer outputAnalyzer,
+ boolean tsa) {
+ if (outputAnalyzer.getExitValue() != 0) {
+ return Status.ERROR;
+ }
+ if (!outputAnalyzer.getOutput().contains(Test.JAR_SIGNED)) {
+ return Status.ERROR;
}
- return null;
- }
-
- // Determines the status of signing.
- private static Status signingStatus(OutputAnalyzer outputAnalyzer) {
- if (outputAnalyzer.getExitValue() == 0) {
- if (outputAnalyzer.getOutput().contains(Test.WARNING)) {
- return Status.WARNING;
- } else {
- return Status.NORMAL;
- }
- } else {
- return Status.ERROR;
+ boolean warning = false;
+ for (String line : outputAnalyzer.getOutput().lines()
+ .toArray(String[]::new)) {
+ if (line.matches(Test.ERROR + " ?")) return Status.ERROR;
+ if (line.matches(Test.WARNING + " ?")) warning = true;
}
+ return warning ? Status.WARNING : Status.NORMAL;
}
// Determines the status of verifying.
- private static Status verifyingStatus(OutputAnalyzer outputAnalyzer) {
- if (outputAnalyzer.getExitValue() == 0) {
- String output = outputAnalyzer.getOutput();
- if (!output.contains(Test.JAR_VERIFIED)) {
- return Status.ERROR;
- } else if (output.contains(Test.WARNING)) {
- return Status.WARNING;
- } else {
- return Status.NORMAL;
+ private static Status verifyingStatus(SignItem signItem, VerifyItem
+ verifyItem, OutputAnalyzer outputAnalyzer) {
+ List<String> expectedSignedContent = new ArrayList<>();
+ if (verifyItem.certInfo == null) {
+ expectedSignedContent.addAll(signItem.jarContents);
+ } else {
+ SignItem i = signItem;
+ while (i != null) {
+ if (i.certInfo != null && i.certInfo.equals(verifyItem.certInfo)) {
+ expectedSignedContent.addAll(i.jarContents);
+ }
+ i = i.prevSign;
}
- } else {
+ }
+ List<String> expectedUnsignedContent =
+ new ArrayList<>(signItem.jarContents);
+ expectedUnsignedContent.removeAll(expectedSignedContent);
+
+ int expectedExitCode = !STRICT || expectedUnsignedContent.isEmpty() ? 0 : 32;
+ if (outputAnalyzer.getExitValue() != expectedExitCode) {
+ System.out.println("verifyingStatus: error: exit code != " + expectedExitCode + ": " + outputAnalyzer.getExitValue() + " != " + expectedExitCode);
+ return Status.ERROR;
+ }
+ String expectedSuccessMessage = expectedUnsignedContent.isEmpty() ?
+ Test.JAR_VERIFIED : Test.JAR_VERIFIED_WITH_SIGNER_ERRORS;
+ if (!outputAnalyzer.getOutput().contains(expectedSuccessMessage)) {
+ System.out.println("verifyingStatus: error: expectedSuccessMessage not found: " + expectedSuccessMessage);
return Status.ERROR;
}
- }
- // Extracts string from text by specified patterns.
- private static String extract(String text, String linePattern,
- String replacePattern) {
- Matcher lineMatcher = Pattern.compile(linePattern).matcher(text);
- if (lineMatcher.find()) {
- String line = lineMatcher.group(0);
- return line.replaceAll(replacePattern, "");
- } else {
- return null;
+ boolean tsa = signItem.tsaIndex >= 0;
+ boolean warning = false;
+ for (String line : outputAnalyzer.getOutput().lines()
+ .toArray(String[]::new)) {
+ if (line.isBlank()) continue;
+ if (Test.JAR_VERIFIED.equals(line)) continue;
+ if (line.matches(Test.ERROR + " ?") && expectedExitCode == 0) {
+ System.out.println("verifyingStatus: error: line.matches(" + Test.ERROR + "\" ?\"): " + line);
+ return Status.ERROR;
+ }
+ if (line.matches(Test.WARNING + " ?")) {
+ warning = true;
+ continue;
+ }
+ if (!warning) continue;
+ line = line.strip();
+ if (Test.NOT_YET_VALID_CERT_SIGNING_WARNING.equals(line)) continue;
+ if (Test.HAS_EXPIRING_CERT_SIGNING_WARNING.equals(line)) continue;
+ if (Test.HAS_EXPIRING_CERT_VERIFYING_WARNING.equals(line)) continue;
+ if (line.matches("^" + Test.NO_TIMESTAMP_SIGNING_WARN_TEMPLATE
+ .replaceAll(
+ "\\(%1\\$tY-%1\\$tm-%1\\$td\\)", "\\\\([^\\\\)]+\\\\)"
+ + "( or after any future revocation date)?")
+ .replaceAll("[.]", "[.]") + "$") && !tsa) continue;
+ if (line.matches("^" + Test.NO_TIMESTAMP_VERIFYING_WARN_TEMPLATE
+ .replaceAll("\\(as early as %1\\$tY-%1\\$tm-%1\\$td\\)",
+ "\\\\([^\\\\)]+\\\\)"
+ + "( or after any future revocation date)?")
+ .replaceAll("[.]", "[.]") + "$") && !tsa) continue;
+ if (line.matches("^This jar contains signatures that do(es)? not "
+ + "include a timestamp[.] Without a timestamp, users may "
+ + "not be able to validate this jar after the signer "
+ + "certificate's expiration date \\([^\\)]+\\) or after "
+ + "any future revocation date[.]") && !tsa) continue;
+ if (Test.CERTIFICATE_SELF_SIGNED.equals(line)) continue;
+ if (Test.HAS_EXPIRED_CERT_VERIFYING_WARNING.equals(line)
+ && signItem.certInfo.expired) continue;
+ System.out.println("verifyingStatus: unexpected line: " + line);
+ return Status.ERROR; // treat unexpected warnings as error
}
+ return warning ? Status.WARNING : Status.NORMAL;
}
// Using specified jarsigner to sign the pre-created jar with specified
// algorithms.
private static OutputAnalyzer signJar(String jarsignerPath, String sigalg,
- String tsadigestalg, String tsa, String alias, String signedJar)
- throws Throwable {
- List<String> arguments = new ArrayList<String>();
+ String jarDigestAlgorithm,
+ String tsadigestalg, String tsa, String alias, String unsignedJar,
+ String signedJar) throws Throwable {
+ List<String> arguments = new ArrayList<>();
if (PROXY_HOST != null && PROXY_PORT != null) {
arguments.add("-J-Dhttp.proxyHost=" + PROXY_HOST);
@@ -633,6 +856,10 @@
arguments.add("-J-Djava.security.properties=" + JAVA_SECURITY);
arguments.add("-debug");
arguments.add("-verbose");
+ if (jarDigestAlgorithm != null) {
+ arguments.add("-digestalg");
+ arguments.add(jarDigestAlgorithm);
+ }
if (sigalg != null) {
arguments.add("-sigalg");
arguments.add(sigalg);
@@ -649,28 +876,34 @@
arguments.add(KEYSTORE);
arguments.add("-storepass");
arguments.add(PASSWORD);
+ arguments.add("-sigfile");
+ arguments.add(nextSigfileName(alias, unsignedJar, signedJar));
arguments.add("-signedjar");
arguments.add(signedJar + ".jar");
- arguments.add(TEST_JAR_NAME);
+ arguments.add(unsignedJar + ".jar");
arguments.add(alias);
- OutputAnalyzer outputAnalyzer = execTool(
- jarsignerPath,
+ OutputAnalyzer outputAnalyzer = execTool(jarsignerPath,
arguments.toArray(new String[arguments.size()]));
return outputAnalyzer;
}
// Using specified jarsigner to verify the signed jar.
private static OutputAnalyzer verifyJar(String jarsignerPath,
- String signedJar) throws Throwable {
- OutputAnalyzer outputAnalyzer = execTool(
- jarsignerPath,
- "-J-Djava.security.properties=" + JAVA_SECURITY,
- "-debug",
- "-verbose",
- "-certs",
- "-keystore", KEYSTORE,
- "-verify", signedJar + ".jar");
+ String signedJar, String alias) throws Throwable {
+ List<String> arguments = new ArrayList<>();
+ arguments.add("-J-Djava.security.properties=" + JAVA_SECURITY);
+ arguments.add("-debug");
+ arguments.add("-verbose");
+ arguments.add("-certs");
+ arguments.add("-keystore");
+ arguments.add(KEYSTORE);
+ arguments.add("-verify");
+ if (STRICT) arguments.add("-strict");
+ arguments.add(signedJar + ".jar");
+ if (alias != null) arguments.add(alias);
+ OutputAnalyzer outputAnalyzer = execTool(jarsignerPath,
+ arguments.toArray(new String[arguments.size()]));
return outputAnalyzer;
}
@@ -686,20 +919,24 @@
report.append("TSA list:\n");
for(TsaInfo tsaInfo : tsaList) {
report.append(
- String.format("%d=%s%n", tsaInfo.index, tsaInfo.tsaUrl));
+ String.format("%d=%s%n", tsaInfo.index,
+ tsaInfo.tsaUrl == null ? "notsa" : tsaInfo.tsaUrl));
}
report.append(HtmlHelper.endPre());
report.append(HtmlHelper.startTable());
// Generates report headers.
- List<String> headers = new ArrayList<String>();
- headers.add("[Certificate]");
+ List<String> headers = new ArrayList<>();
+ headers.add("[Jarfile]");
+ headers.add("[Signing Certificate]");
headers.add("[Signer JDK]");
headers.add("[Signature Algorithm]");
- headers.add("[TSA Digest]");
+ headers.add("[Jar Digest Algorithm]");
+ headers.add("[TSA Digest Algorithm]");
headers.add("[TSA]");
headers.add("[Signing Status]");
headers.add("[Verifier JDK]");
+ headers.add("[Verifying Certificate]");
headers.add("[Verifying Status]");
if (DELAY_VERIFY) {
headers.add("[Delay Verifying Status]");
@@ -710,10 +947,11 @@
StringBuilder failedReport = new StringBuilder(report.toString());
- boolean failed = false;
+ boolean failed = signItems.isEmpty();
// Generates report rows.
for (SignItem signItem : signItems) {
+ failed = failed || signItem.verifyItems.isEmpty();
for (VerifyItem verifyItem : signItem.verifyItems) {
String reportRow = reportRow(signItem, verifyItem);
report.append(reportRow);
@@ -767,13 +1005,21 @@
// ensures the output is in US English.
private static OutputAnalyzer execTool(String toolPath, String... args)
throws Throwable {
- String[] cmd = new String[args.length + 4];
- cmd[0] = toolPath;
- cmd[1] = "-J-Duser.language=en";
- cmd[2] = "-J-Duser.country=US";
- cmd[3] = "-J-Djava.security.egd=file:/dev/./urandom";
- System.arraycopy(args, 0, cmd, 4, args.length);
- return ProcessTools.executeCommand(cmd);
+ long start = System.currentTimeMillis();
+ try {
+
+ String[] cmd = new String[args.length + 4];
+ cmd[0] = toolPath;
+ cmd[1] = "-J-Duser.language=en";
+ cmd[2] = "-J-Duser.country=US";
+ cmd[3] = "-J-Djava.security.egd=file:/dev/./urandom";
+ System.arraycopy(args, 0, cmd, 4, args.length);
+ return ProcessTools.executeCommand(cmd);
+
+ } finally {
+ long end = System.currentTimeMillis();
+ System.out.println("child process duration [ms]: " + (end - start));
+ }
}
private static class JdkInfo {
@@ -781,17 +1027,20 @@
private final String jdkPath;
private final String jarsignerPath;
private final String version;
+ private final int majorVersion;
private final boolean supportsTsadigestalg;
- private Map<String, Boolean> sigalgMap = new HashMap<String, Boolean>();
+ private Map<String, Boolean> sigalgMap = new HashMap<>();
private JdkInfo(String jdkPath) throws Throwable {
this.jdkPath = jdkPath;
version = execJdkUtils(jdkPath, JdkUtils.M_JAVA_RUNTIME_VERSION);
- if (version == null || version.trim().isEmpty()) {
+ if (version == null || version.isBlank()) {
throw new RuntimeException(
"Cannot determine the JDK version: " + jdkPath);
}
+ majorVersion = Integer.parseInt((version.matches("^1[.].*") ?
+ version.substring(2) : version).replaceAll("[^0-9].*$", ""));
jarsignerPath = jarsignerPath(jdkPath);
supportsTsadigestalg = execTool(jarsignerPath, "-help")
.getOutput().contains("-tsadigestalg");
@@ -799,7 +1048,7 @@
private boolean isSupportedSigalg(String sigalg) throws Throwable {
if (!sigalgMap.containsKey(sigalg)) {
- boolean isSupported = "true".equalsIgnoreCase(
+ boolean isSupported = Boolean.parseBoolean(
execJdkUtils(
jdkPath,
JdkUtils.M_IS_SUPPORTED_SIGALG,
@@ -810,8 +1059,13 @@
return sigalgMap.get(sigalg);
}
- private boolean isJdk6() {
- return version.startsWith("1.6");
+ private boolean isAtLeastMajorVersion(int minVersion) {
+ return majorVersion >= minVersion;
+ }
+
+ private boolean supportsKeyAlg(String keyAlgorithm) {
+ // JDK 6 doesn't support EC
+ return isAtLeastMajorVersion(6) || !EC.equals(keyAlgorithm);
}
@Override
@@ -839,13 +1093,18 @@
return false;
return true;
}
+
+ @Override
+ public String toString() {
+ return "JdkInfo[" + version + ", " + jdkPath + "]";
+ }
}
private static class TsaInfo {
private final int index;
private final String tsaUrl;
- private Set<String> digestList = new HashSet<String>();
+ private Set<String> digestList = new HashSet<>();
private TsaInfo(int index, String tsa) {
this.index = index;
@@ -853,51 +1112,75 @@
}
private void addDigest(String digest) {
- if (!ignore(digest)) {
- digestList.add(digest);
- }
- }
-
- private static boolean ignore(String digest) {
- return !SHA1.equalsIgnoreCase(digest)
- && !SHA256.equalsIgnoreCase(digest)
- && !SHA512.equalsIgnoreCase(digest);
+ digestList.add(digest);
}
private boolean isDigestSupported(String digest) {
return digest == null || digestList.isEmpty()
|| digestList.contains(digest);
}
+
+ @Override
+ public String toString() {
+ return "TsaInfo[" + index + ", " + tsaUrl + "]";
+ }
}
private static class CertInfo {
- private final String jdkVersion;
+ private static int certCounter;
+
+ // nr distinguishes cert CNs in jarsigner -verify output
+ private final int nr = ++certCounter;
+ private final JdkInfo jdkInfo;
private final String keyAlgorithm;
private final String digestAlgorithm;
private final int keySize;
private final boolean expired;
- private CertInfo(String jdkVersion, String keyAlgorithm,
+ private CertInfo(JdkInfo jdkInfo, String keyAlgorithm,
String digestAlgorithm, int keySize, boolean expired) {
- this.jdkVersion = jdkVersion;
+ this.jdkInfo = jdkInfo;
this.keyAlgorithm = keyAlgorithm;
this.digestAlgorithm = digestAlgorithm;
this.keySize = keySize;
this.expired = expired;
}
+ private String sigalg() {
+ return DEFAULT.equals(digestAlgorithm) ? null : expectedSigalg();
+ }
+
+ private String expectedSigalg() {
+ return (DEFAULT.equals(this.digestAlgorithm) ? this.digestAlgorithm
+ : "SHA-256").replace("-", "") + "with" +
+ keyAlgorithm + (EC.equals(keyAlgorithm) ? "DSA" : "");
+ }
+
+ private int expectedKeySize() {
+ if (keySize != 0) return keySize;
+
+ // defaults
+ if (RSA.equals(keyAlgorithm) || DSA.equals(keyAlgorithm)) {
+ return 2048;
+ } else if (EC.equals(keyAlgorithm)) {
+ return 256;
+ } else {
+ throw new RuntimeException("problem determining key size");
+ }
+ }
+
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
- + ((digestAlgorithm == null) ? 0 : digestAlgorithm.hashCode());
+ + (digestAlgorithm == null ? 0 : digestAlgorithm.hashCode());
result = prime * result + (expired ? 1231 : 1237);
result = prime * result
- + ((jdkVersion == null) ? 0 : jdkVersion.hashCode());
+ + (jdkInfo == null ? 0 : jdkInfo.hashCode());
result = prime * result
- + ((keyAlgorithm == null) ? 0 : keyAlgorithm.hashCode());
+ + (keyAlgorithm == null ? 0 : keyAlgorithm.hashCode());
result = prime * result + keySize;
return result;
}
@@ -918,10 +1201,10 @@
return false;
if (expired != other.expired)
return false;
- if (jdkVersion == null) {
- if (other.jdkVersion != null)
+ if (jdkInfo == null) {
+ if (other.jdkInfo != null)
return false;
- } else if (!jdkVersion.equals(other.jdkVersion))
+ } else if (!jdkInfo.equals(other.jdkInfo))
return false;
if (keyAlgorithm == null) {
if (other.keyAlgorithm != null)
@@ -934,12 +1217,16 @@
}
private String alias() {
- return jdkVersion + "_" + toString();
+ return (jdkInfo.version + "_" + toString())
+ // lower case for jks due to
+ // sun.security.provider.JavaKeyStore.JDK.convertAlias
+ .toLowerCase(Locale.ENGLISH);
}
@Override
public String toString() {
- return keyAlgorithm + "_" + digestAlgorithm
+ return "nr" + nr + "_"
+ + keyAlgorithm + "_" + digestAlgorithm
+ (keySize == 0 ? "" : "_" + keySize)
+ (expired ? "_Expired" : "");
}
@@ -949,7 +1236,7 @@
// TSA service with an arbitrary valid/expired certificate.
private static class TsaFilter {
- private static final Set<Condition> SET = new HashSet<Condition>();
+ private static final Set<Condition> SET = new HashSet<>();
private static boolean filter(String signerVersion,
String digestAlgorithm, boolean expiredCert, int tsaIndex) {
@@ -1029,22 +1316,32 @@
private static class SignItem {
+ private SignItem prevSign;
private CertInfo certInfo;
- private String version;
- private String signatureAlgorithm;
- // Signature algorithm that is extracted from verification output.
- private String extractedSignatureAlgorithm;
+ private JdkInfo jdkInfo;
+ private String digestAlgorithm;
private String tsaDigestAlgorithm;
- // TSA digest algorithm that is extracted from verification output.
- private String extractedTsaDigestAlgorithm;
private int tsaIndex;
private Status status;
+ private String unsignedJar;
private String signedJar;
+ private List<String> jarContents = new ArrayList<>();
- private List<VerifyItem> verifyItems = new ArrayList<VerifyItem>();
+ private List<VerifyItem> verifyItems = new ArrayList<>();
private static SignItem build() {
- return new SignItem();
+ return new SignItem()
+ .addContentFiles(Arrays.asList("META-INF/MANIFEST.MF"));
+ }
+
+ private static SignItem build(SignItem prevSign) {
+ return build().prevSign(prevSign).unsignedJar(prevSign.signedJar)
+ .addContentFiles(prevSign.jarContents);
+ }
+
+ private SignItem prevSign(SignItem prevSign) {
+ this.prevSign = prevSign;
+ return this;
}
private SignItem certInfo(CertInfo certInfo) {
@@ -1052,20 +1349,18 @@
return this;
}
- private SignItem version(String version) {
- this.version = version;
+ private SignItem jdkInfo(JdkInfo jdkInfo) {
+ this.jdkInfo = jdkInfo;
return this;
}
- private SignItem signatureAlgorithm(String signatureAlgorithm) {
- this.signatureAlgorithm = signatureAlgorithm;
+ private SignItem digestAlgorithm(String digestAlgorithm) {
+ this.digestAlgorithm = digestAlgorithm;
return this;
}
- private SignItem extractedSignatureAlgorithm(
- String extractedSignatureAlgorithm) {
- this.extractedSignatureAlgorithm = extractedSignatureAlgorithm;
- return this;
+ String expectedDigestAlg() {
+ return digestAlgorithm != null ? digestAlgorithm : "SHA-256";
}
private SignItem tsaDigestAlgorithm(String tsaDigestAlgorithm) {
@@ -1073,10 +1368,8 @@
return this;
}
- private SignItem extractedTsaDigestAlgorithm(
- String extractedTsaDigestAlgorithm) {
- this.extractedTsaDigestAlgorithm = extractedTsaDigestAlgorithm;
- return this;
+ String expectedTsaDigestAlg() {
+ return tsaDigestAlgorithm != null ? tsaDigestAlgorithm : "SHA-256";
}
private SignItem tsaIndex(int tsaIndex) {
@@ -1089,18 +1382,49 @@
return this;
}
+ private SignItem unsignedJar(String unsignedJar) {
+ this.unsignedJar = unsignedJar;
+ return this;
+ }
+
private SignItem signedJar(String signedJar) {
this.signedJar = signedJar;
return this;
}
+ private SignItem addContentFiles(List<String> files) {
+ this.jarContents.addAll(files);
+ return this;
+ }
+
private void addVerifyItem(VerifyItem verifyItem) {
verifyItems.add(verifyItem);
}
+
+ private boolean isErrorInclPrev() {
+ if (prevSign != null && prevSign.isErrorInclPrev()) {
+ System.out.println("SignItem.isErrorInclPrev: returning true from previous");
+ return true;
+ }
+
+ return status == Status.ERROR;
+ }
+ private List<String> toStringWithPrev(Function<SignItem,String> toStr) {
+ List<String> s = new ArrayList<>();
+ if (prevSign != null) {
+ s.addAll(prevSign.toStringWithPrev(toStr));
+ }
+ if (status != null) { // no status means jar creation or update item
+ s.add(toStr.apply(this));
+ }
+ return s;
+ }
}
private static class VerifyItem {
+ private VerifyItem prevVerify;
+ private CertInfo certInfo;
private JdkInfo jdkInfo;
private Status status = Status.NONE;
private Status delayStatus = Status.NONE;
@@ -1111,15 +1435,54 @@
return verifyItem;
}
+ private VerifyItem certInfo(CertInfo certInfo) {
+ this.certInfo = certInfo;
+ return this;
+ }
+
+ private void addSignerCertInfos(SignItem signItem) {
+ VerifyItem prevVerify = this;
+ CertInfo lastCertInfo = null;
+ while (signItem != null) {
+ // (signItem.certInfo == null) means create or update jar step
+ if (signItem.certInfo != null
+ && !signItem.certInfo.equals(lastCertInfo)) {
+ lastCertInfo = signItem.certInfo;
+ prevVerify = prevVerify.prevVerify =
+ build(jdkInfo).certInfo(signItem.certInfo);
+ }
+ signItem = signItem.prevSign;
+ }
+ }
+
private VerifyItem status(Status status) {
this.status = status;
return this;
}
+ private boolean isErrorInclPrev() {
+ if (prevVerify != null && prevVerify.isErrorInclPrev()) {
+ System.out.println("VerifyItem.isErrorInclPrev: returning true from previous");
+ return true;
+ }
+
+ return status == Status.ERROR || delayStatus == Status.ERROR;
+ }
+
private VerifyItem delayStatus(Status status) {
this.delayStatus = status;
return this;
}
+
+ private List<String> toStringWithPrev(
+ Function<VerifyItem,String> toStr) {
+ List<String> s = new ArrayList<>();
+ if (prevVerify != null) {
+ s.addAll(prevVerify.toStringWithPrev(toStr));
+ }
+ s.add(toStr.apply(this));
+ return s;
+ }
}
// The identifier for a specific signing.
@@ -1130,32 +1493,41 @@
// The identifier for a specific verifying.
private static String verifyingId(SignItem signItem, VerifyItem verifyItem,
boolean delayVerify) {
- return "S_" + signingId(signItem) + "-" + (delayVerify ? "DV" : "V")
- + "_" + verifyItem.jdkInfo.version;
+ return signingId(signItem) + (delayVerify ? "-DV" : "-V")
+ + "_" + verifyItem.jdkInfo.version +
+ (verifyItem.certInfo == null ? "" : "_" + verifyItem.certInfo);
}
private static String reportRow(SignItem signItem, VerifyItem verifyItem) {
- List<String> values = new ArrayList<String>();
- values.add(signItem.certInfo.toString());
- values.add(signItem.version);
- values.add(null2Default(signItem.signatureAlgorithm,
- signItem.extractedSignatureAlgorithm));
- values.add(signItem.tsaIndex == -1
- ? ""
- : null2Default(signItem.tsaDigestAlgorithm,
- signItem.extractedTsaDigestAlgorithm));
- values.add(signItem.tsaIndex == -1 ? "" : signItem.tsaIndex + "");
- values.add(HtmlHelper.anchorLink(
+ List<String> values = new ArrayList<>();
+ Consumer<Function<SignItem, String>> s_values_add = f -> {
+ values.add(String.join("<br/><br/>", signItem.toStringWithPrev(f)));
+ };
+ Consumer<Function<VerifyItem, String>> v_values_add = f -> {
+ values.add(String.join("<br/><br/>", verifyItem.toStringWithPrev(f)));
+ };
+ s_values_add.accept(i -> i.unsignedJar + " -> " + i.signedJar);
+ s_values_add.accept(i -> i.certInfo.toString());
+ s_values_add.accept(i -> i.jdkInfo.version);
+ s_values_add.accept(i -> i.certInfo.expectedSigalg());
+ s_values_add.accept(i ->
+ null2Default(i.digestAlgorithm, i.expectedDigestAlg()));
+ s_values_add.accept(i -> i.tsaIndex == -1 ? "" :
+ null2Default(i.tsaDigestAlgorithm, i.expectedTsaDigestAlg()));
+ s_values_add.accept(i -> i.tsaIndex == -1 ? "" : i.tsaIndex + "");
+ s_values_add.accept(i -> HtmlHelper.anchorLink(
PhaseOutputStream.fileName(PhaseOutputStream.Phase.SIGNING),
- signingId(signItem),
- signItem.status.toString()));
+ signingId(i),
+ "" + i.status));
values.add(verifyItem.jdkInfo.version);
- values.add(HtmlHelper.anchorLink(
+ v_values_add.accept(i ->
+ i.certInfo == null ? "no alias" : "" + i.certInfo);
+ v_values_add.accept(i -> HtmlHelper.anchorLink(
PhaseOutputStream.fileName(PhaseOutputStream.Phase.VERIFYING),
- verifyingId(signItem, verifyItem, false),
- verifyItem.status.toString()));
+ verifyingId(signItem, i, false),
+ "" + i.status.toString()));
if (DELAY_VERIFY) {
- values.add(HtmlHelper.anchorLink(
+ v_values_add.accept(i -> HtmlHelper.anchorLink(
PhaseOutputStream.fileName(
PhaseOutputStream.Phase.DELAY_VERIFYING),
verifyingId(signItem, verifyItem, true),
@@ -1165,19 +1537,54 @@
return HtmlHelper.htmlRow(values.toArray(new String[values.size()]));
}
- private static boolean isFailed(SignItem signItem,
- VerifyItem verifyItem) {
- return signItem.status == Status.ERROR
- || verifyItem.status == Status.ERROR
- || verifyItem.delayStatus == Status.ERROR;
+ private static boolean isFailed(SignItem signItem, VerifyItem verifyItem) {
+ System.out.println("isFailed: signItem = " + signItem + ", verifyItem = " + verifyItem);
+ // TODO: except known failing cases
+
+ // Note about isAtLeastMajorVersion in the following conditions:
+ // signItem.jdkInfo is the jdk which signed the jar last and
+ // signItem.prevSign.jdkInfo is the jdk which signed the jar first
+ // assuming only two successive signatures as there actually are now.
+ // the first signature always works and always has. subject here is
+ // the update of an already signed jar. the following conditions always
+ // depend on the second jdk that updated the jar with another signature
+ // and the first one (signItem(.prevSign)+.jdkInfo) can be ignored.
+ // this is different for verifyItem. verifyItem.prevVerify refers to
+ // the first signature created by signItem(.prevSign)+.jdkInfo.
+ // all verifyItem(.prevVerify)+.jdkInfo however point always to the same
+ // jdk, only their certInfo is different. the same signatures are
+ // verified with different jdks in different top-level VerifyItems
+ // attached directly to signItem.verifyItems and not to
+ // verifyItem.prevVerify.
+
+ // ManifestDigester fails to parse manifests ending in '\r' with
+ // IndexOutOfBoundsException at ManifestDigester.java:87 before 8217375
+ if (signItem.signedJar.startsWith("eofr")
+ && !signItem.jdkInfo.isAtLeastMajorVersion(13)
+ && !verifyItem.jdkInfo.isAtLeastMajorVersion(13)) return false;
+
+ // if there is no blank line after main attributes, JarSigner adds
+ // individual sections nevertheless without being properly delimited
+ // in JarSigner.java:777..790 without checking for blank line
+ // before 8217375
+// if (signItem.signedJar.startsWith("eofn-")
+// && signItem.signedJar.contains("-addfile-")
+// && !signItem.jdkInfo.isAtLeastMajorVersion(13)
+// && !verifyItem.jdkInfo.isAtLeastMajorVersion(13)) return false; // FIXME
+
+// System.out.println("isFailed: signItem.isErrorInclPrev() " + signItem.isErrorInclPrev());
+// System.out.println("isFailed: verifyItem.isErrorInclPrev() " + verifyItem.isErrorInclPrev());
+ boolean isFailed = signItem.isErrorInclPrev() || verifyItem.isErrorInclPrev();
+ System.out.println("isFailed: returning " + isFailed);
+ return isFailed;
}
// If a value is null, then displays the default value or N/A.
private static String null2Default(String value, String defaultValue) {
- return value == null
- ? DEFAULT + "(" + (defaultValue == null
+ return value != null ? value :
+ DEFAULT + "(" + (defaultValue == null
? "N/A"
- : defaultValue) + ")"
- : value;
+ : defaultValue) + ")";
}
+
}
--- a/test/jdk/sun/security/tools/jarsigner/compatibility/DetailsOutputStream.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sun/security/tools/jarsigner/compatibility/DetailsOutputStream.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -34,8 +34,9 @@
private PhaseOutputStream phaseOutputStream = new PhaseOutputStream();
- public DetailsOutputStream() throws FileNotFoundException {
- super("details.out", true);
+ public DetailsOutputStream(String filename) throws FileNotFoundException {
+ super(filename != null && !filename.isEmpty() ? filename :
+ "details.out", true);
}
public void transferPhase() throws IOException {
--- a/test/jdk/sun/security/tools/jarsigner/compatibility/HtmlHelper.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sun/security/tools/jarsigner/compatibility/HtmlHelper.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -60,7 +60,7 @@
}
public static String startTable() {
- return startTag("table " + STYLE);
+ return startTag("table border=\"1\" padding=\"1\" cellspacing=\"0\" " + STYLE);
}
public static String endTable() {
@@ -68,19 +68,19 @@
}
public static String startTr() {
- return startTag("tr");
+ return "\t" + startTag("tr") + "\n";
}
public static String endTr() {
- return endTag("tr");
+ return "\t" + endTag("tr") + "\n";
}
public static String startTd() {
- return startTag("td");
+ return "\t\t" + startTag("td");
}
public static String endTd() {
- return endTag("td");
+ return endTag("td") + "\n";
}
public static String startTag(String tag) {
@@ -92,7 +92,7 @@
}
public static String anchorName(String name, String text) {
- return "<a name=" + name + ">" + text + "</a>";
+ return "<a name=" + name + "><hr/>" + text + "</a>";
}
public static String anchorLink(String file, String anchorName,
--- a/test/jdk/sun/security/tools/jarsigner/compatibility/JdkUtils.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sun/security/tools/jarsigner/compatibility/JdkUtils.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -21,41 +21,59 @@
* questions.
*/
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
+import static java.util.Arrays.asList;
/*
* This class is used for returning some specific JDK information.
*/
public class JdkUtils {
+ private enum Alg {
+ KEY, SIG, DIGEST;
+ }
+
static final String M_JAVA_RUNTIME_VERSION = "javaRuntimeVersion";
+ static final String M_IS_SUPPORTED_KEYALG = "isSupportedKeyalg";
static final String M_IS_SUPPORTED_SIGALG = "isSupportedSigalg";
+ static final String M_IS_SUPPORTED_DIGESTALG = "isSupportedDigestalg";
// Returns the JDK build version.
static String javaRuntimeVersion() {
return System.getProperty("java.runtime.version");
}
- // Checks if the specified signature algorithm is supported by the JDK.
- static boolean isSupportedSigalg(String sigalg) {
- boolean isSupported = false;
+ // Checks if the specified algorithm is supported by the JDK.
+ static boolean isSupportedAlg(Alg algType, String algName) {
try {
- isSupported = Signature.getInstance(sigalg) != null;
+ switch (algType) {
+ case KEY:
+ return KeyPairGenerator.getInstance(algName) != null;
+ case SIG:
+ return Signature.getInstance(algName) != null;
+ case DIGEST:
+ return MessageDigest.getInstance(algName) != null;
+ }
} catch (NoSuchAlgorithmException e) { }
-
- if (!isSupported) {
- System.out.println(sigalg + " is not supported yet.");
- }
-
- return isSupported;
+ System.out.println(algName + " is not supported yet.");
+ return false;
}
public static void main(String[] args) {
if (M_JAVA_RUNTIME_VERSION.equals(args[0])) {
System.out.print(javaRuntimeVersion());
+ } else if (M_IS_SUPPORTED_KEYALG.equals(args[0])) {
+ System.out.print(isSupportedAlg(Alg.KEY, args[1]));
} else if (M_IS_SUPPORTED_SIGALG.equals(args[0])) {
- System.out.print(isSupportedSigalg(args[1]));
+ System.out.print(isSupportedAlg(Alg.SIG, args[1]));
+ } else if (M_IS_SUPPORTED_DIGESTALG.equals(args[0])) {
+ System.out.print(isSupportedAlg(Alg.DIGEST, args[1]));
+ } else {
+ throw new IllegalArgumentException("invalid: " + asList(args));
}
}
+
}
--- a/test/jdk/sun/security/tools/jarsigner/compatibility/README Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sun/security/tools/jarsigner/compatibility/README Thu Jul 25 08:24:01 2019 -0400
@@ -1,4 +1,4 @@
-# Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2017, 2019, 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
@@ -35,6 +35,10 @@
stdout and stderr to file JTwork/scratch/details.out.
##### Report Columns #####
+
+Jarfile
+ The filenames used in the tests
+
Certificate
Certificate identifier. The identifier consists of specific attributes of
the certificate. Generally, the naming convention is:
@@ -44,10 +48,23 @@
The JDK version that signs jar.
Signature Algorithm
- The signature algorithm used by signing.
+ The signature algorithm used to sign the key as in 'keytool -sigalg'.
+
+ Note: The values displayed in this column are specified to jarsigner only
+ in case a test does not work with a default value.
+ In any case the specified value or expected default value is compared in
+ verifying phase against jarsigner's output and the test fails if it does
+ not match.
-TSA Digest
- The timestamp digest algorithm used by signing.
+Jar Digest Algorithm
+ The digest algorithm used to digest the files contained in the JAR file and
+ the manifest and signature files as in 'jarsigner -digestalg'.
+ See also note above about default values for Signature Algorithm.
+
+TSA Digest Algorithm
+ The timestamp digest algorithm used by TSA as in 'jarsigner -tsadigestalg'.
+ Shows no value if no TSA used.
+ See also note above about default values for Signature Algorithm.
TSA
TSA URL index. All of TSA URLs and their indices can be found at the top
@@ -80,24 +97,38 @@
-jdk:<path/to/testing/JDK>
[-DproxyHost=<host> \
-DproxyPort=<port> \
+ -Dteeout=filename \
-DtsaListFile=</url/to/tsaListFile> \
- -DtsaList=</path/to/tsa1#/path/to/tsa2#/path/to/tsa3#...> \
+ -DtsaList=</path/to/tsa1#/path/to/tsa2#/path/to/tsa3#...|notsa> \
-DjdkListFile=</path/to/jdkListFile> \
-DjdkList=</path/to/jdk1#/path/to/jdk2#/path/to/jdk3#...> \
-DjavaSecurityFile=</path/to/java/security/properties/file> \
-DdelayVerify=<true|false> \
-DcertValidity=<[1, 1440]>] \
- <JDK_REPO>/jdk/test/sun/security/tools/jarsigner/compatibility/Compatibility.java
+ <JDK_REPO>/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java
Besides the common jtreg options, like -jdk, this test introduces a set of
properties for receiving users' inputs and making the test more flexible. These
-properties are:
+properties are (all to specify after -D as system properties):
+
proxyHost=<host>
This property indicates proxy host.
proxyPort=<port>
This property indicates proxy port. The default value is 80.
+o=filename
+ Redirects a copy of what is written to stdout into the specified file which
+ allows for observing progress or problems during a longer running test.
+ (Compatibility test replaces System.out to collect the output but even if
+ it didn't jtreg would not print anything until the test would have ended.)
+ Note that relative paths resolve relatively to some temporary working
+ directory created by jtreg. Defaults to JTwork/scratch/details.out.
+ The specified file is not deleted or emptied and the output is appended
+ though jtreg deletes the default file with the whole directory if this
+ option is not specified or the file point into JTwork/scratch.
+ Example (Bash style): tail -F log & jtreg ... -Do=$(pwd)/log ...
+
tsaListFile=</path/to/tsaListFile>
This property indicates a local file, which contains a set of TSA URLs and
the supported digest algorithms (by optional parameter digests). The format
@@ -112,11 +143,12 @@
on SHA-1, SHA-256 and SHA-512. So, if other digest algorithms, like SHA-224
and SHA-384, are listed, they just be ignored.
-tsaList=</path/to/tsa1#/path/to/tsa2;digests=SHA-1,SHA-256#...>
+tsaList=</path/to/tsa1#/path/to/tsa2;digests=SHA-1,SHA-256#...|notsa>
This property directly lists a set of TSAs in command. "#" is the delimiter.
Note that, if both of tsaListFile and tsaList are specified, only property
- jdkListFile is selected. If neither of tsaListFile and tsaList is specified,
- the test will fails immediately.
+ tsaListFile is selected. If neither of tsaListFile and tsaList is specified,
+ the test fails immediately.
+ If tsaList has a value of "notsa", no tsa is used.
jdkListFile=</path/to/jdkListFile>
This property indicates a local file, which contains a set of local JDK
@@ -129,11 +161,42 @@
jdkList=</path/to/jdk1#/path/to/jdk2#/path/to/jdk3#...>
This property directly lists a set of local JDK paths in command. "#" is
the delimiter.
+ An element "TEST_JDK" as in
+jdkList=</path/to/jdk1#/path/to/jdk2#TEST_JDK#...>
+ adds the testing JDK, which is specified by jtreg option -jdk, to the jdk
+ list. All signed jars are verified with the current testing JDK, which is
+ specified by jtreg option -jdk, by default in addition to the JDKs given
+ in jdkList but it is not used to also sign jars by default.
+ If neither jdkList nor jdkListFile are specified, the current testing JDK,
+ which is specified by jtreg option -jdk, is used to sign the jars, like:
+jdkList=TEST_JDK
Note that, if both of jdkListFile and jdkList are specified, only property
jdkListFile is selected. If neither of jdkListFile nor jdkList is specified,
- the testing JDK, which is specified by jtreg option -jdk will be used as
+ the testing JDK, which is specified by jtreg option -jdk, will be used as
the only one JDK in the JDK list.
+ The testing JDK, which is specified by jtreg option "-jdk", should include
+ the fix for JDK-8163304. Otherwise, the signature algorithm and timestamp
+ digest algorithm cannot be extracted from verification output. And this JDK
+ should support as many as possible signature algorithms. Anyway the latest
+ JDK build is always recommended.
+
+testComprehensiveJarContents=<false|true>
+ If false, all tests are executed with only one typical JAR file. Otherwise,
+ if true, a whole bunch of JAR files with several edge case contents are
+ fed through the tests such as empty manifest or manifests with non-default
+ line breaks. Default is false.
+
+testJarUpdate=<false|true>
+ If false, all tested JAR files are signed with one JDK and verified with
+ each JDK, same or other. If true, in addition, all JAR files are modified
+ after having been signed, and are then each signed again with each JDK and
+ verified each JDK, same or other. Default is false.
+
+strict=<false|true>
+ If true, '-strict' option is specified to jarsigner along with '-verify'.
+ Default is false.
+
javaSecurityFile=</path/to/java/security/properties/file>
This property indicates an alternative java security properties file. The
default file is the path of file java.scurity that is distributed with
@@ -143,16 +206,36 @@
This property indicates if doing an additional verifying after all of valid
certificates expire. The default value is false.
+expired=<false|true>
+ This property indicates whether or not all tests should be repeated with an
+ expired certificate. Refers to the certificate validity period and not to
+ TSA. The default value is true.
+
certValidity=<[1, 1440]>
This property indicates the remaining validity period in minutes for valid
certificates. The value range is [1, 1440]. The default value is 1440.
Note that, if delayVerify is false, this property doesn't take effect.
-The testing JDK, which is specified by jtreg option "-jdk", should include the
-fix for JDK-8163304. Otherwise, the signature algorithm and timestamp digest
-algorithm cannot be extracted from verification output. And this JDK should
-support as many as possible signature algorithms. Anyway the latest JDK build
-is always recommended.
+keyAlgs=RSA;1024;2048;#DSA;1024;2048;#EC;384;521;
+ Specifies key algorithms to use in the test. For each key algorithm the
+ sizes it should be tested with can be specified after semicolons and
+ otherwise default values are used. An empty keysize denotes the default
+ keysize and invokes keytool without a keysize specified. On JDK 6 and
+ earlier, EC is not supported and always skipped.
+
+digestAlgs=SHA-1#SHA-256#SHA-384#SHA-512#
+ Specifies the digest algorithms used for both digesting files contained in
+ the JAR file, manifests and signature files as well as certificates (keys)
+ and for TSA.
+ Ignored with TSA for jarsigner versions that don't support '-tsadigestalg'
+ parameter, for digest algorithms specified not to be supported by a TSA
+ server ('digests' sub-option is given to a tsaList item where digest
+ algorithm is not contained in list), or in cases no TSA is used at all
+ ('tsaList=notsa').
+ Note that the same set of digest algorithms is used in all three places
+ (signing the key, digesting the JAR, and for the TSA) and cannot be
+ specified individually except that some TSAs may exclude some digest
+ algorithms.
##### Examples #####
$ cat /path/to/jdkList
@@ -177,13 +260,13 @@
http://zeitstempel.dfn.de
https://tsp.iaik.tugraz.at/tsp/TspRequest
-$ jtreg -va -nr -timeout:100 \
+$ jtreg -va -nr \
-jdk:/path/to/latest/jdk \
-DproxyHost=<proxy> -DproxyPort=<port> \
-DjdkListFile=/path/to/jdkList \
-DtsaListFile=/path/to/tsaList \
-DdelayVerify=true -DcertValidity=60 \
- <JDK_REPO>/jdk/test/sun/security/tools/jarsigner/compatibility/Compatibility.java
+ <JDK_REPO>/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java
The above is a comprehensive usage example. File "jdkList" lists the paths of
testing JDK builds, and file "tsaList" lists the URLs of TSA services. Some TSAs,
@@ -197,19 +280,28 @@
If don't want to provide such JDK list and TSA list files, the test allows to
specify JDKs and TSAs (via properties jdkList and tsaList respectively) in the
command directly, like the below style,
-$ jtreg -va -nr -timeout:100 \
+$ jtreg -va -nr \
-jdk:/path/to/latest/jdk \
-DproxyHost=<proxy> -DproxyPort=<port> \
-DjdkList=/path/to/jdk6u171-b05#/path/to/jdk7u161-b05#/path/to/jdk8u144-b01#/path/to/jdk9-179 \
-DtsaList=http://timestamp.comodoca.com/rfc3161#http://timestamp.entrust.net/TSS/RFC3161sha1TS;digests=SHA-1,SHA-256 \
-DdelayVerify=true -DcertValidity=60 \
- <JDK_REPO>/jdk/test/sun/security/tools/jarsigner/compatibility/Compatibility.java
+ <JDK_REPO>/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java
Furthermore, here introduces one of the simplest usages. It doesn't specify any
JDK list, so the testing JDK, which is specified by jtreg option "-jdk", will
be tested. And it doesn't apply delay verifying, and no proxy is used, and use
only one TSA. Now, the command is pretty simple and looks like the followings,
-$ jtreg -va -nr -timeout:100 \
+$ jtreg -va -nr \
-jdk:/path/to/latest/jdk \
-DtsaList=http://timestamp.comodoca.com/rfc3161 \
- <JDK_REPO>/jdk/test/sun/security/tools/jarsigner/compatibility/Compatibility.java
\ No newline at end of file
+ <JDK_REPO>/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java
+
+It also works without a tsaList but not without the tsaList argument present
+in order to prevent it going missing or ignored unnoticed. May be useful for
+local tests but not recommended for real regression tests. Together with other
+arguments, a very short running test could be started for example with:
+$ jtreg -va -nr \
+ -jdk:/path/to/latest/jdk \
+ -DtsaList=notsa "-DkeyAlgs=EC;" -DdigestAlgs=SHA-256 -Dexpired=false
+ <JDK_REPO>/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/tools/jarsigner/compatibility/SignTwice.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/**
+ * @bug 8217375
+ * @summary This test runs those test cases of {@link Compatibility} test nearby
+ * which can be executed within the currently built and tested JDK and without
+ * TSA, with only one digest algorithm and with only one key (algorithm and
+ * size) and without delayed verification.
+ * Other test cases are to be executed manually invoking {@link Compatibility}
+ * involving more than the currently built and tested JDK verifying the
+ * compatibility of jarsigner across different JDK releases.
+ * For more details about the test and its usages, please look at the README.
+ */
+/*
+ * @test
+ * @library /test/lib ../warnings
+ * @compile Compatibility.java
+ * @run main/othervm
+ * -Djava.security.properties=./java.security
+ * -Duser.language=en
+ * -Duser.country=US
+ * -DjdkList=TEST_JDK
+ * -DtsaList=notsa
+ * -Dexpired=false
+ * -DtestComprehensiveJarContents=true
+ * -DtestJarUpdate=true
+ * -Dstrict=true
+ * -DkeyAlgs=EC;#RSA;#DSA;
+ * -DdigestAlgs=SHA-512
+ * SignTwice
+ */
+public class SignTwice {
+
+ public static void main(String[] args) throws Throwable {
+ Compatibility.main(args);
+ }
+
+}
--- a/test/jdk/sun/security/tools/jarsigner/warnings/Test.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sun/security/tools/jarsigner/warnings/Test.java Thu Jul 25 08:24:01 2019 -0400
@@ -61,12 +61,16 @@
static final int VALIDITY = 365;
static final String WARNING = "Warning:";
- static final String WARNING_OR_ERROR = "(Warning|Error):";
+ static final String ERROR = "[Ee]rror:";
+ static final String WARNING_OR_ERROR = "(" + WARNING + "|" + ERROR + ")";
static final String CHAIN_NOT_VALIDATED_VERIFYING_WARNING
= "This jar contains entries "
+ "whose certificate chain is invalid.";
+ static final String CERTIFICATE_SELF_SIGNED
+ = "The signer's certificate is self-signed.";
+
static final String ALIAS_NOT_IN_STORE_VERIFYING_WARNING
= "This jar contains signed entries "
+ "that are not signed by alias in this keystore.";
--- a/test/jdk/sun/security/util/FilePermCompat/CompatImpact.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sun/security/util/FilePermCompat/CompatImpact.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -90,7 +90,7 @@
.debug(testcase)
.start();
if (p.waitFor() != 0) {
- Files.copy(Paths.get("stderr." + testcase), System.out);
+ Files.copy(Paths.get(testcase + ".stderr"), System.out);
failed += testcase + " ";
}
@@ -101,7 +101,7 @@
.debug(testcase)
.start();
if (p.waitFor() != 0) {
- Files.copy(Paths.get("stderr." + testcase), System.out);
+ Files.copy(Paths.get(testcase + ".stderr"), System.out);
failed += testcase + " ";
}
@@ -114,7 +114,7 @@
.debug(testcase)
.start();
if (p.waitFor() != 0) {
- Files.copy(Paths.get("stderr." + testcase), System.out);
+ Files.copy(Paths.get(testcase + ".stderr"), System.out);
failed += testcase + " ";
}
@@ -126,7 +126,7 @@
.debug(testcase)
.start();
if (p.waitFor() != 0) {
- Files.copy(Paths.get("stderr." + testcase), System.out);
+ Files.copy(Paths.get(testcase + ".stderr"), System.out);
failed += testcase + " ";
}
@@ -138,7 +138,7 @@
.debug(testcase)
.start();
if (p.waitFor() != 0) {
- Files.copy(Paths.get("stderr." + testcase), System.out);
+ Files.copy(Paths.get(testcase + ".stderr"), System.out);
failed += testcase + " ";
}
@@ -238,6 +238,7 @@
// For my policy, f is passed into test and new MP(f)
// will be set as new policy
p.perm(new SecurityPermission("setPolicy"));
+ p.perm(new SecurityPermission("getPolicy"));
p.args(f);
break;
default:
@@ -249,7 +250,9 @@
// My own Policy impl, with only one granted permission, also not smart
// enough to know whether ProtectionDomain grants any permission
static class MP extends Policy {
+ static final Policy DEFAULT_POLICY = Policy.getPolicy();
final PermissionCollection pc;
+
MP(String f) {
FilePermission p = new FilePermission(f, "read");
pc = p.newPermissionCollection();
@@ -267,7 +270,7 @@
@Override
public boolean implies(ProtectionDomain domain, Permission permission) {
- return pc.implies(permission);
+ return pc.implies(permission) || DEFAULT_POLICY.implies(domain, permission);
}
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/util/ManifestDigester/DigestInput.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.jar.Attributes.Name;
+import java.util.stream.Collectors;
+
+import sun.security.util.ManifestDigester;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Factory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.Test;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @bug 8217375
+ * @modules java.base/sun.security.util
+ * @compile ../../tools/jarsigner/Utils.java
+ * @run testng DigestInput
+ * @summary Checks that the manifest main attributes and entry digests are the
+ * same as before resolution of bug 8217375 which means they treat some white
+ * space different for oldStyle or digestWorkaround except for the blank line
+ * at the end of the manifest file for digestWorkaround.
+ */
+public class DigestInput {
+
+ /**
+ * Filters some test cases for calibrating expected digests with previous
+ * implementation. TODO: Delete this after calibrating with old sources.
+ */
+ static final boolean FIXED_8217375 = true; // FIXME
+
+ /**
+ * {@link ManifestDigester.Entry#digestWorkaround} should not feed the
+ * trailing blank line into the digester. Before resolution of 8217375 it
+ * fed the trailing blank line into the digest if the second line break
+ * was at the end of the file due to <pre>
+ * if (allBlank || (i == len-1)) {
+ * if (i == len-1)
+ * pos.endOfSection = i;
+ * else
+ * pos.endOfSection = last;
+ * </pre> in {@link ManifestDigester#findSection}. In that case at the end
+ * of the manifest file, {@link ManifestDigester.Entry#digestWorkaround}
+ * would have produced the same digest as
+ * {@link ManifestDigester.Entry#digest} which was wrong and without effect
+ * at best.
+ * <p>
+ * Once this fix is accepted, this flag can be removed along with
+ * {@link #assertDigestEqualsCatchWorkaroundBroken}.
+ */
+ static final boolean FIXED_8217375_EOF_ENDOFSECTION = FIXED_8217375;
+
+ static final String SECTION_NAME = "some individual section name";
+
+ @DataProvider(name = "parameters")
+ public static Object[][] parameters() {
+ List<Object[]> tests = new ArrayList<>();
+ for (String lineBreak : new String[] { "\n", "\r", "\r\n" }) {
+ if ("\r".equals(lineBreak) && !FIXED_8217375) continue;
+ for (int addLB = 0; addLB <= 4; addLB++) {
+ for (int numSecs = 0; numSecs <= 4; numSecs++) {
+ for (boolean otherSec : new Boolean[] { false, true }) {
+ for (boolean oldStyle : new Boolean[] { false, true }) {
+ for (boolean workaround :
+ new Boolean[] { false, true }) {
+ tests.add(new Object[] {
+ lineBreak, addLB, numSecs, otherSec,
+ oldStyle, workaround
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+ return tests.toArray(new Object[tests.size()][]);
+ }
+
+ @Factory(dataProvider = "parameters")
+ public static Object[] createTests(
+ String lineBreak, int additionalLineBreaks,
+ int numberOfSections, boolean hasOtherSection,
+ boolean oldStyle, boolean digestWorkaround) {
+ return new Object[] { new DigestInput(lineBreak,
+ additionalLineBreaks, numberOfSections, hasOtherSection,
+ oldStyle, digestWorkaround)
+ };
+ }
+
+ final String lineBreak;
+ final int additionalLineBreaks; // number of blank lines delimiting section
+ final int numberOfSections;
+ final boolean hasOtherSection;
+ final boolean oldStyle;
+ final boolean digestWorkaround;
+
+ public DigestInput(
+ String lineBreak, int additionalLineBreaks,
+ int numberOfSections, boolean hasOtherSection,
+ boolean oldStyle, boolean digestWorkaround) {
+ this.lineBreak = lineBreak;
+ this.additionalLineBreaks = additionalLineBreaks;
+ this.numberOfSections = numberOfSections;
+ this.hasOtherSection = hasOtherSection;
+ this.oldStyle = oldStyle;
+ this.digestWorkaround = digestWorkaround;
+ }
+
+ @BeforeMethod
+ public void verbose() {
+ System.out.println("-".repeat(72));
+ System.out.println("lineBreak = " +
+ Utils.escapeStringWithNumbers(lineBreak));
+ System.out.println("additionalLineBreaks = " + additionalLineBreaks);
+ System.out.println("numberOfSections = " + numberOfSections);
+ System.out.println("hasOtherSection = " + hasOtherSection);
+ System.out.println("oldStyle = " + oldStyle);
+ System.out.println("digestWorkaround = " + digestWorkaround);
+ System.out.println("-".repeat(72));
+ }
+
+ byte[] rawManifestBytes() {
+ return (
+ Name.MANIFEST_VERSION + ": 1.0" + lineBreak +
+ "OldStyle0: no trailing space" + lineBreak +
+ "OldStyle1: trailing space " + lineBreak +
+ "OldStyle2: two trailing spaces " + lineBreak +
+ lineBreak.repeat(additionalLineBreaks) +
+ (
+ "Name: " + SECTION_NAME + lineBreak +
+ "OldStyle0: no trailing space" + lineBreak +
+ "OldStyle1: trailing space " + lineBreak +
+ "OldStyle2: two trailing spaces " + lineBreak +
+ lineBreak.repeat(additionalLineBreaks)
+ ).repeat(numberOfSections) +
+ (hasOtherSection ?
+ "Name: unrelated trailing section" + lineBreak +
+ "OldStyle0: no trailing space" + lineBreak +
+ "OldStyle1: trailing space " + lineBreak +
+ "OldStyle2: two trailing spaces " + lineBreak +
+ lineBreak.repeat(additionalLineBreaks)
+ : "")
+ ).getBytes(UTF_8);
+ }
+
+ byte[] expectedMainAttrsDigest(boolean digestWorkaround) {
+ return (
+ Name.MANIFEST_VERSION + ": 1.0" + lineBreak +
+ "OldStyle0: no trailing space" + lineBreak +
+ "OldStyle1: trailing space" +
+ (!oldStyle || !lineBreak.startsWith("\r") || digestWorkaround ?
+ " " : "") + lineBreak +
+ "OldStyle2: two trailing spaces " +
+ (!oldStyle || !lineBreak.startsWith("\r") || digestWorkaround ?
+ " " : "") + lineBreak +
+ (
+ (
+ !digestWorkaround
+ || (
+ additionalLineBreaks == 1
+ && numberOfSections == 0
+ && !hasOtherSection
+ && (
+ digestWorkaround
+ && !FIXED_8217375_EOF_ENDOFSECTION
+ )
+ )
+ ) && (
+ additionalLineBreaks > 0
+ || numberOfSections > 0
+ || hasOtherSection
+ )
+ ? lineBreak : "")
+ ).getBytes(UTF_8);
+ }
+
+ byte[] expectedIndividualSectionDigest(boolean digestWorkaround) {
+ if (numberOfSections == 0) return null;
+ return (
+ (
+ "Name: " + SECTION_NAME + lineBreak +
+ "OldStyle0: no trailing space" + lineBreak +
+ "OldStyle1: trailing space" +
+ (!oldStyle || !lineBreak.startsWith("\r")
+ || digestWorkaround ? " " : "") + lineBreak +
+ "OldStyle2: two trailing spaces " +
+ (!oldStyle || !lineBreak.startsWith("\r")
+ || digestWorkaround ? " " : "") + lineBreak +
+ (
+ (
+ !digestWorkaround
+ ) && (
+ additionalLineBreaks > 0
+ )
+ ? lineBreak : "")
+ ).repeat(numberOfSections) +
+ (
+ additionalLineBreaks == 1
+ && !hasOtherSection
+ && digestWorkaround
+ && !FIXED_8217375_EOF_ENDOFSECTION
+ ? lineBreak : "")
+ ).getBytes(UTF_8);
+ }
+
+ class EchoMessageDigest extends MessageDigest {
+
+ ByteArrayOutputStream buf;
+
+ EchoMessageDigest() {
+ super("echo");
+ }
+
+ @Override
+ protected void engineReset() {
+ buf = new ByteArrayOutputStream();
+ }
+
+ @Override
+ protected void engineUpdate(byte input) {
+ buf.write(input);
+ }
+
+ @Override
+ protected void engineUpdate(byte[] i, int o, int l) {
+ buf.write(i, o, l);
+ }
+
+ @Override protected byte[] engineDigest() {
+ return buf.toByteArray();
+ }
+
+ }
+
+ byte[] digestMainAttributes(byte[] mfBytes) throws Exception {
+ Utils.echoManifest(mfBytes, "going to digest main attributes of");
+
+ ManifestDigester md = new ManifestDigester(mfBytes);
+ ManifestDigester.Entry entry =
+ md.get(ManifestDigester.MF_MAIN_ATTRS, oldStyle);
+ MessageDigest digester = new EchoMessageDigest();
+ return digestWorkaround ?
+ entry.digestWorkaround(digester) : entry.digest(digester);
+ }
+
+ byte[] digestIndividualSection(byte[] mfBytes) throws Exception {
+ Utils.echoManifest(mfBytes,
+ "going to digest section " + SECTION_NAME + " of");
+
+ ManifestDigester md = new ManifestDigester(mfBytes);
+ ManifestDigester.Entry entry = md.get(SECTION_NAME, oldStyle);
+ if (entry == null) {
+ return null;
+ }
+ MessageDigest digester = new EchoMessageDigest();
+ return digestWorkaround ?
+ entry.digestWorkaround(digester) : entry.digest(digester);
+ }
+
+
+ /**
+ * Checks that the manifest main attributes digest is the same as before.
+ */
+ @Test
+ public void testMainAttributesDigest() throws Exception {
+ byte[] mfRaw = rawManifestBytes();
+ byte[] digest = digestMainAttributes(mfRaw);
+ byte[] expectedDigest = expectedMainAttrsDigest(digestWorkaround);
+
+ // the individual section will be digested along with the main
+ // attributes if not properly delimited with a blank line
+ if (additionalLineBreaks == 0
+ && (numberOfSections > 0 || hasOtherSection)) {
+ assertNotEquals(digest, expectedDigest);
+ return;
+ }
+
+ byte[] expectedDigestNoWorkaround = expectedMainAttrsDigest(false);
+
+// assertDigestEquals(digest, expectedDigest); // FIXME
+ assertDigestEqualsCatchWorkaroundBroken(
+ digest, expectedDigest, expectedDigestNoWorkaround);
+ }
+
+ /**
+ * Checks that an individual section digest is the same as before.
+ */
+ @Test
+ public void testIndividualSectionDigest() throws Exception {
+ byte[] mfRaw = rawManifestBytes();
+ byte[] digest = digestIndividualSection(mfRaw);
+
+ // no digest will be produced for an individual section that is not
+ // properly section delimited with a blank line.
+ byte[] expectedDigest =
+ additionalLineBreaks == 0 ? null :
+ expectedIndividualSectionDigest(digestWorkaround);
+
+ byte[] expectedDigestNoWorkaround =
+ additionalLineBreaks == 0 ? null :
+ expectedIndividualSectionDigest(false);
+
+// assertDigestEquals(digest, expectedDigest); // FIXME
+ assertDigestEqualsCatchWorkaroundBroken(
+ digest, expectedDigest, expectedDigestNoWorkaround);
+ }
+
+ static int firstDiffPos = Integer.MAX_VALUE;
+
+ /**
+ * @see FIXED_8217375_EOF_ENDOFSECTION
+ */
+ void assertDigestEqualsCatchWorkaroundBroken(
+ byte[] actual, byte[] expected, byte[] expectedNoWorkaround)
+ throws IOException {
+ try {
+ assertDigestEquals(actual, expected);
+ } catch (AssertionError e) {
+ if (digestWorkaround && FIXED_8217375_EOF_ENDOFSECTION &&
+ Arrays.equals(expected, expectedNoWorkaround)) {
+ // if digests with and without workaround are the same anyway
+ // the workaround has failed and could not have worked with
+ // the same digest as produced without workaround before
+ // which would not match either because equal.
+ return;
+ }
+ fail("failed also without digestWorkaound", e);
+ }
+ }
+
+ void assertDigestEquals(byte[] actual, byte[] expected) throws IOException {
+ if (actual == null && expected == null) return;
+ Utils.echoManifest(actual, "actual digest");
+ Utils.echoManifest(expected, "expected digest");
+ for (int i = 0; i < actual.length && i < expected.length; i++) {
+ if (actual[i] != expected[i]) {
+ firstDiffPos = Math.min(firstDiffPos, i);
+ verbose();
+ fail("found first difference in current test"
+ + " at position " + i);
+ }
+ }
+ if (actual.length != expected.length) {
+ int diffPos = Math.min(actual.length, expected.length);
+ firstDiffPos = Math.min(firstDiffPos, diffPos);
+ verbose();
+ fail("found first difference in current test"
+ + " at position " + diffPos + " after one digest end");
+ }
+ assertEquals(actual, expected);
+ }
+
+ @AfterTest
+ public void reportFirstDiffPos() {
+ System.err.println("found first difference in all tests"
+ + " at position " + firstDiffPos);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/util/ManifestDigester/FindSection.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,750 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.concurrent.Callable;
+import java.util.function.Consumer;
+
+import sun.security.util.ManifestDigester;
+
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Factory;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @bug 8217375
+ * @modules java.base/sun.security.util:+open
+ * @compile ../../tools/jarsigner/Utils.java
+ * @run testng/othervm FindSection
+ * @summary Check {@link ManifestDigester#findSection}.
+ */
+public class FindSection {
+
+ /*
+ * TODO:
+ * FIXED_8217375 is not intended to keep. it is intended to show what
+ * exactly has changed with respect to the previous version for which no
+ * such test existed.
+ */
+ static final boolean FIXED_8217375 = true;
+
+ /**
+ * {@link ManifestDigester.Entry#digestWorkaround} should not feed the
+ * trailing blank line into the digester. Before resolution of 8217375 it
+ * fed the trailing blank line into the digest if the second line break
+ * was at the end of the file due to <pre>
+ * if (allBlank || (i == len-1)) {
+ * if (i == len-1)
+ * pos.endOfSection = i;
+ * else
+ * pos.endOfSection = last;
+ * </pre> in {@link ManifestDigester#findSection}. In that case at the end
+ * of the manifest file, {@link ManifestDigester.Entry#digestWorkaround}
+ * would have produced the same digest as
+ * {@link ManifestDigester.Entry#digest} which was wrong and without effect
+ * at best.
+ * <p>
+ * Once this fix is accepted, this flag can be removed along with
+ * {@link #actualEndOfSection8217375}.
+ */
+ static final boolean FIXED_8217375_EOF_ENDOFSECTION = FIXED_8217375;
+
+ /**
+ * {@link ManifestDigester.Position.endOfSection} usually points to the
+ * start position of the blank line trailing a section minus one.
+ * If a {@link ManifestDigester.Position} returned by
+ * {@link ManifestDigester#findSection} is based on a portion that starts
+ * with a blank line, above statement is (or was) not true, because of the
+ * initialization of {@code last} in {@link ManifestDigester#findSection}
+ * <pre>
+ * int last = offset;
+ * </pre>
+ * which would point after incrementing it in {@code pos.endOfSection + 1}
+ * on line 128 (line number before this change) or {@code int sectionLen =
+ * pos.endOfSection-start+1;} on line 133 (line number before this change)
+ * at one byte after the first line break character of usually two and
+ * possibly (assuming "{@code \r\n}" default line break normally) in between
+ * the two characters of a line break. After subtracting again the index of
+ * the section start position on former line 133, the last byte would be
+ * missed to be digested by {@link ManifestDigester.Entry#digestWorkaround}.
+ * <p>
+ * All this, however could possibly matter (or have mattered) only when
+ * {@link ManifestDigester#findSection} was invoked with an offset position
+ * pointing straight to a line break which happens if a manifest starts
+ * with an empty line or if there are superfluous blank lines between
+ * sections in both cases no useful manifest portion is identified.
+ * Superfluous blank lines are not identified as sections (because they
+ * don't have a name and specifically don't meet {@code if (len > 6) {} on
+ * former line 136. Manifests starting with a line break are not any more
+ * useful either.
+ * <p>
+ * Once this fix is accepted, this flag can be removed along with
+ * {@link #actualEndOfSection8217375}.
+ */
+ static final boolean FIXED_8217375_STARTWITHBLANKLINE_ENDOFSECTION =
+ FIXED_8217375;
+
+ static Constructor<?> PositionConstructor;
+ static Method findSection;
+ static Field rawBytes;
+ static Field endOfFirstLine;
+ static Field endOfSection;
+ static Field startOfNext;
+
+ @BeforeClass
+ public static void setFindSectionAccessible() throws Exception {
+ Class<?> Position = Arrays.stream(ManifestDigester.class.
+ getDeclaredClasses()).filter(c -> c.getSimpleName().
+ equals("Position")).findFirst().get();
+ PositionConstructor = Position.getDeclaredConstructor();
+ PositionConstructor.setAccessible(true);
+ findSection = ManifestDigester.class.getDeclaredMethod("findSection",
+ int.class, Position);
+ findSection.setAccessible(true);
+ rawBytes = ManifestDigester.class.getDeclaredField("rawBytes");
+ rawBytes.setAccessible(true);
+ endOfFirstLine = Position.getDeclaredField("endOfFirstLine");
+ endOfFirstLine.setAccessible(true);
+ endOfSection = Position.getDeclaredField("endOfSection");
+ endOfSection.setAccessible(true);
+ startOfNext = Position.getDeclaredField("startOfNext");
+ startOfNext.setAccessible(true);
+ }
+
+ static class Position {
+ final int endOfFirstLine; // not including newline character
+
+ final int endOfSection; // end of section, not including the blank line
+ // between sections
+ final int startOfNext; // the start of the next section
+
+ Position(Object pos) throws ReflectiveOperationException {
+ endOfFirstLine = FindSection.endOfFirstLine.getInt(pos);
+ endOfSection = FindSection.endOfSection.getInt(pos);
+ startOfNext = FindSection.startOfNext.getInt(pos);
+ }
+ }
+
+ Position findSection(byte[] manifestBytes)
+ throws ReflectiveOperationException {
+ ManifestDigester manDig = new ManifestDigester("\n\n".getBytes(UTF_8));
+ FindSection.rawBytes.set(manDig, manifestBytes);
+ Object pos = PositionConstructor.newInstance();
+ Object result = findSection.invoke(manDig, offset, pos);
+ if (Boolean.FALSE.equals(result)) {
+ return null; // indicates findSection having returned false
+ } else {
+ return new Position(pos);
+ }
+ }
+
+ @DataProvider(name = "parameters")
+ public static Object[][] parameters() {
+ return new Object[][] { { 0 }, { 42 } };
+ }
+
+ @Factory(dataProvider = "parameters")
+ public static Object[] createTests(int offset) {
+ return new Object[]{ new FindSection(offset) };
+ }
+
+ final int offset;
+
+ FindSection(int offset) {
+ this.offset = offset;
+ }
+
+ @BeforeMethod
+ public void verbose() {
+ System.out.println("offset = " + offset);
+ }
+
+ Position findSection(String manifestString)
+ throws ReflectiveOperationException {
+ byte[] manifestBytes = manifestString.getBytes(UTF_8);
+ byte[] manifestWithOffset = new byte[manifestBytes.length + offset];
+ System.arraycopy(manifestBytes, 0, manifestWithOffset, offset,
+ manifestBytes.length);
+ return findSection(manifestWithOffset);
+ }
+
+ /**
+ * Surprising, but the offset actually makes a difference in
+ * {@link ManifestDigester#findSection} return value.
+ */
+ @SuppressWarnings("unused")
+ int actualEndOfFirstLine8217375(int correctPosition) {
+ // if the parsed portion of the manifest starts with a blank line,
+ // and offset is 0, "pos.endOfFirstLine = -1;" probably denoting a
+ // yet uninitialized value coincides with the assignment by
+ // "pos.endOfFirstLine = i-1;" if i == 0 and
+ // "if (pos.endOfFirstLine == -1)" after "case '\n':" happens to
+ // become true even though already assigned.
+ if (offset == 0 && correctPosition == -1 && !FIXED_8217375) return 0;
+ return correctPosition;
+ }
+
+ @SuppressWarnings("unused")
+ int actualEndOfSection8217375(int correctPosition, boolean eof, int lbl) {
+ // if the parsed portion of the manifest ends with a blank line and
+ // just before eof, the blank line is included in Position.endOfSection/
+ // Section.length (the one usually without blank line as well as in
+ // Position.startOfNext/Section.lengthWithBlankLine) which is used
+ // in digestWorkaround (independent of the digest without workaround)
+ if (eof && !FIXED_8217375_EOF_ENDOFSECTION) {
+ return correctPosition + lbl;
+ } else if (correctPosition == -1
+ && !FIXED_8217375_STARTWITHBLANKLINE_ENDOFSECTION) {
+ return 0;
+ } else {
+ return correctPosition;
+ }
+ }
+
+ AssertionError collectErrors(AssertionError a, Runnable run) {
+ try {
+ run.run();
+ } catch (AssertionError e) {
+ if (a == null) a = new AssertionError();
+ a.addSuppressed(e);
+ }
+ return a;
+ }
+
+ void assertPosition(Position pos,
+ int endOfFirstLine, int endOfSection, int startOfNext) {
+ AssertionError a = null;
+ a = collectErrors(a, () -> assertEquals(
+ pos.endOfFirstLine, endOfFirstLine + offset, "endOfFirstLine"));
+ a = collectErrors(a, () -> assertEquals(
+ pos.endOfSection, endOfSection + offset, "endOfSection"));
+ a = collectErrors(a, () -> assertEquals(
+ pos.startOfNext, startOfNext + offset, "startOfNext"));
+ if (a != null) throw a;
+ }
+
+ void catchCrCausesIndexOutOfBoundsException(
+ Callable<Position> test, Consumer<Position> asserts) {
+ try {
+ Position x = test.call();
+ if (!FIXED_8217375) fail();
+ asserts.accept(x);
+ } catch (Exception e) {
+ if (e instanceof IndexOutOfBoundsException ||
+ e.getCause() instanceof IndexOutOfBoundsException) {
+ if (FIXED_8217375) throw new AssertionError(e);
+ } else {
+ throw new AssertionError(e);
+ }
+ }
+ }
+
+ @Test
+ public void testEmpty() throws Exception {
+ assertNull(findSection(""));
+ }
+
+ @Test
+ public void testOneLineBreakCr() throws Exception {
+ catchCrCausesIndexOutOfBoundsException(
+ () -> findSection("\r"),
+ p -> assertPosition(p,
+ -1, actualEndOfSection8217375(-1, false, 1), 1)
+ );
+ }
+
+ @Test
+ public void testOneLineBreakLf() throws Exception {
+ assertPosition(findSection("\n"),
+ -1, actualEndOfSection8217375(-1, false, 1), 1);
+ }
+
+ @Test
+ public void testOneLineBreakCrLf() throws Exception {
+ assertPosition(findSection("\r\n"),
+ actualEndOfFirstLine8217375(-1),
+ actualEndOfSection8217375(-1, true, 2),
+ 2);
+ }
+
+ @Test
+ public void testSpaceAndLineBreakCr() throws Exception {
+ catchCrCausesIndexOutOfBoundsException(
+ () -> findSection(" \r"),
+ p -> assertPosition(p, 2, 3, 4)
+ );
+ }
+
+ @Test
+ public void testSpaceAndOneLineBreakLf() throws Exception {
+ assertPosition(findSection(" \n"), 2, 3, 4);
+ }
+
+ @Test
+ public void testSpaceAndOneLineBreakCrLf() throws Exception {
+ assertPosition(findSection(" \r\n"), 2, 4, 5);
+ }
+
+ @Test
+ public void testOneLineBreakCrAndSpace() throws Exception {
+ assertPosition(findSection("\r "),
+ -1, actualEndOfSection8217375(-1, false, 1), 1);
+ }
+
+ @Test
+ public void testOneLineBreakLfAndSpace() throws Exception {
+ assertPosition(findSection("\n "),
+ -1, actualEndOfSection8217375(-1, false, 1), 1);
+ }
+
+ @Test
+ public void testOneLineBreakCrLfAndSpace() throws Exception {
+ assertPosition(findSection("\r\n "),
+ actualEndOfFirstLine8217375(-1),
+ actualEndOfSection8217375(-1, false, 1),
+ 2);
+ }
+
+ @Test
+ public void testCrEof() throws Exception {
+ catchCrCausesIndexOutOfBoundsException(
+ () -> findSection("abc\r"),
+ p -> assertPosition(p, 2, 3, 4)
+ );
+ }
+
+ @Test
+ public void testLfEof() throws Exception {
+ assertPosition(findSection("abc\n"), 2, 3, 4);
+ }
+
+ @Test
+ public void testCrLfEof() throws Exception {
+ assertPosition(findSection("abc\r\n"), 2, 4, 5);
+ }
+
+ @Test
+ public void testCrContinued() throws Exception {
+ assertPosition(findSection("abc\rxyz\r\n\r\n "), 2, 8, 11);
+ }
+
+ @Test
+ public void testLfContinued() throws Exception {
+ assertPosition(findSection("abc\nxyz\r\n\r\n "), 2, 8, 11);
+ }
+
+ @Test
+ public void testCrLfContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\n "), 2, 9, 12);
+ }
+
+ @Test
+ public void testCrCrEof() throws Exception {
+ catchCrCausesIndexOutOfBoundsException(
+ () -> findSection("abc\r\nxyz\r\r"),
+ p -> assertPosition(p,
+ 2, actualEndOfSection8217375(8, true, 1), 10)
+ );
+ }
+
+ @Test
+ public void testCrCrContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\r "), 2, 8, 10);
+ }
+
+ @Test
+ public void testLfLfEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\n\n"),
+ 2, actualEndOfSection8217375(8, true, 1), 10);
+ }
+
+ @Test
+ public void testLfLfContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\n\n "), 2, 8, 10);
+ }
+
+ @Test
+ public void testCrLfEof2() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n"), 2, 9, 10);
+ }
+
+ @Test
+ public void testMainSectionNotTerminatedWithLineBreak() throws Exception {
+ assertNull(findSection("abc\r\nxyz\r\n "));
+ }
+
+ @Test
+ public void testLfCrEof() throws Exception {
+ catchCrCausesIndexOutOfBoundsException(
+ () -> findSection("abc\r\nxyz\n\r"),
+ p -> assertPosition(p,
+ 2, actualEndOfSection8217375(8, true, 1), 10)
+ );
+ }
+
+ @Test
+ public void testLfCrContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\n\r "), 2, 8, 10);
+ }
+
+ @Test
+ public void testCrLfCrEof() throws Exception {
+ catchCrCausesIndexOutOfBoundsException(
+ () -> findSection("abc\r\nxyz\r\n\r"),
+ p -> assertPosition(p,
+ 2, actualEndOfSection8217375(9, true, 2), 11)
+ );
+ }
+
+ @Test
+ public void testCrLfCrContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r "), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfLfEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\n"),
+ 2, actualEndOfSection8217375(9, true, 1), 11);
+ }
+
+ @Test
+ public void testCrLfLfContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\n "), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfCrLfEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\n"),
+ 2, actualEndOfSection8217375(9, true, 2), 12);
+ }
+
+ @Test
+ public void testCrLfCfLfContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\n "), 2, 9, 12);
+ }
+
+ @Test
+ public void testCrLfCrCrEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\r"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfCrCrContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\r "), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfLfCrEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\n\r"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfLfCrContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\n\r "), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfCrLfCrEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\n\r"), 2, 9, 12);
+ }
+
+ @Test
+ public void testCrLfCfLfCrContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\n\r "), 2, 9, 12);
+ }
+
+ @Test
+ public void testCrLfCrLfContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\n "), 2, 9, 12);
+ }
+
+ @Test
+ public void testCrLfLfLfEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\n\n"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfLfLfContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\n\n "), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfCrLfLfContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\n\n "), 2, 9, 12);
+ }
+
+ @Test
+ public void testCrLfCrCrLfEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\r\n"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfCrCrLfContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\r\n "), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfLfCrLfEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\n\r\n"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfLfCrLfContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\n\r\n "), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfCrLfCrLfEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\n\r\n"), 2, 9, 12);
+ }
+
+ @Test
+ public void testCrLfCfLfCrLfContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\n\r\n "), 2, 9, 12);
+ }
+
+ @Test
+ public void testCrLfLfCrCrEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\n\r\r"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfCrLfCrCrEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\n\r\r"), 2, 9, 12);
+ }
+
+ @Test
+ public void testCrLfCrLfCrContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\n\r "), 2, 9, 12);
+ }
+
+ @Test
+ public void testCrLfLfLfCrEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\n\n\r"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfLfCrLfCrEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\n\r\n\r"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfLfLfLfEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\n\n\n"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfLfCrLfLfEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\n\r\n\n"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfLfCrCrLfEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\n\r\r\n"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfCrLfCrCrLfEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\n\r\r\n"), 2, 9, 12);
+ }
+
+ @Test
+ public void testCrLfCrLfCrLfContinued() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\n\r\n "), 2, 9, 12);
+ }
+
+ @Test
+ public void testCrLfLfLfCrLfEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\n\n\r\n"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfLfCrLfCrLfEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\n\r\n\r\n"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfCrCrLfCrCrEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\r\n\r"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfCrCrCrCrEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\r\r"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfCrCrLfLfEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\r\n\n"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfCrCrLfCrLfEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\r\n\r\n"), 2, 9, 11);
+ }
+
+ @Test
+ public void testCrLfCrCrCrLfEof() throws Exception {
+ assertPosition(findSection("abc\r\nxyz\r\n\r\r\r\n"), 2, 9, 11);
+ }
+
+ /*
+ * endOfFirstLine is the same regardless of the line break delimiter
+ */
+ @Test
+ public void testEndOfFirstLineVsLineBreak() throws Exception {
+ for (String lb : new String[] { "\r", "\n", "\r\n" }) {
+ Position p = findSection("abc" + lb + "xyz" + lb + lb + " ");
+
+ // main assertion showing endOfFirstLine independent of line break
+ assertEquals(p.endOfFirstLine, 2 + offset);
+
+ // assert remaining positions as well just for completeness
+ assertPosition(p, 2, 5 + 2 * lb.length(), 6 + 3 * lb.length());
+ }
+ }
+
+ /*
+ * '\r' at the end of the bytes causes index out of bounds exception
+ */
+ @Test
+ public void testCrLastCausesIndexOutOfBounds() throws Exception {
+ catchCrCausesIndexOutOfBoundsException(
+ () -> findSection("\r"),
+ p -> assertPosition(p,
+ -1, actualEndOfSection8217375(-1, true, 1), 1)
+ );
+ }
+
+ /*
+ * endOfSection includes second line break if at end of bytes only
+ */
+ @Test
+ public void testEndOfSectionWithLineBreakVsEof() throws Exception {
+ AssertionError errors = new AssertionError("offset = " + offset);
+ for (String lb : new String[] { "\r", "\n", "\r\n" }) {
+ for (boolean eof : new boolean[] { false, true }) {
+ Position p;
+ try {
+ p = findSection("abc" + lb + lb + (eof ? "" : "xyz"));
+ } catch (RuntimeException | ReflectiveOperationException e) {
+ if ((e instanceof IndexOutOfBoundsException ||
+ e.getCause() instanceof IndexOutOfBoundsException)
+ && eof && "\r".equals(lb) && !FIXED_8217375) continue;
+ throw e;
+ }
+
+ AssertionError a = new AssertionError("offset = " + offset
+ + ", lb = " + Utils.escapeStringWithNumbers(lb) + ", "
+ + "eof = " + eof);
+
+ // main assertion showing endOfSection including second line
+ // break when at end of file
+ a = collectErrors(a, () -> assertEquals(
+ p.endOfSection,
+ actualEndOfSection8217375(
+ 2 + lb.length() + offset, eof, lb.length()) ));
+
+ // assert remaining positions as well just for completeness
+ a = collectErrors(a, () -> assertPosition(p,
+ 2,
+ actualEndOfSection8217375(
+ 2 + lb.length(), eof, lb.length()),
+ 3 + lb.length() * 2));
+
+ if (a.getSuppressed().length > 0) errors.addSuppressed(a);
+ }
+ }
+ if (errors.getSuppressed().length > 0) throw errors;
+ }
+
+ /*
+ * returns position even if only one line break before end of bytes.
+ * because no name will be found the result will be skipped and no entry
+ * will be created.
+ */
+ @Test
+ public void testReturnPosVsEof() throws Exception {
+ for (String lb : new String[] { "\r", "\n", "\r\n" }) {
+ for (boolean eof : new boolean[] { false, true }) {
+ try {
+ Position p = findSection("abc" + lb + (eof ? "" : "xyz"));
+ assertTrue(p != null == eof);
+ } catch (RuntimeException | ReflectiveOperationException e) {
+ if ((e instanceof IndexOutOfBoundsException ||
+ e.getCause() instanceof IndexOutOfBoundsException)
+ && eof && "\r".equals(lb) && !FIXED_8217375) continue;
+ throw e;
+ }
+ }
+ }
+ }
+
+ /*
+ * it could be normally be expected that startOfNext would point to the
+ * start of the next section after a blank line but that is not the case
+ * if a section ends with only one line break and no blank line immediately
+ * before eof of the manifest.
+ * such an entry will be digested without the trailing blank line which is
+ * only fine until another section should be added afterwards.
+ */
+ @Test
+ public void testStartOfNextPointsToEofWithNoBlankLine() throws Exception {
+ for (String lb : new String[] { "\r", "\n", "\r\n" }) {
+ for (boolean blank : new boolean[] { false, true }) {
+ String manifest = "abc" + lb + "xyz" + lb + (blank ? lb : "");
+ try {
+ Position p = findSection(manifest);
+
+ // assert that startOfNext points to eof in all cases
+ // whether with or without a blank line before eof
+ assertEquals(p.startOfNext, manifest.length() + offset);
+
+ // assert remaining positions as well just for completeness
+ assertPosition(p,
+ 2,
+ actualEndOfSection8217375(
+ 5 + lb.length() * 2,
+ true,
+ blank ? lb.length() : 0),
+ manifest.length());
+ } catch (RuntimeException | ReflectiveOperationException e) {
+ if ((e instanceof IndexOutOfBoundsException ||
+ e.getCause() instanceof IndexOutOfBoundsException)
+ && "\r".equals(lb) && !FIXED_8217375) continue;
+ throw e;
+ }
+ }
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/util/ManifestDigester/FindSections.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import sun.security.util.ManifestDigester;
+import org.testng.annotations.Test;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.testng.Assert.*;
+
+
+/**
+ * @test
+ * @bug 8217375
+ * @modules java.base/sun.security.util
+ * @run testng FindSections
+ * @summary Check {@link ManifestDigester#ManifestDigester} processing
+ * individual sections and particularly identifying their names correctly.
+ * Main attributes are not covered in this test.
+ * <p>
+ * See also {@link FindSection} for the {@link ManifestDigester#findSection}
+ * method specifically.
+ */
+public class FindSections {
+
+ static final String DEFAULT_MANIFEST = "Manifest-Version: 1.0\r\n\r\n";
+
+ void test(String manifest, String expectedSection) {
+ ManifestDigester md = new ManifestDigester(manifest.getBytes(UTF_8));
+ if (expectedSection != null) {
+ assertNotNull(md.get(expectedSection, false));
+ } else {
+ assertNull(md.get(expectedSection, false));
+ }
+ }
+
+ @Test
+ public void testNameNoSpaceAfterColon() throws Exception {
+ test(DEFAULT_MANIFEST + "Name:section\r\n\r\n", null);
+ }
+
+ @Test
+ public void testNameCase() throws Exception {
+ test(DEFAULT_MANIFEST + "nAME: section\r\n\r\n", "section");
+ }
+
+ @Test
+ public void testEmptyName() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: \r\n\r\n", "");
+ }
+
+ @Test
+ public void testShortestInvalidSection() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: ", null);
+ }
+
+ @Test
+ public void testMinimalValidSection() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: \r", "");
+ }
+
+ @Test
+ public void testNameNotContinued() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: FooBar\r\n", "FooBar");
+ }
+
+ @Test
+ public void testImmediatelyContinuedCrName() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: \r FooBar\r\n", "FooBar");
+ }
+
+ @Test
+ public void testImmediatelyContinuedLfName() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: \n FooBar\r\n", "FooBar");
+ }
+
+ @Test
+ public void testImmediatelyContinuedCrLfName() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: \r\n FooBar\r\n", "FooBar");
+ }
+
+ @Test
+ public void testNameContinuedCr() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: FooBar\r \r\n", "FooBar");
+ }
+
+ @Test
+ public void testNameContinuedLf() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: FooBar\n \r\n", "FooBar");
+ }
+
+ @Test
+ public void testNameContinuedCrLf() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: FooBar\r\n \r\n", "FooBar");
+ }
+
+ @Test
+ public void testNameContinuedCrIgnoreNextChar() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: Foo\r: Bar\r\n", "Foo");
+ }
+
+ @Test
+ public void testNameContinuedCrIgnoreNextCharSpace() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: Foo\r Bar\r\n", "Foo Bar");
+ }
+
+ @Test
+ public void testNameContinuedContinuedCr() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: Fo\r\n oB\r ar\r\n", "FooBar");
+ }
+
+ @Test
+ public void testNameContinuedContinuedLf() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: Fo\r\n oB\n ar\r\n", "FooBar");
+ }
+
+ @Test
+ public void testNameContinuedContinuedCrLf() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: Fo\r\n oB\r\n ar\r\n", "FooBar");
+ }
+
+ @Test
+ public void testNameContinuedEndCr() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: Foo\r\n Bar\r", "FooBar");
+ }
+
+ @Test
+ public void testNameContinuedEndLf() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: Foo\r\n Bar\n", "FooBar");
+ }
+
+ @Test
+ public void testNameContinuedEndCrLf() throws Exception {
+ test(DEFAULT_MANIFEST + "Name: Foo\r\n Bar\r\n", "FooBar");
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/util/ManifestDigester/LineBreaks.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.function.Function;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import sun.security.util.ManifestDigester;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @bug 8217375
+ * @modules java.base/sun.security.util
+ * @compile ../../tools/jarsigner/Utils.java
+ * @run testng LineBreaks
+ * @summary Verify {@code ManifestDigester} reads different line breaks well.
+ * The specifications state:
+ * <q><pre>newline: CR LF | LF | CR (not followed by LF)</pre></q>.
+ * This test does not verify that the digests are correct.
+ */
+public class LineBreaks {
+
+ static final String KEY = "Key";
+ static final String VALUE = "Value";
+ static final String SECTION = "Section";
+ static final String FOO = "Foo";
+ static final String BAR = "Bar";
+
+ static final String EXCEED_LINE_WIDTH_LIMIT = "x".repeat(71);
+
+ String breakAndContinue(String str, String lineBreak) {
+ // assert no multi-byte UTF-8 encoded characters in this test
+ assertEquals(str.getBytes(UTF_8).length, str.length());
+
+ int p = 1;
+ while (p + 71 < str.length()) {
+ p += 71;
+ str = str.substring(0, p) + lineBreak + " " + str.substring(p);
+ p += lineBreak.length() + 1;
+ }
+ return str;
+ }
+
+ byte[] createTestManifest(String lineBreak, boolean onlyMainAttrs,
+ String excess) throws IOException {
+ System.out.println("lineBreak = "
+ + Utils.escapeStringWithNumbers(lineBreak));
+ System.out.println("onlyMainAttrs = " + onlyMainAttrs);
+ String mf = "";
+ mf += breakAndContinue(
+ KEY + ": " + VALUE + excess, lineBreak) + lineBreak;
+ mf += lineBreak;
+ if (!onlyMainAttrs) {
+ mf += breakAndContinue(
+ "Name: " + SECTION + excess, lineBreak) + lineBreak;
+ mf += breakAndContinue(
+ FOO + ": " + BAR + excess, lineBreak) + lineBreak;
+ mf += lineBreak;
+ }
+ byte[] mfBytes = mf.getBytes(UTF_8);
+ Utils.echoManifest(mfBytes, "binary manifest");
+ return mfBytes;
+ }
+
+ @DataProvider(name = "parameters")
+ public static Object[][] parameters() {
+ List<Object[]> tests = new ArrayList<>();
+ for (String lineBreak : new String[] { "\n", "\r", "\r\n" }) {
+ for (boolean onlyMainAttrs : new boolean[] { false, true }) {
+ for (int numLbs = 0; numLbs < 3; numLbs++) {
+ tests.add(new Object[]{ lineBreak, onlyMainAttrs, numLbs });
+ }
+ }
+ }
+ return tests.toArray(new Object[tests.size()][]);
+ }
+
+ @Test(dataProvider = "parameters")
+ public void test(String lineBreak, boolean onlyMainAttrs, int numLbs)
+ throws IOException {
+ String excess = EXCEED_LINE_WIDTH_LIMIT.repeat(numLbs);
+ byte[] mfBytes = createTestManifest(lineBreak, onlyMainAttrs, excess);
+
+ // self-test: make sure the manifest is valid and represents the
+ // values as expected before attempting to digest it
+ Manifest mf = new Manifest(new ByteArrayInputStream(mfBytes));
+ assertEquals(mf.getMainAttributes().getValue(KEY), VALUE + excess);
+ Attributes section = mf.getAttributes(SECTION + excess);
+ if (onlyMainAttrs) {
+ assertNull(section);
+ } else {
+ assertEquals(section.getValue(FOO), BAR + excess);
+ }
+
+ // verify that ManifestDigester has actually found the individual
+ // section if and only if it was present thereby also implying based
+ // on ManifestDigester implementation that the main attributes were
+ // found before
+ ManifestDigester md = new ManifestDigester(mfBytes);
+ assertTrue((md.get(SECTION + excess, false) != null) != onlyMainAttrs);
+ }
+
+ static List<Integer> stringToIntList(String string) {
+ byte[] bytes = string.getBytes(UTF_8);
+ List<Integer> list = new ArrayList<>();
+ for (int i = 0; i < bytes.length; i++) {
+ list.add((int) bytes[i]);
+ }
+ return list;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/util/ManifestDigester/ReproduceRaw.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.jar.Manifest;
+
+import sun.security.util.ManifestDigester;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Factory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @bug 8217375
+ * @modules java.base/sun.security.util
+ * @compile ../../tools/jarsigner/Utils.java
+ * @run testng ReproduceRaw
+ * @summary Verifies that {@link ManifestDigester} can reproduce parts of
+ * manifests in their binary form so that {@link JarSigner} can rely on
+ * {@link ManifestDigester.Entry#reproduceRaw} to write in a map view
+ * unmodified entries back also unmodified in their binary form.
+ * <p>
+ * See also<ul>
+ * <li>{@link PreserveRawManifestEntryAndDigest} with end to end tests
+ * with {@code jarsigner} tool and</li>
+ * <li>{@link FindHeaderEndVsManifestDigesterFindFirstSection} about
+ * identifying the binary portion of only main attributes and more extensive
+ * main attributes digesting tests while this one test here is more about
+ * reproducing individual sections and that they result in the same
+ * digests.</li>
+ * </ul>
+ */
+public class ReproduceRaw {
+
+ static final boolean VERBOSE = false;
+
+ @DataProvider(name = "parameters")
+ public static Object[][] parameters() {
+ List<Object[]> tests = new ArrayList<>();
+ for (String lineBreak : new String[] { "\n", "\r", "\r\n" }) {
+ for (boolean oldStyle : new Boolean[] { false, true }) {
+ for (boolean workaround : new Boolean[] { false, true }) {
+ tests.add(new Object[] { lineBreak, oldStyle, workaround });
+ }
+ }
+ }
+ return tests.toArray(new Object[tests.size()][]);
+ }
+
+ @Factory(dataProvider = "parameters")
+ public static Object[] createTests(String lineBreak,
+ boolean oldStyle, boolean digestWorkaround) {
+ return new Object[]{
+ new ReproduceRaw(lineBreak, oldStyle, digestWorkaround)
+ };
+ }
+
+ final String lineBreak;
+ final boolean oldStyle;
+ final boolean digestWorkaround;
+
+ public ReproduceRaw(String lineBreak,
+ boolean oldStyle, boolean digestWorkaround) {
+ this.lineBreak = lineBreak;
+ this.oldStyle = oldStyle;
+ this.digestWorkaround = digestWorkaround;
+ }
+
+ @BeforeMethod
+ public void verbose() {
+ System.out.println("lineBreak = " +
+ Utils.escapeStringWithNumbers(lineBreak));
+ System.out.println("oldStyle = " + oldStyle);
+ System.out.println("digestWorkaround = " + digestWorkaround);
+ }
+
+ class EchoMessageDigest extends MessageDigest {
+
+ ByteArrayOutputStream buf;
+
+ EchoMessageDigest() {
+ super("echo");
+ }
+
+ @Override
+ protected void engineReset() {
+ buf = new ByteArrayOutputStream();
+ }
+
+ @Override
+ protected void engineUpdate(byte input) {
+ buf.write(input);
+ }
+
+ @Override
+ protected void engineUpdate(byte[] i, int o, int l) {
+ buf.write(i, o, l);
+ }
+
+ @Override protected byte[] engineDigest() {
+ return buf.toByteArray();
+ }
+
+ }
+
+ /**
+ * similar to corresponding part of {@link JarSigner#sign0}
+ * (stripped down to the code for reproducing the old manifest entry by
+ * entry which was too difficult to achieve using the real JarSigner code
+ * in the test here)
+ */
+ byte[] reproduceRawManifest(byte[] mfRawBytes,
+ boolean mainAttsProperlyDelimited,
+ boolean sectionProperlyDelimited) throws IOException {
+ Manifest manifest = new Manifest(new ByteArrayInputStream(mfRawBytes));
+ ManifestDigester oldMd = new ManifestDigester(mfRawBytes);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ // main attributes
+ assertEquals(oldMd.getMainAttsEntry().isProperlyDelimited(),
+ mainAttsProperlyDelimited);
+ oldMd.getMainAttsEntry().reproduceRaw(baos);
+
+ // individual sections
+ for (String key : manifest.getEntries().keySet()) {
+ assertEquals(oldMd.get(key).isProperlyDelimited(),
+ sectionProperlyDelimited);
+ oldMd.get(key).reproduceRaw(baos);
+ }
+
+ return baos.toByteArray();
+ }
+
+ static String regExscape(String expr) {
+ for (int i = 0; i < expr.length(); i++) {
+ if (expr.charAt(i) == '\r' || expr.charAt(i) == '\n') {
+ expr = expr.substring(0, i) + "\\" + expr.substring(i++);
+ }
+ }
+ return expr;
+ }
+
+ byte[] digest(byte[] manifest, String section) {
+ MessageDigest digester = new EchoMessageDigest();
+ ManifestDigester md = new ManifestDigester(manifest);
+ ManifestDigester.Entry entry = section == null ?
+ md.getMainAttsEntry(oldStyle) : md.get(section, oldStyle);
+ return digestWorkaround ?
+ entry.digestWorkaround(digester) :
+ entry.digest(digester);
+ }
+
+ void test(byte[] originalManifest, boolean mainAttsProperlyDelimited,
+ boolean sectionProperlyDelimited) throws Exception {
+ Utils.echoManifest(originalManifest, "original manifest");
+ byte[] reproducedManifest = reproduceRawManifest(originalManifest,
+ mainAttsProperlyDelimited, sectionProperlyDelimited);
+ Utils.echoManifest(reproducedManifest, "reproduced manifest");
+
+ // The reproduced manifest is not necessarily completely identical to
+ // the original if it contained superfluous blank lines.
+ // It's sufficient that the digests are equal and as an additional
+ // check, the reproduced manifest is here compared to the original
+ // without more than double line breaks.
+ if (!lineBreak.repeat(2).equals(new String(originalManifest, UTF_8))) {
+ assertEquals(
+ new String(reproducedManifest, UTF_8),
+ new String(originalManifest, UTF_8).replaceAll(
+ regExscape(lineBreak) + "(" + regExscape(lineBreak) + ")+",
+ lineBreak.repeat(2)));
+ }
+
+ // compare digests of reproduced manifest entries with digests of
+ // original manifest entries
+ assertEquals(digest(originalManifest, null),
+ digest(reproducedManifest, null));
+ for (String key : new Manifest(new ByteArrayInputStream(
+ originalManifest)).getEntries().keySet()) {
+ assertEquals(digest(originalManifest, key),
+ digest(reproducedManifest, key));
+ }
+
+ // parse and compare original and reproduced manifests as manifests
+ assertEquals(new Manifest(new ByteArrayInputStream(originalManifest)),
+ new Manifest(new ByteArrayInputStream(reproducedManifest)));
+ }
+
+ void test(byte[] originalManifest, boolean mainAttsProperlyDelimited)
+ throws Exception {
+ // assert all individual sections properly delimited particularly useful
+ // when no individual sections present
+ test(originalManifest, mainAttsProperlyDelimited, true);
+ }
+
+ @Test
+ public void testManifestStartsWithBlankLine() throws Exception {
+ test(lineBreak.getBytes(UTF_8), true);
+ test(lineBreak.repeat(2).getBytes(UTF_8), true);
+ }
+
+ @Test
+ public void testEOFAndNoLineBreakAfterMainAttributes() throws Exception {
+ assertThrows(RuntimeException.class, () ->
+ test("Manifest-Version: 1.0".getBytes(UTF_8), false)
+ );
+ }
+
+ @Test
+ public void testEOFAndNoBlankLineAfterMainAttributes() throws Exception {
+ test(("Manifest-Version: 1.0" + lineBreak).getBytes(UTF_8), false);
+ }
+
+ @Test
+ public void testNormalMainAttributes() throws Exception {
+ test(("Manifest-Version: 1.0" +
+ lineBreak.repeat(2)).getBytes(UTF_8), true);
+ }
+
+ @Test
+ public void testExtraLineBreakAfterMainAttributes() throws Exception {
+ test(("Manifest-Version: 1.0" +
+ lineBreak.repeat(3)).getBytes(UTF_8), true);
+ }
+
+ @Test
+ public void testIndividualSectionNoLineBreak() throws Exception {
+ assertNull(new ManifestDigester((
+ "Manifest-Version: 1.0" + lineBreak +
+ lineBreak +
+ "Name: Section-Name" + lineBreak +
+ "Key: Value"
+ ).getBytes(UTF_8)).get("Section-Name"));
+ }
+
+ @Test
+ public void testIndividualSectionOneLineBreak() throws Exception {
+ test((
+ "Manifest-Version: 1.0" + lineBreak +
+ lineBreak +
+ "Name: Section-Name" + lineBreak +
+ "Key: Value" + lineBreak
+ ).getBytes(UTF_8), true, false);
+ }
+
+ @Test
+ public void testNormalIndividualSectionTwoLineBreak() throws Exception {
+ test((
+ "Manifest-Version: 1.0" + lineBreak +
+ lineBreak +
+ "Name: Section-Name" + lineBreak +
+ "Key: Value" + lineBreak.repeat(2)
+ ).getBytes(UTF_8), true, true);
+ }
+
+ @Test
+ public void testExtraLineBreakAfterIndividualSection() throws Exception {
+ test((
+ "Manifest-Version: 1.0" + lineBreak +
+ lineBreak +
+ "Name: Section-Name" + lineBreak +
+ "Key: Value" + lineBreak.repeat(3)
+ ).getBytes(UTF_8), true, true);
+ }
+
+ @Test
+ public void testIndividualSections() throws Exception {
+ test((
+ "Manifest-Version: 1.0" + lineBreak +
+ lineBreak +
+ "Name: Section-Name" + lineBreak +
+ "Key: Value" + lineBreak +
+ lineBreak +
+ "Name: Section-Name" + lineBreak +
+ "Key: Value" + lineBreak +
+ lineBreak
+ ).getBytes(UTF_8), true, true);
+ }
+
+ @Test
+ public void testExtraLineBreakBetweenIndividualSections() throws Exception {
+ test((
+ "Manifest-Version: 1.0" + lineBreak +
+ lineBreak +
+ "Name: Section-Name" + lineBreak +
+ "Key: Value" + lineBreak +
+ lineBreak.repeat(2) +
+ "Name: Section-Name" + lineBreak +
+ "Key: Value" + lineBreak +
+ lineBreak
+ ).getBytes(UTF_8), true, true);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/tools/jcmd/JcmdOutputEncodingTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import java.util.Arrays;
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.JDKToolLauncher;
+
+/*
+ * @test
+ * @bug 8222491
+ * @summary Tests if we handle the encoding of jcmd output correctly.
+ * @library /test/lib
+ * @run main JcmdOutputEncodingTest
+ */
+public class JcmdOutputEncodingTest {
+
+ public static void main(String[] args) throws Exception {
+ testThreadDump();
+ }
+
+ private static void testThreadDump() throws Exception {
+ String markerName = "markerName" + "\u00e4\u0bb5".repeat(10_000);
+ Thread.currentThread().setName(markerName);
+
+ JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jcmd");
+ launcher.addToolArg(Long.toString(ProcessTools.getProcessId()));
+ launcher.addToolArg("Thread.print");
+
+ ProcessBuilder processBuilder = new ProcessBuilder();
+ processBuilder.command(launcher.getCommand());
+ OutputAnalyzer output = ProcessTools.executeProcess(processBuilder);
+ output.shouldHaveExitValue(0);
+ output.shouldContain(markerName);
+ }
+}
--- a/test/jdk/sun/tools/jstack/BasicJStackTest.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jdk/sun/tools/jstack/BasicJStackTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -36,6 +36,7 @@
public class BasicJStackTest {
private static ProcessBuilder processBuilder = new ProcessBuilder();
+ private static String markerName = "markerName" + "\u00e4\u0bb5".repeat(10_000);
public static void main(String[] args) throws Exception {
testJstackNoArgs();
@@ -45,14 +46,17 @@
private static void testJstackNoArgs() throws Exception {
OutputAnalyzer output = jstack();
output.shouldHaveExitValue(0);
+ output.shouldContain(markerName);
}
private static void testJstack_l() throws Exception {
OutputAnalyzer output = jstack("-l");
output.shouldHaveExitValue(0);
+ output.shouldContain(markerName);
}
private static OutputAnalyzer jstack(String... toolArgs) throws Exception {
+ Thread.currentThread().setName(markerName);
JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jstack");
launcher.addVMArg("-XX:+UsePerfData");
if (toolArgs != null) {
--- a/test/jtreg-ext/requires/VMProps.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/jtreg-ext/requires/VMProps.java Thu Jul 25 08:24:01 2019 -0400
@@ -46,6 +46,7 @@
import sun.hotspot.gc.GC;
import sun.hotspot.WhiteBox;
import jdk.test.lib.Platform;
+import jdk.test.lib.Container;
/**
* The Class to be invoked by jtreg prior Test Suite execution to
@@ -455,7 +456,7 @@
}
private boolean checkDockerSupport() throws IOException, InterruptedException {
- ProcessBuilder pb = new ProcessBuilder("docker", "ps");
+ ProcessBuilder pb = new ProcessBuilder(Container.ENGINE_COMMAND, "ps");
Process p = pb.start();
p.waitFor(10, TimeUnit.SECONDS);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/jdk/javadoc/doclet/testLinkOption/TestOptionOrder.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2018, 2019, 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.
+ */
+
+/*
+ * @test
+ * @bug 8222791
+ * @summary Order of evaluation of -link params in Javadoc tool reversed:
+ * regression with split packages
+ * @library /tools/lib ../../lib
+ * @modules
+ * jdk.javadoc/jdk.javadoc.internal.api
+ * jdk.javadoc/jdk.javadoc.internal.tool
+ * jdk.compiler/com.sun.tools.javac.api
+ * jdk.compiler/com.sun.tools.javac.main
+ * @build toolbox.JavacTask toolbox.JavadocTask toolbox.ToolBox
+ * @build javadoc.tester.*
+ * @run main TestOptionOrder
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import toolbox.JavacTask;
+import toolbox.JavadocTask;
+import toolbox.Task;
+import toolbox.ToolBox;
+
+import javadoc.tester.JavadocTester;
+
+public class TestOptionOrder extends JavadocTester {
+ final ToolBox tb;
+
+ public static void main(String... args) throws Exception {
+ TestOptionOrder tester = new TestOptionOrder();
+ tester.runTests(m -> new Object[] { Path.of(m.getName())} );
+ }
+
+ TestOptionOrder() throws Exception {
+ tb = new ToolBox();
+ }
+
+ enum Kind { PACKAGE_LIST, ELEMENT_LIST };
+
+ @Test
+ public void testLib1Lib2PackageList(Path base) throws Exception {
+ test(base, "lib1", "lib2", Kind.PACKAGE_LIST);
+ }
+
+ @Test
+ public void testLib1Lib2ElementList(Path base) throws Exception {
+ test(base, "lib1", "lib2", Kind.ELEMENT_LIST);
+ }
+
+ @Test
+ public void testLib2Lib1PackageList(Path base) throws Exception {
+ test(base, "lib2", "lib1", Kind.PACKAGE_LIST);
+ }
+
+ @Test
+ public void testLib2Lib1ElementList(Path base) throws Exception {
+ test(base, "lib2", "lib1", Kind.ELEMENT_LIST);
+ }
+
+ private void test(Path base, String first, String second, Kind kind) throws Exception {
+ createLib(base, first, kind);
+ createLib(base, second, kind);
+
+ Path src = base.resolve("src");
+
+ tb.writeJavaFiles(src,
+ "package app;\n"
+ + "/** Lorem ipsum.\n"
+ + " * @see lib.LibClass\n"
+ + " */\n"
+ + "public class App {\n"
+ + " /** Reference to LibClass. */\n"
+ + " public lib.LibClass lc;\n"
+ + "}\n");
+
+ javadoc("-d", base.resolve("out").toString(),
+ "-classpath",
+ base.resolve(first).resolve("classes")
+ + File.pathSeparator
+ + base.resolve(second).resolve("classes"),
+ "-linkoffline",
+ "http://example.com/" + first,
+ base.resolve(first).resolve("api").toString(),
+ "-linkoffline",
+ "http://example.com/" + second,
+ base.resolve(second).resolve("api").toString(),
+ "-sourcepath", src.toString(),
+ "app");
+
+ checkOrder("app/App.html",
+ // Instance in See Also
+ "<dd><a href=\"http://example.com/" + first + "/lib/LibClass.html",
+ // Instance in Field declaration
+ "<td class=\"colFirst\"><code><a href=\"http://example.com/" + first + "/lib/LibClass.html"
+ );
+ }
+
+ private void createLib(Path base, String name, Kind kind) throws Exception {
+ Path libBase = Files.createDirectories(base.resolve(name));
+ Path libSrc = libBase.resolve("src");
+
+ tb.writeJavaFiles(libSrc,
+ "package lib;\n"
+ + "/** Library " + name + ".*/\n"
+ + "public class LibClass { }\n");
+
+ new JavacTask(tb)
+ .outdir(Files.createDirectories(libBase.resolve("classes")))
+ .files(tb.findJavaFiles(libSrc))
+ .run(Task.Expect.SUCCESS);
+
+ Path libApi = libBase.resolve("api");
+ new JavadocTask(tb)
+ .sourcepath(libSrc)
+ .outdir(Files.createDirectories(libBase.resolve("api")))
+ .options("lib")
+ .run(Task.Expect.SUCCESS);
+
+ if (kind == Kind.PACKAGE_LIST) {
+ Path elementList = libApi.resolve("element-list");
+ Path packageList = libApi.resolve("package-list");
+ Files.move(elementList, packageList);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/TextBlockIllegalEscape.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,14 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8227640
+ * @summary Verify that illegal escapes in text blocks do not crash the javac.
+ * @compile/fail/ref=TextBlockIllegalEscape.out --enable-preview -source ${jdk.version} -XDrawDiagnostics TextBlockIllegalEscape.java
+ */
+
+public class TextBlockIllegalEscape {
+ static void test() {
+ EQ("""
+ \!
+ """, "");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/TextBlockIllegalEscape.out Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,4 @@
+TextBlockIllegalEscape.java:11:13: compiler.err.illegal.esc.char
+- compiler.note.preview.filename: TextBlockIllegalEscape.java
+- compiler.note.preview.recompile
+1 error
--- a/test/langtools/tools/javac/doctree/positions/TestPosition.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/langtools/tools/javac/doctree/positions/TestPosition.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 8008174
+ * @bug 8008174 8227923
* @summary proper source positions for doc comments
* @modules jdk.compiler
* @build TestPosition
--- a/test/langtools/tools/javac/doctree/positions/TestPosition.out Thu Jul 25 08:13:44 2019 -0400
+++ b/test/langtools/tools/javac/doctree/positions/TestPosition.out Thu Jul 25 08:24:01 2019 -0400
@@ -3,6 +3,8 @@
*
* <p>Description with {@link java.io.InputStream link}
*
+ * <em>text < < <!--some comment--> suffix</em>
+ *
* @param first description
* @param second description
* @return whatever
@@ -15,6 +17,18 @@
LINK:{@link java.io.InputStream link}
REFERENCE:java.io.InputStream
TEXT:link
+TEXT:
+ *
+ *!trailing-whitespace!
+START_ELEMENT:<em>
+TEXT:text!trailing-whitespace!
+ENTITY:<
+TEXT:!trailing-whitespace!
+ENTITY:<
+TEXT:!trailing-whitespace!
+COMMENT:<!--some comment-->
+TEXT: suffix
+END_ELEMENT:</em>
PARAM:@param first description
IDENTIFIER:first
TEXT:description
@@ -61,7 +75,7 @@
SINCE:@since
ERRONEOUS:@see
withWhiteSpaces:
-DOC_COMMENT:First sentence.
+DOC_COMMENT:First sentence. <em >text <!-- some comment --> suffix</em >
*
* <p>Description with {@link }, {@link java.util.List#add( int )},
* {@link java.util.List#add( int ) some text with whitespaces}, {@link
@@ -72,6 +86,14 @@
* @throws java.lang.IllegalStateException
* @throws java.lang.IllegalStateException some text
TEXT:First sentence.
+START_ELEMENT:<em >
+TEXT:text!trailing-whitespace!
+COMMENT:<!-- some comment -->
+TEXT: suffix
+END_ELEMENT:</em >
+TEXT:
+ *
+ *!trailing-whitespace!
START_ELEMENT:<p>
TEXT:Description with!trailing-whitespace!
LINK:{@link }
@@ -96,4 +118,79 @@
REFERENCE:java.lang.IllegalStateException
THROWS:@throws java.lang.IllegalStateException some text
REFERENCE:java.lang.IllegalStateException
-TEXT:some text
\ No newline at end of file
+TEXT:some text
+erroneous2:
+DOC_COMMENT:First sentence.
+ *
+ * <p>Description with {@unknownInlineTag }, {@unknownInlineTag text}, {@unknownInlineTag
+ *
+ * @param p1 p {@unknownInlineTag text
+ * @param p2 p <
+ * @param p3 p <em
+ * @param p4 p <!--
+ * @param p5 p <!-- --
+ * @param p6 p <!-- --
+ * @param p7 p &
+ * @param p8 p <
+ * @param p9 p <em> </
+ * @param pa p <em> </em
+TEXT:First sentence.
+START_ELEMENT:<p>
+TEXT:Description with!trailing-whitespace!
+UNKNOWN_INLINE_TAG:{@unknownInlineTag }
+TEXT:
+TEXT:,!trailing-whitespace!
+UNKNOWN_INLINE_TAG:{@unknownInlineTag text}
+TEXT:text
+TEXT:,!trailing-whitespace!
+ERRONEOUS:{@unknownInlineTag
+PARAM:@param p1 p {@unknownInlineTag text
+IDENTIFIER:p1
+TEXT:p!trailing-whitespace!
+ERRONEOUS:{@unknownInlineTag text
+PARAM:@param p2 p <
+IDENTIFIER:p2
+TEXT:p!trailing-whitespace!
+ERRONEOUS:<
+PARAM:@param p3 p <em
+IDENTIFIER:p3
+TEXT:p!trailing-whitespace!
+ERRONEOUS:<
+TEXT:em
+PARAM:@param p4 p <!--
+IDENTIFIER:p4
+TEXT:p!trailing-whitespace!
+ERRONEOUS:<
+TEXT:!--
+PARAM:@param p5 p <!-- --
+IDENTIFIER:p5
+TEXT:p!trailing-whitespace!
+ERRONEOUS:<
+TEXT:!-- --
+PARAM:@param p6 p <!-- --
+IDENTIFIER:p6
+TEXT:p!trailing-whitespace!
+ERRONEOUS:<
+TEXT:!-- --
+PARAM:@param p7 p &
+IDENTIFIER:p7
+TEXT:p!trailing-whitespace!
+ERRONEOUS:&
+PARAM:@param p8 p <
+IDENTIFIER:p8
+TEXT:p!trailing-whitespace!
+ERRONEOUS:<
+PARAM:@param p9 p <em> </
+IDENTIFIER:p9
+TEXT:p!trailing-whitespace!
+START_ELEMENT:<em>
+TEXT:!trailing-whitespace!
+ERRONEOUS:<
+TEXT:/
+PARAM:@param pa p <em> </em
+IDENTIFIER:pa
+TEXT:p!trailing-whitespace!
+START_ELEMENT:<em>
+TEXT:!trailing-whitespace!
+ERRONEOUS:<
+TEXT:/em
--- a/test/langtools/tools/javac/doctree/positions/TestPositionSource.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/langtools/tools/javac/doctree/positions/TestPositionSource.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2019, 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
@@ -27,6 +27,8 @@
*
* <p>Description with {@link java.io.InputStream link}
*
+ * <em>text < < <!--some comment--> suffix</em>
+ *
* @param first description
* @param second description
* @return whatever
@@ -54,7 +56,7 @@
return true;
}
- /**First sentence.
+ /**First sentence. <em >text <!-- some comment --> suffix</em >
*
* <p>Description with {@link }, {@link java.util.List#add( int )},
* {@link java.util.List#add( int ) some text with whitespaces}, {@link
@@ -69,4 +71,22 @@
return true;
}
+ /**First sentence.
+ *
+ * <p>Description with {@unknownInlineTag }, {@unknownInlineTag text}, {@unknownInlineTag
+ *
+ * @param p1 p {@unknownInlineTag text
+ * @param p2 p <
+ * @param p3 p <em
+ * @param p4 p <!--
+ * @param p5 p <!-- --
+ * @param p6 p <!-- --
+ * @param p7 p &
+ * @param p8 p <
+ * @param p9 p <em> </
+ * @param pa p <em> </em
+ */
+ public void erroneous2(int p1, int p2, int p3, int p4, int p5,
+ int p6, int p7, int p8, int p9, int pa) {
+ }
}
--- a/test/langtools/tools/javac/file/LimitedImage.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/langtools/tools/javac/file/LimitedImage.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -28,7 +28,7 @@
* @library /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
- * @run main/othervm --limit-modules jdk.compiler LimitedImage
+ * @run main/othervm --limit-modules jdk.compiler,jdk.internal.vm.compiler LimitedImage
*/
import java.io.IOException;
--- a/test/langtools/tools/javac/modules/InheritRuntimeEnvironmentTest.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/langtools/tools/javac/modules/InheritRuntimeEnvironmentTest.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -130,7 +130,8 @@
// This is the test, to verify that the module being compiled will not be able to read
// modules on the module path when a --limit-modules is used
new TestCase(base)
- .testOpts("--module-path", modules.toString(), "--limit-modules", "jdk.compiler")
+ .testOpts("--module-path", modules.toString(),
+ "--limit-modules", "jdk.compiler,jdk.internal.vm.compiler")
.otherOpts("-XDrawDiagnostics",
"--module-source-path", src.toString(),
"-classpath", emptyClassPath.toString())
--- a/test/langtools/tools/sjavac/IdleShutdown.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/langtools/tools/sjavac/IdleShutdown.java Thu Jul 25 08:24:01 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -88,9 +88,13 @@
long error = Math.abs(expectedTimeout - timeoutTimestamp.get());
log("Timeout error: " + error + " ms");
- if (error > TIMEOUT_MS * .1)
- throw new AssertionError("Error too big");
-
+ String timeoutFactorText = System.getProperty("test.timeout.factor", "1.0");
+ double timeoutFactor = Double.parseDouble(timeoutFactorText);
+ double allowedError = TIMEOUT_MS * 0.1 * timeoutFactor;
+ if (error > allowedError) {
+ throw new AssertionError("Timeout error too large, error is " + error +
+ " milliseconds, we allowed " + allowedError + " milliseconds");
+ }
log("Shutting down");
service.shutdown();
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/lib/jdk/test/lib/Container.java Thu Jul 25 08:24:01 2019 -0400
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019, Red Hat Inc.
+ * 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 jdk.test.lib;
+
+public class Container {
+ // Use this property to specify docker location on your system.
+ // E.g.: "/usr/local/bin/docker". We define this constant here so
+ // that it can be used in VMProps as well which checks docker support
+ // via this command
+ public static final String ENGINE_COMMAND =
+ System.getProperty("jdk.test.container.command", "docker");
+}
--- a/test/lib/jdk/test/lib/containers/cgroup/MetricsTester.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/lib/jdk/test/lib/containers/cgroup/MetricsTester.java Thu Jul 25 08:24:01 2019 -0400
@@ -531,16 +531,20 @@
long newUsage = metrics.getCpuUsage();
long[] newPerCpu = metrics.getPerCpuUsage();
- if (newSysVal <= startSysVal) {
+ // system/user CPU usage counters may be slowly increasing.
+ // allow for equal values for a pass
+ if (newSysVal < startSysVal) {
fail(SubSystem.CPU, "getCpuSystemUsage", newSysVal, startSysVal);
}
- if (newUserVal <= startUserVal) {
+ // system/user CPU usage counters may be slowly increasing.
+ // allow for equal values for a pass
+ if (newUserVal < startUserVal) {
fail(SubSystem.CPU, "getCpuUserUsage", newUserVal, startUserVal);
}
if (newUsage <= startUsage) {
- fail(SubSystem.CPU, "getCpuUserUsage", newUsage, startUsage);
+ fail(SubSystem.CPU, "getCpuUsage", newUsage, startUsage);
}
boolean success = false;
--- a/test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java Thu Jul 25 08:24:01 2019 -0400
@@ -37,6 +37,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import jdk.test.lib.Container;
import jdk.test.lib.Utils;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
@@ -54,11 +55,6 @@
// diagnostic information.
private static final int MAX_LINES_TO_COPY_FOR_CHILD_STDOUT = 100;
- // Use this property to specify docker location on your system.
- // E.g.: "/usr/local/bin/docker".
- private static final String DOCKER_COMMAND =
- System.getProperty("jdk.test.docker.command", "docker");
-
// Set this property to true to retain image after test. By default
// images are removed after test execution completes.
// Retaining the image can be useful for diagnostics and image inspection.
@@ -116,7 +112,7 @@
*/
private static boolean isDockerEngineAvailableCheck() throws Exception {
try {
- execute(DOCKER_COMMAND, "ps")
+ execute(Container.ENGINE_COMMAND, "ps")
.shouldHaveExitValue(0)
.shouldContain("CONTAINER")
.shouldContain("IMAGE");
@@ -179,9 +175,8 @@
DockerfileConfig.getBaseImageVersion());
try {
// Build the docker
- execute(DOCKER_COMMAND, "build", "--no-cache", "--tag", imageName, buildDir.toString())
- .shouldHaveExitValue(0)
- .shouldContain("Successfully built");
+ execute(Container.ENGINE_COMMAND, "build", "--no-cache", "--tag", imageName, buildDir.toString())
+ .shouldHaveExitValue(0);
} catch (Exception e) {
// If docker image building fails there is a good chance it happens due to environment and/or
// configuration other than product failure. Throw jtreg skipped exception in such case
@@ -194,7 +189,7 @@
/**
* Build the docker command to run java inside a container
*
- * @param DockerRunOptions optins for running docker
+ * @param DockerRunOptions options for running docker
*
* @return command
* @throws Exception
@@ -202,7 +197,7 @@
public static List<String> buildJavaCommand(DockerRunOptions opts) throws Exception {
List<String> cmd = new ArrayList<>();
- cmd.add(DOCKER_COMMAND);
+ cmd.add(Container.ENGINE_COMMAND);
cmd.add("run");
if (opts.tty)
cmd.add("--tty=true");
@@ -227,7 +222,7 @@
/**
* Run Java inside the docker image with specified parameters and options.
*
- * @param DockerRunOptions optins for running docker
+ * @param DockerRunOptions options for running docker
*
* @return output of the run command
* @throws Exception
@@ -240,11 +235,11 @@
/**
* Remove docker image
*
- * @param DockerRunOptions optins for running docker
+ * @param DockerRunOptions options for running docker
* @throws Exception
*/
public static void removeDockerImage(String imageNameAndTag) throws Exception {
- execute(DOCKER_COMMAND, "rmi", "--force", imageNameAndTag);
+ execute(Container.ENGINE_COMMAND, "rmi", "--force", imageNameAndTag);
}
--- a/test/lib/jdk/test/lib/process/OutputAnalyzer.java Thu Jul 25 08:13:44 2019 -0400
+++ b/test/lib/jdk/test/lib/process/OutputAnalyzer.java Thu Jul 25 08:24:01 2019 -0400
@@ -635,10 +635,10 @@
* just a subset of it.
*
* @param from
- * The line from where output will be matched.
+ * The line (excluded) from where output will be matched.
* Set {@code from} to null for matching from the first line.
* @param to
- * The line until where output will be matched.
+ * The line (excluded) until where output will be matched.
* Set {@code to} to null for matching until the last line.
* @param pattern
* Matching pattern
@@ -653,10 +653,10 @@
* just a subset of it.
*
* @param from
- * The line from where stdout will be matched.
+ * The line (excluded) from where stdout will be matched.
* Set {@code from} to null for matching from the first line.
* @param to
- * The line until where stdout will be matched.
+ * The line (excluded) until where stdout will be matched.
* Set {@code to} to null for matching until the last line.
* @param pattern
* Matching pattern
@@ -670,25 +670,26 @@
int fromIndex = 0;
if (from != null) {
- fromIndex = indexOf(lines, from);
- Asserts.assertGreaterThan(fromIndex, -1,
+ fromIndex = indexOf(lines, from, 0) + 1; // + 1 -> apply 'pattern' to lines after 'from' match
+ Asserts.assertGreaterThan(fromIndex, 0,
"The line/pattern '" + from + "' from where the output should match can not be found");
}
int toIndex = lines.size();
if (to != null) {
- toIndex = indexOf(lines, to);
- Asserts.assertGreaterThan(toIndex, -1,
+ toIndex = indexOf(lines, to, fromIndex);
+ Asserts.assertGreaterThan(toIndex, fromIndex,
"The line/pattern '" + to + "' until where the output should match can not be found");
}
List<String> subList = lines.subList(fromIndex, toIndex);
- Asserts.assertFalse(subList.isEmpty(), "There are no lines to check");
+ Asserts.assertFalse(subList.isEmpty(), "There are no lines to check:"
+ + " range " + fromIndex + ".." + toIndex + ", subList = " + subList);
subList.stream()
.filter(Pattern.compile(pattern).asPredicate().negate())
.findAny()
- .ifPresent(line -> Asserts.assertTrue(false,
+ .ifPresent(line -> Asserts.fail(
"The line '" + line + "' does not match pattern '" + pattern + "'"));
return this;
@@ -698,11 +699,12 @@
* Check if there is a line matching {@code regexp} and return its index
*
* @param regexp Matching pattern
+ * @param fromIndex Start matching after so many lines skipped
* @return Index of first matching line
*/
- private int indexOf(List<String> lines, String regexp) {
+ private int indexOf(List<String> lines, String regexp, int fromIndex) {
Pattern pattern = Pattern.compile(regexp);
- for (int i = 0; i < lines.size(); i++) {
+ for (int i = fromIndex; i < lines.size(); i++) {
if (pattern.matcher(lines.get(i)).matches()) {
return i;
}