# HG changeset patch # User prr # Date 1462303520 25200 # Node ID 74cd426ebb3d13c69be87b4ecf1e04da8dcac454 # Parent 8ff4232c93c227b328ac89a534f1ddd7a1fa21fa# Parent c6aca850d1626712dfa70be931133ea976a5b37b Merge diff -r 8ff4232c93c2 -r 74cd426ebb3d .hgtags --- a/.hgtags Tue May 03 09:48:02 2016 -0700 +++ b/.hgtags Tue May 03 12:25:20 2016 -0700 @@ -358,3 +358,4 @@ 6072af7a98be3922f26bdce71b53bb3646cb2ac9 jdk-9+113 c84d0cce090e161d736de69e941830adf8c2f87a jdk-9+114 8d78fb40648dd221ce4ef19f9d5aa41ee1a3a884 jdk-9+115 +84aba7335005a3a47751dcf1f37935f97df9f99a jdk-9+116 diff -r 8ff4232c93c2 -r 74cd426ebb3d .hgtags-top-repo --- a/.hgtags-top-repo Tue May 03 09:48:02 2016 -0700 +++ b/.hgtags-top-repo Tue May 03 12:25:20 2016 -0700 @@ -358,3 +358,4 @@ 55b6d550828d1223b364e6ead4a56e56411c56df jdk-9+113 1d992540870ff33fe6cc550443388588df9b9e4f jdk-9+114 09617ce980b99d49abfd54dacfed353c47e2a115 jdk-9+115 +6743a8e0cab7b5f6f4a0575f6664892f0ab740af jdk-9+116 diff -r 8ff4232c93c2 -r 74cd426ebb3d common/autoconf/boot-jdk.m4 --- a/common/autoconf/boot-jdk.m4 Tue May 03 09:48:02 2016 -0700 +++ b/common/autoconf/boot-jdk.m4 Tue May 03 12:25:20 2016 -0700 @@ -397,6 +397,7 @@ ADD_JVM_ARG_IF_OK([-XX:+UseSerialGC],boot_jdk_jvmargs_small,[$JAVA]) ADD_JVM_ARG_IF_OK([-Xms32M],boot_jdk_jvmargs_small,[$JAVA]) ADD_JVM_ARG_IF_OK([-Xmx512M],boot_jdk_jvmargs_small,[$JAVA]) + ADD_JVM_ARG_IF_OK([-XX:TieredStopAtLevel=1],boot_jdk_jvmargs_small,[$JAVA]) AC_MSG_RESULT([$boot_jdk_jvmargs_small]) diff -r 8ff4232c93c2 -r 74cd426ebb3d common/autoconf/compare.sh.in --- a/common/autoconf/compare.sh.in Tue May 03 09:48:02 2016 -0700 +++ b/common/autoconf/compare.sh.in Tue May 03 12:25:20 2016 -0700 @@ -31,7 +31,7 @@ export LEGACY_BUILD_DIR=@OPENJDK_TARGET_OS@-@OPENJDK_TARGET_CPU_LEGACY@ -sexport OPENJDK_TARGET_OS="@OPENJDK_TARGET_OS@" +export OPENJDK_TARGET_OS="@OPENJDK_TARGET_OS@" export OPENJDK_TARGET_CPU="@OPENJDK_TARGET_CPU@" export OPENJDK_TARGET_CPU_LIBDIR="@OPENJDK_TARGET_CPU_LIBDIR@" export DEBUG_LEVEL="@DEBUG_LEVEL@" diff -r 8ff4232c93c2 -r 74cd426ebb3d common/autoconf/generated-configure.sh --- a/common/autoconf/generated-configure.sh Tue May 03 09:48:02 2016 -0700 +++ b/common/autoconf/generated-configure.sh Tue May 03 12:25:20 2016 -0700 @@ -1224,9 +1224,9 @@ with_dxsdk with_dxsdk_lib with_dxsdk_include -enable_jtreg_failure_handler enable_new_hotspot_build enable_hotspot_test_in_build +enable_jtreg_failure_handler with_num_cores with_memory_size with_jobs @@ -5070,7 +5070,7 @@ #CUSTOM_AUTOCONF_INCLUDE # Do not change or remove the following line, it is needed for consistency checks: -DATE_WHEN_GENERATED=1460963400 +DATE_WHEN_GENERATED=1462204427 ############################################################################### # @@ -15492,7 +15492,7 @@ HOTSPOT_TARGET_CPU_DEFINE=PPC32 elif test "x$OPENJDK_TARGET_CPU" = xs390; then HOTSPOT_TARGET_CPU_DEFINE=S390 - elif test "x$OPENJDK_TARGET_CPU" = ss390x; then + elif test "x$OPENJDK_TARGET_CPU" = xs390x; then HOTSPOT_TARGET_CPU_DEFINE=S390 fi @@ -15648,7 +15648,7 @@ HOTSPOT_BUILD_CPU_DEFINE=PPC32 elif test "x$OPENJDK_BUILD_CPU" = xs390; then HOTSPOT_BUILD_CPU_DEFINE=S390 - elif test "x$OPENJDK_BUILD_CPU" = ss390x; then + elif test "x$OPENJDK_BUILD_CPU" = xs390x; then HOTSPOT_BUILD_CPU_DEFINE=S390 fi @@ -64282,6 +64282,21 @@ fi + $ECHO "Check if jvm arg is ok: -XX:TieredStopAtLevel=1" >&5 + $ECHO "Command: $JAVA -XX:TieredStopAtLevel=1 -version" >&5 + OUTPUT=`$JAVA -XX:TieredStopAtLevel=1 -version 2>&1` + FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` + FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` + if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then + boot_jdk_jvmargs_small="$boot_jdk_jvmargs_small -XX:TieredStopAtLevel=1" + JVM_ARG_OK=true + else + $ECHO "Arg failed:" >&5 + $ECHO "$OUTPUT" >&5 + JVM_ARG_OK=false + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $boot_jdk_jvmargs_small" >&5 $as_echo "$boot_jdk_jvmargs_small" >&6; } diff -r 8ff4232c93c2 -r 74cd426ebb3d common/autoconf/platform.m4 --- a/common/autoconf/platform.m4 Tue May 03 09:48:02 2016 -0700 +++ b/common/autoconf/platform.m4 Tue May 03 12:25:20 2016 -0700 @@ -435,7 +435,7 @@ HOTSPOT_$1_CPU_DEFINE=PPC32 elif test "x$OPENJDK_$1_CPU" = xs390; then HOTSPOT_$1_CPU_DEFINE=S390 - elif test "x$OPENJDK_$1_CPU" = ss390x; then + elif test "x$OPENJDK_$1_CPU" = xs390x; then HOTSPOT_$1_CPU_DEFINE=S390 fi AC_SUBST(HOTSPOT_$1_CPU_DEFINE) diff -r 8ff4232c93c2 -r 74cd426ebb3d common/conf/jib-profiles.js --- a/common/conf/jib-profiles.js Tue May 03 09:48:02 2016 -0700 +++ b/common/conf/jib-profiles.js Tue May 03 12:25:20 2016 -0700 @@ -212,14 +212,17 @@ * @returns Common values */ var getJibProfilesCommon = function (input) { - var common = { - dependencies: ["boot_jdk", "gnumake", "jtreg"], - configure_args: ["--with-default-make-target=all", "--enable-jtreg-failure-handler"], - configure_args_32bit: ["--with-target-bits=32", "--with-jvm-variants=client,server"], - configure_args_debug: ["--enable-debug"], - configure_args_slowdebug: ["--with-debug-level=slowdebug"], - organization: "jpg.infra.builddeps" - }; + var common = {}; + + common.dependencies = ["boot_jdk", "gnumake", "jtreg"], + common.default_make_targets = ["product-images", "test-image"], + common.default_make_targets_debug = common.default_make_targets; + common.default_make_targets_slowdebug = common.default_make_targets; + common.configure_args = ["--enable-jtreg-failure-handler"], + common.configure_args_32bit = ["--with-target-bits=32", "--with-jvm-variants=client,server"], + common.configure_args_debug = ["--enable-debug"], + common.configure_args_slowdebug = ["--with-debug-level=slowdebug"], + common.organization = "jpg.infra.builddeps" return common; }; @@ -241,8 +244,8 @@ target_os: "linux", target_cpu: "x64", dependencies: concat(common.dependencies, "devkit"), - configure_args: concat(common.configure_args, "--with-zlib=system"), - make_args: common.make_args + configure_args: concat(common.configure_args, "--with-zlib=system"), + default_make_targets: concat(common.default_make_targets, "docs-image") }, "linux-x86": { @@ -252,39 +255,39 @@ dependencies: concat(common.dependencies, "devkit"), configure_args: concat(common.configure_args, common.configure_args_32bit, "--with-zlib=system"), - make_args: common.make_args + default_make_targets: common.default_make_targets }, "macosx-x64": { target_os: "macosx", target_cpu: "x64", dependencies: concat(common.dependencies, "devkit"), - configure_args: concat(common.configure_args, "--with-zlib=system"), - make_args: common.make_args + configure_args: concat(common.configure_args, "--with-zlib=system"), + default_make_targets: common.default_make_targets }, "solaris-x64": { target_os: "solaris", target_cpu: "x64", dependencies: concat(common.dependencies, "devkit", "cups"), - configure_args: concat(common.configure_args, "--with-zlib=system"), - make_args: common.make_args + configure_args: concat(common.configure_args, "--with-zlib=system"), + default_make_targets: common.default_make_targets }, "solaris-sparcv9": { target_os: "solaris", target_cpu: "sparcv9", dependencies: concat(common.dependencies, "devkit", "cups"), - configure_args: concat(common.configure_args, "--with-zlib=system"), - make_args: common.make_args + configure_args: concat(common.configure_args, "--with-zlib=system"), + default_make_targets: common.default_make_targets }, "windows-x64": { target_os: "windows", target_cpu: "x64", dependencies: concat(common.dependencies, "devkit", "freetype"), - configure_args: common.configure_args, - make_args: common.make_args + configure_args: concat(common.configure_args), + default_make_targets: common.default_make_targets }, "windows-x86": { @@ -293,7 +296,7 @@ build_cpu: "x64", dependencies: concat(common.dependencies, "devkit", "freetype"), configure_args: concat(common.configure_args, common.configure_args_32bit), - make_args: common.make_args + default_make_targets: common.default_make_targets } }; profiles = concatObjects(profiles, mainProfiles); @@ -306,14 +309,15 @@ // implementation builds. var openOnlyProfiles = generateOpenOnlyProfiles(common, mainProfiles); // The open only profiles on linux are used for reference builds and should - // produce the compact profile images by default. + // produce the compact profile images by default. This adds "profiles" as an + // extra default target. var openOnlyProfilesExtra = { "linux-x64-open": { - configure_args: ["--with-default-make-target=all profiles"], + default_make_targets: "profiles" }, "linux-x86-open": { - configure_args: ["--with-default-make-target=all profiles"], + default_make_targets: "profiles" } }; var openOnlyProfiles = concatObjects(openOnlyProfiles, openOnlyProfilesExtra); @@ -336,6 +340,7 @@ // Generate the missing platform attributes profiles = generatePlatformAttributes(profiles); + profiles = generateDefaultMakeTargetsConfigureArg(common, profiles); return profiles; }; @@ -469,6 +474,8 @@ var debugProfile = profile + "-debug"; newProfiles[debugProfile] = clone(profiles[profile]); newProfiles[debugProfile].debug_level = "fastdebug"; + newProfiles[debugProfile].default_make_targets + = common.default_make_targets_debug; newProfiles[debugProfile].labels = concat(newProfiles[debugProfile].labels || [], "debug"), newProfiles[debugProfile].configure_args @@ -492,6 +499,8 @@ var debugProfile = profile + "-slowdebug"; newProfiles[debugProfile] = clone(profiles[profile]); newProfiles[debugProfile].debug_level = "slowdebug"; + newProfiles[debugProfile].default_make_targets + = common.default_make_targets_slowdebug; newProfiles[debugProfile].labels = concat(newProfiles[debugProfile].labels || [], "slowdebug"), newProfiles[debugProfile].configure_args @@ -524,6 +533,39 @@ }; /** + * The default_make_targets attribute on a profile is not a real Jib attribute. + * This function rewrites that attribute into the corresponding configure arg. + * Calling this function multiple times on the same profiles object is safe. + * + * @param common Common values + * @param profiles Profiles map to rewrite profiles for + * @returns {{}} New map of profiles with the make targets converted + */ +var generateDefaultMakeTargetsConfigureArg = function (common, profiles) { + var ret = concatObjects(profiles, {}); + for (var profile in ret) { + if (ret[profile]["default_make_targets"] != null) { + var targetsString = concat(ret[profile].default_make_targets).join(" "); + // Iterate over all configure args and see if --with-default-make-target + // is already there and change it, otherwise add it. + var found = false; + for (var arg in ret[profile].configure_args) { + if (arg.startsWith("--with-default-make-target")) { + found = true; + arg.replace(/=.*/, "=" + targetsString); + } + } + if (!found) { + ret[profile].configure_args = concat( + ret[profile].configure_args, + "--with-default-make-target=" + targetsString); + } + } + } + return ret; +} + +/** * Deep clones an object tree. * * @param o Object to clone diff -r 8ff4232c93c2 -r 74cd426ebb3d corba/.hgtags --- a/corba/.hgtags Tue May 03 09:48:02 2016 -0700 +++ b/corba/.hgtags Tue May 03 12:25:20 2016 -0700 @@ -358,3 +358,4 @@ cc30faa2da498c478e89ab062ff160653ca1b170 jdk-9+113 10d175b0368c30f54350fc648adc41b94ce357ee jdk-9+114 7bab1b1b36824924b1c657a8419369ba93d198d3 jdk-9+115 +7dfa7377a5e601b8f740741a9a80e04c72dd04d6 jdk-9+116 diff -r 8ff4232c93c2 -r 74cd426ebb3d hotspot/.hgtags --- a/hotspot/.hgtags Tue May 03 09:48:02 2016 -0700 +++ b/hotspot/.hgtags Tue May 03 12:25:20 2016 -0700 @@ -518,3 +518,4 @@ c569f8d89269fb6205b90f727581eb8cc04132f9 jdk-9+113 b64432bae5271735fd53300b2005b713e98ef411 jdk-9+114 88dd08d7be0fe7fb9f1914b1628f0aae9bf56e25 jdk-9+115 +61a214186dae6811dd989e9165e42f7dbf02acde jdk-9+116 diff -r 8ff4232c93c2 -r 74cd426ebb3d jaxp/.hgtags --- a/jaxp/.hgtags Tue May 03 09:48:02 2016 -0700 +++ b/jaxp/.hgtags Tue May 03 12:25:20 2016 -0700 @@ -358,3 +358,4 @@ 28626780e245fccbfb9bad8e3b05f62357958038 jdk-9+113 147114dd0641cd7c9fe6e81642eb993a7b9c6f0b jdk-9+114 1902a5bda18e794b31fc5f520f5e7d827714b50d jdk-9+115 +9d71d20e614777cd23c1a43b38b5c08a9094d27a jdk-9+116 diff -r 8ff4232c93c2 -r 74cd426ebb3d jaxp/src/java.xml/share/classes/javax/xml/catalog/CatalogFeatures.java --- a/jaxp/src/java.xml/share/classes/javax/xml/catalog/CatalogFeatures.java Tue May 03 09:48:02 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/javax/xml/catalog/CatalogFeatures.java Tue May 03 12:25:20 2016 -0700 @@ -444,13 +444,15 @@ } } else if (index == Feature.FILES.ordinal()) { try { - if (Util.verifyAndGetURI(value, null) == null) { - CatalogMessages.reportIAE(new Object[]{value, Feature.FILES.name()}, null); + String[] catalogFile = value.split(";[ ]*"); + for (String temp : catalogFile) { + if (Util.verifyAndGetURI(temp, null) == null) { + CatalogMessages.reportIAE(new Object[]{value, Feature.FILES.name()}, null); + } } }catch (MalformedURLException | URISyntaxException | IllegalArgumentException ex) { CatalogMessages.reportIAE(new Object[]{value, Feature.FILES.name()}, ex); } - } if (states[index] == null || state.compareTo(states[index]) >= 0) { values[index] = value; diff -r 8ff4232c93c2 -r 74cd426ebb3d jaxp/test/javax/xml/jaxp/libs/jaxp/library/TestPolicy.java --- a/jaxp/test/javax/xml/jaxp/libs/jaxp/library/TestPolicy.java Tue May 03 09:48:02 2016 -0700 +++ b/jaxp/test/javax/xml/jaxp/libs/jaxp/library/TestPolicy.java Tue May 03 12:25:20 2016 -0700 @@ -91,6 +91,7 @@ permissions.add(new PropertyPermission("line.separator", "read")); permissions.add(new PropertyPermission("fileStringBuffer", "read")); permissions.add(new PropertyPermission("dataproviderthreadcount", "read")); + permissions.add(new RuntimePermission("charsetProvider")); } /* diff -r 8ff4232c93c2 -r 74cd426ebb3d jaxp/test/javax/xml/jaxp/unittest/catalog/CatalogTest.java --- a/jaxp/test/javax/xml/jaxp/unittest/catalog/CatalogTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jaxp/test/javax/xml/jaxp/unittest/catalog/CatalogTest.java Tue May 03 12:25:20 2016 -0700 @@ -23,6 +23,7 @@ package catalog; import java.io.IOException; +import java.nio.file.Paths; import javax.xml.catalog.Catalog; import javax.xml.catalog.CatalogException; import javax.xml.catalog.CatalogFeatures; @@ -34,6 +35,7 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.testng.Assert; +import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import org.xml.sax.Attributes; @@ -44,10 +46,49 @@ import org.xml.sax.ext.DefaultHandler2; /* - * @bug 8081248, 8144966, 8146606, 8146237, 8151154, 8150969, 8151162, 8152527 + * @bug 8081248, 8144966, 8146606, 8146237, 8151154, 8150969, 8151162, 8152527, 8154220 * @summary Tests basic Catalog functions. */ public class CatalogTest { + static final String KEY_FILES = "javax.xml.catalog.files"; + + public String filepath; + + /* + * Initializing fields + */ + @BeforeClass + public void setUpClass() throws Exception { + String file1 = getClass().getResource("first_cat.xml").getFile(); + if (System.getProperty("os.name").contains("Windows")) { + filepath = file1.substring(1, file1.lastIndexOf("/") + 1); + } else { + filepath = file1.substring(0, file1.lastIndexOf("/") + 1); + } + } + + /* + * @bug 8154220 + * Verifies that the file input is validated properly. Valid input includes + * multiple file paths separated by semicolon. + */ + @Test(dataProvider = "hierarchyOfCatFilesData") + public void hierarchyOfCatFiles2(String systemId, String expectedUri) { + String file1 = getClass().getResource("first_cat.xml").getFile(); + String file2 = getClass().getResource("second_cat.xml").getFile(); + String files = file1 + ";" + file2; + + try { + System.setProperty(KEY_FILES, files); + CatalogResolver catalogResolver = CatalogManager.catalogResolver(CatalogFeatures.defaults()); + String sysId = catalogResolver.resolveEntity(null, systemId).getSystemId(); + Assert.assertEquals(sysId, Paths.get(filepath + expectedUri).toUri().toString().replace("///", "/"), "System ID match not right"); + } finally { + System.clearProperty(KEY_FILES); + } + + } + /* * @bug 8152527 * This test is the same as the JDK test ResolveEntityTests:testMatch1. @@ -289,6 +330,19 @@ } /* + DataProvider: used to verify hierarchical catalogs. Refer to JCK test + hierarchyOfCatFiles2. + */ + @DataProvider(name = "hierarchyOfCatFilesData") + Object[][] getHierarchyOfCatFilesData() { + return new Object[][]{ + {"http://www.oracle.com/sequence.dtd", "first.dtd"}, + {"http://www.oracle.com/sequence_next.dtd", "next.dtd"}, + {"http://www.oracle.com/sequence_second.dtd", "second.dtd"} + }; + } + + /* DataProvider: used to verify CatalogResolver's resolveEntity function. Data columns: catalog, prefer, systemId, publicId, expectedUri, expectedFile, msg @@ -300,6 +354,7 @@ {"rewriteSystem_id.xml", "system", "http://www.sys00test.com/rewrite.dtd", "PUB-404", expected, expected, "Relative rewriteSystem with xml:base at group level failed"}, }; } + static String id = "http://openjdk.java.net/xml/catalog/dtd/system.dtd"; /* DataProvider: used to verify how prefer settings affect the result of the diff -r 8ff4232c93c2 -r 74cd426ebb3d jaxp/test/javax/xml/jaxp/unittest/catalog/first_cat.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/unittest/catalog/first_cat.xml Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff -r 8ff4232c93c2 -r 74cd426ebb3d jaxp/test/javax/xml/jaxp/unittest/catalog/next_cat.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/unittest/catalog/next_cat.xml Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff -r 8ff4232c93c2 -r 74cd426ebb3d jaxp/test/javax/xml/jaxp/unittest/catalog/second_cat.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/unittest/catalog/second_cat.xml Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff -r 8ff4232c93c2 -r 74cd426ebb3d jaxws/.hgtags --- a/jaxws/.hgtags Tue May 03 09:48:02 2016 -0700 +++ b/jaxws/.hgtags Tue May 03 12:25:20 2016 -0700 @@ -361,3 +361,4 @@ e980062475c10d21137051045bf95ee229db9b27 jdk-9+113 b314bb02182b9ca94708a91f312c377f5435f740 jdk-9+114 4ff86e5489e4c0513dadfa69def8601c110ca5cd jdk-9+115 +529f0bf896e58525614d863e283ad155531941cb jdk-9+116 diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/make/Tools.gmk --- a/jdk/make/Tools.gmk Tue May 03 09:48:02 2016 -0700 +++ b/jdk/make/Tools.gmk Tue May 03 12:25:20 2016 -0700 @@ -96,7 +96,13 @@ TOOL_SPP = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes build.tools.spp.Spp # Nimbus is used somewhere in the swing build. + +ifeq ($(BOOT_JDK_MODULAR), true) + COMPILENIMBUS_ADD_MODS := -addmods java.xml.bind +endif + TOOL_GENERATENIMBUS = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ + $(COMPILENIMBUS_ADD_MODS) \ build.tools.generatenimbus.Generator TOOL_WRAPPERGENERATOR = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/make/gendata/Gendata-java.base.gmk --- a/jdk/make/gendata/Gendata-java.base.gmk Tue May 03 09:48:02 2016 -0700 +++ b/jdk/make/gendata/Gendata-java.base.gmk Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 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 @@ -41,7 +41,7 @@ GENDATA_UNINAME := $(JDK_OUTPUTDIR)/modules/java.base/java/lang/uniName.dat $(GENDATA_UNINAME): $(JDK_TOPDIR)/make/data/unicodedata/UnicodeData.txt $(BUILD_TOOLS_JDK) - $(MKDIR) -p $(@D) + $(call MakeDir, $(@D)) $(TOOL_CHARACTERNAME) $< $@ TARGETS += $(GENDATA_UNINAME) @@ -51,7 +51,7 @@ GENDATA_CURDATA := $(JDK_OUTPUTDIR)/modules/java.base/java/util/currency.data $(GENDATA_CURDATA): $(JDK_TOPDIR)/make/data/currency/CurrencyData.properties $(BUILD_TOOLS_JDK) - $(MKDIR) -p $(@D) + $(call MakeDir, $(@D)) $(RM) $@ $(TOOL_GENERATECURRENCYDATA) -o $@.tmp < $< $(MV) $@.tmp $@ @@ -67,10 +67,10 @@ # RESTRICTED_PKGS_SRC is optionally set in custom extension for this makefile $(GENDATA_JAVA_SECURITY): $(BUILD_TOOLS) $(GENDATA_JAVA_SECURITY_SRC) $(RESTRICTED_PKGS_SRC) - $(ECHO) "Generating java.security" - $(MKDIR) -p $(@D) + $(call LogInfo, Generating java.security) + $(call MakeDir, $(@D)) $(TOOL_MAKEJAVASECURITY) $(GENDATA_JAVA_SECURITY_SRC) $@ $(OPENJDK_TARGET_OS) \ - $(OPENJDK_TARGET_CPU_ARCH) $(RESTRICTED_PKGS_SRC) || exit 1 + $(OPENJDK_TARGET_CPU_ARCH) $(RESTRICTED_PKGS_SRC) TARGETS += $(GENDATA_JAVA_SECURITY) @@ -78,7 +78,7 @@ $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/classlist: \ $(JDK_TOPDIR)/make/data/classlist/classlist.$(OPENJDK_TARGET_OS) - $(MKDIR) -p $(@D) + $(call MakeDir, $(@D)) $(RM) $@ $@.tmp $(TOOL_ADDJSUM) $< $@.tmp $(MV) $@.tmp $@ diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/make/gendata/GendataBreakIterator.gmk --- a/jdk/make/gendata/GendataBreakIterator.gmk Tue May 03 09:48:02 2016 -0700 +++ b/jdk/make/gendata/GendataBreakIterator.gmk Tue May 03 12:25:20 2016 -0700 @@ -62,10 +62,13 @@ BIN := $(BREAK_ITERATOR_CLASSES)/jdk.localedata)) ifeq ($(BOOT_JDK_MODULAR), true) - BREAK_ITERATOR_BOOTCLASSPATH := -Xpatch:$(BREAK_ITERATOR_CLASSES) \ - -XaddExports:java.base/sun.text=ALL-UNNAMED \ - -XaddExports:java.base/sun.text.resources=ALL-UNNAMED \ - -XaddExports:jdk.localedata/sun.text.resources.ext=ALL-UNNAMED + BREAK_ITERATOR_BOOTCLASSPATH := \ + -Xpatch:java.base=$(BREAK_ITERATOR_CLASSES)/java.base \ + -Xpatch:jdk.localedata=$(BREAK_ITERATOR_CLASSES)/jdk.localedata \ + -XaddExports:java.base/sun.text=ALL-UNNAMED \ + -XaddExports:java.base/sun.text.resources=ALL-UNNAMED \ + -XaddExports:jdk.localedata/sun.text.resources.ext=ALL-UNNAMED \ + # else BREAK_ITERATOR_BOOTCLASSPATH := -Xbootclasspath/p:$(call PathList, \ $(BREAK_ITERATOR_CLASSES)/java.base \ diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/make/launcher/Launcher-java.desktop.gmk --- a/jdk/make/launcher/Launcher-java.desktop.gmk Tue May 03 09:48:02 2016 -0700 +++ b/jdk/make/launcher/Launcher-java.desktop.gmk Tue May 03 12:25:20 2016 -0700 @@ -31,7 +31,7 @@ ifndef BUILD_HEADLESS_ONLY $(eval $(call SetupBuildLauncher, appletviewer, \ MAIN_CLASS := sun.applet.Main, \ - JAVA_ARGS := -addmods ALL-SYSTEM, \ + JAVA_ARGS := -addmods ALL-DEFAULT, \ LIBS_unix := $(X_LIBS), \ )) endif diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/make/launcher/Launcher-java.scripting.gmk --- a/jdk/make/launcher/Launcher-java.scripting.gmk Tue May 03 09:48:02 2016 -0700 +++ b/jdk/make/launcher/Launcher-java.scripting.gmk Tue May 03 12:25:20 2016 -0700 @@ -27,4 +27,5 @@ $(eval $(call SetupBuildLauncher, jrunscript, \ MAIN_CLASS := com.sun.tools.script.shell.Main, \ + JAVA_ARGS := -addmods ALL-DEFAULT, \ )) diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/make/launcher/Launcher-jdk.compiler.gmk --- a/jdk/make/launcher/Launcher-jdk.compiler.gmk Tue May 03 09:48:02 2016 -0700 +++ b/jdk/make/launcher/Launcher-jdk.compiler.gmk Tue May 03 12:25:20 2016 -0700 @@ -27,7 +27,8 @@ $(eval $(call SetupBuildLauncher, javac, \ MAIN_CLASS := com.sun.tools.javac.Main, \ - CFLAGS := -DEXPAND_CLASSPATH_WILDCARDS \ + JAVA_ARGS := -addmods ALL-DEFAULT, \ + CFLAGS := -DEXPAND_CLASSPATH_WILDCARDS \ -DNEVER_ACT_AS_SERVER_CLASS_MACHINE, \ )) diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/make/launcher/Launcher-jdk.javadoc.gmk --- a/jdk/make/launcher/Launcher-jdk.javadoc.gmk Tue May 03 09:48:02 2016 -0700 +++ b/jdk/make/launcher/Launcher-jdk.javadoc.gmk Tue May 03 12:25:20 2016 -0700 @@ -27,6 +27,7 @@ $(eval $(call SetupBuildLauncher, javadoc, \ MAIN_CLASS := jdk.javadoc.internal.tool.Main, \ + JAVA_ARGS := -addmods ALL-DEFAULT, \ CFLAGS := -DEXPAND_CLASSPATH_WILDCARDS \ -DNEVER_ACT_AS_SERVER_CLASS_MACHINE, \ )) diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/make/launcher/Launcher-jdk.jlink.gmk --- a/jdk/make/launcher/Launcher-jdk.jlink.gmk Tue May 03 09:48:02 2016 -0700 +++ b/jdk/make/launcher/Launcher-jdk.jlink.gmk Tue May 03 12:25:20 2016 -0700 @@ -32,6 +32,7 @@ $(eval $(call SetupBuildLauncher, jlink,\ MAIN_CLASS := jdk.tools.jlink.internal.Main, \ + JAVA_ARGS := -addmods ALL-DEFAULT, \ CFLAGS := -DENABLE_ARG_FILES \ -DEXPAND_CLASSPATH_WILDCARDS \ -DNEVER_ACT_AS_SERVER_CLASS_MACHINE, \ diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/make/launcher/Launcher-jdk.scripting.nashorn.shell.gmk --- a/jdk/make/launcher/Launcher-jdk.scripting.nashorn.shell.gmk Tue May 03 09:48:02 2016 -0700 +++ b/jdk/make/launcher/Launcher-jdk.scripting.nashorn.shell.gmk Tue May 03 12:25:20 2016 -0700 @@ -27,6 +27,6 @@ $(eval $(call SetupBuildLauncher, jjs, \ MAIN_CLASS := jdk.nashorn.tools.jjs.Main, \ - JAVA_ARGS := -addmods ALL-SYSTEM, \ + JAVA_ARGS := -addmods ALL-DEFAULT, \ CFLAGS := -DENABLE_ARG_FILES, \ )) diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/linux/classes/sun/nio/fs/LinuxFileSystemProvider.java --- a/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxFileSystemProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxFileSystemProvider.java Tue May 03 12:25:20 2016 -0700 @@ -102,7 +102,7 @@ @Override FileTypeDetector getFileTypeDetector() { - String userHome = GetPropertyAction.getProperty("user.home"); + String userHome = GetPropertyAction.privilegedGetProperty("user.home"); Path userMimeTypes = Paths.get(userHome, ".mime.types"); Path etcMimeTypes = Paths.get("/etc/mime.types"); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/macosx/classes/sun/nio/ch/KQueueArrayWrapper.java --- a/jdk/src/java.base/macosx/classes/sun/nio/ch/KQueueArrayWrapper.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/macosx/classes/sun/nio/ch/KQueueArrayWrapper.java Tue May 03 12:25:20 2016 -0700 @@ -84,7 +84,8 @@ static { IOUtil.load(); initStructSizes(); - String datamodel = GetPropertyAction.getProperty("sun.arch.data.model"); + String datamodel = + GetPropertyAction.privilegedGetProperty("sun.arch.data.model"); is64bit = "64".equals(datamodel); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/macosx/classes/sun/nio/fs/MacOSXFileSystem.java --- a/jdk/src/java.base/macosx/classes/sun/nio/fs/MacOSXFileSystem.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/macosx/classes/sun/nio/fs/MacOSXFileSystem.java Tue May 03 12:25:20 2016 -0700 @@ -29,8 +29,6 @@ import java.io.IOException; import java.util.*; import java.util.regex.Pattern; -import java.security.AccessController; -import sun.security.action.GetPropertyAction; import static sun.nio.fs.MacOSXNativeDispatcher.*; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/macosx/classes/sun/nio/fs/MacOSXFileSystemProvider.java --- a/jdk/src/java.base/macosx/classes/sun/nio/fs/MacOSXFileSystemProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/macosx/classes/sun/nio/fs/MacOSXFileSystemProvider.java Tue May 03 12:25:20 2016 -0700 @@ -46,8 +46,8 @@ @Override FileTypeDetector getFileTypeDetector() { - Path userMimeTypes = Paths.get( - GetPropertyAction.getProperty("user.home"), ".mime.types"); + Path userMimeTypes = Paths.get(GetPropertyAction + .privilegedGetProperty("user.home"), ".mime.types"); return chain(new MimeTypesFileTypeDetector(userMimeTypes), new UTIFileTypeDetector()); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/io/File.java --- a/jdk/src/java.base/share/classes/java/io/File.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/io/File.java Tue May 03 12:25:20 2016 -0700 @@ -1896,7 +1896,7 @@ // temporary directory location private static final File tmpdir = new File( - GetPropertyAction.getProperty("java.io.tmpdir")); + GetPropertyAction.privilegedGetProperty("java.io.tmpdir")); static File location() { return tmpdir; } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/Class.java --- a/jdk/src/java.base/share/classes/java/lang/Class.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/Class.java Tue May 03 12:25:20 2016 -0700 @@ -470,7 +470,7 @@ * expression with an empty argument list. The class is initialized if it * has not already been initialized. * - *

Note that this method propagates any exception thrown by the + * @deprecated This method propagates any exception thrown by the * nullary constructor, including a checked exception. Use of * this method effectively bypasses the compile-time exception * checking that would otherwise be performed by the compiler. @@ -500,6 +500,7 @@ * of this class. */ @CallerSensitive + @Deprecated(since="9") public T newInstance() throws InstantiationException, IllegalAccessException { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/ClassLoader.java --- a/jdk/src/java.base/share/classes/java/lang/ClassLoader.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/ClassLoader.java Tue May 03 12:25:20 2016 -0700 @@ -2615,7 +2615,7 @@ ServicesCatalog createOrGetServicesCatalog() { ServicesCatalog catalog = servicesCatalog; if (catalog == null) { - catalog = new ServicesCatalog(); + catalog = ServicesCatalog.create(); boolean set = trySetObjectField("servicesCatalog", catalog); if (!set) { // beaten by someone else diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/ProcessBuilder.java --- a/jdk/src/java.base/share/classes/java/lang/ProcessBuilder.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/ProcessBuilder.java Tue May 03 12:25:20 2016 -0700 @@ -468,7 +468,7 @@ */ public abstract static class Redirect { private static final File NULL_FILE = new File( - (GetPropertyAction.getProperty("os.name") + (GetPropertyAction.privilegedGetProperty("os.name") .startsWith("Windows") ? "NUL" : "/dev/null") ); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/StackStreamFactory.java --- a/jdk/src/java.base/share/classes/java/lang/StackStreamFactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/StackStreamFactory.java Tue May 03 12:25:20 2016 -0700 @@ -78,7 +78,8 @@ * Performance work and extensive testing is needed to replace the * VM built-in backtrace filled in Throwable with the StackWalker. */ - final static boolean isDebug = getProperty("stackwalk.debug", false); + final static boolean isDebug = + "true".equals(GetPropertyAction.privilegedGetProperty("stackwalk.debug")); static StackFrameTraverser makeStackTraverser(StackWalker walker, Function, ? extends T> function) @@ -988,11 +989,4 @@ c.getName().startsWith("java.lang.invoke.LambdaForm"); } - private static boolean getProperty(String key, boolean value) { - String s = GetPropertyAction.getProperty(key); - if (s != null) { - return Boolean.parseBoolean(s); - } - return value; - } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/String.java --- a/jdk/src/java.base/share/classes/java/lang/String.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/String.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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 @@ -78,14 +78,8 @@ *

* The Java language provides special support for the string * concatenation operator ( + ), and for conversion of - * other objects to strings. String concatenation is implemented - * through the {@code StringBuilder}(or {@code StringBuffer}) - * class and its {@code append} method. - * String conversions are implemented through the method - * {@code toString}, defined by {@code Object} and - * inherited by all classes in Java. For additional information on - * string concatenation and conversion, see Gosling, Joy, and Steele, - * The Java Language Specification. + * other objects to strings. For additional information on string + * concatenation and conversion, see The Java™ Language Specification. * *

Unless otherwise noted, passing a {@code null} argument to a constructor * or method in this class will cause a {@link NullPointerException} to be @@ -106,6 +100,14 @@ * into account. The {@link java.text.Collator} class provides methods for * finer-grain, locale-sensitive String comparison. * + * @implNote The implementation of the string concatenation operator is left to + * the discretion of a Java compiler, as long as the compiler ultimately conforms + * to The Java™ Language Specification. For example, the {@code javac} compiler + * may implement the operator with {@code StringBuffer}, {@code StringBuilder}, + * or {@code java.lang.invoke.StringConcatFactory} depending on the JDK version. The + * implementation of string conversion is typically through the method {@code toString}, + * defined by {@code Object} and inherited by all classes in Java. + * * @author Lee Boynton * @author Arthur van Hoff * @author Martin Buchholz @@ -115,6 +117,7 @@ * @see java.lang.StringBuilder * @see java.nio.charset.Charset * @since 1.0 + * @jls 15.18.1 String Concatenation Operator + */ public final class String @@ -2977,6 +2980,7 @@ * * @return a string that has the same contents as this string, but is * guaranteed to be from a pool of unique strings. + * @jls 3.10.5 String Literals */ public native String intern(); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/System.java --- a/jdk/src/java.base/share/classes/java/lang/System.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/System.java Tue May 03 12:25:20 2016 -0700 @@ -69,7 +69,6 @@ import jdk.internal.logger.LocalizedLoggerWrapper; import jdk.internal.module.ModuleBootstrap; -import jdk.internal.module.Modules; import jdk.internal.module.ServicesCatalog; /** @@ -1924,10 +1923,6 @@ // initialize the module system System.bootLayer = ModuleBootstrap.boot(); - // base module needs to be loose (CODETOOLS-7901619) - Module base = Object.class.getModule(); - Modules.addReads(base, null); - // module system initialized VM.initLevel(2); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Tue May 03 12:25:20 2016 -0700 @@ -88,7 +88,7 @@ static { final String key = "jdk.internal.lambda.dumpProxyClasses"; - String path = GetPropertyAction.getProperty(key); + String path = GetPropertyAction.privilegedGetProperty(key); dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java Tue May 03 12:25:20 2016 -0700 @@ -53,7 +53,7 @@ static final boolean VAR_HANDLE_GUARDS; static { - Properties props = GetPropertyAction.getProperties(); + Properties props = GetPropertyAction.privilegedGetProperties(); DEBUG_METHOD_HANDLE_NAMES = Boolean.parseBoolean( props.getProperty("java.lang.invoke.MethodHandle.DEBUG_NAMES")); DUMP_CLASS_FILES = Boolean.parseBoolean( diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java Tue May 03 12:25:20 2016 -0700 @@ -197,7 +197,7 @@ // DEBUG = false; // implied // DUMPER = null; // implied - Properties props = GetPropertyAction.getProperties(); + Properties props = GetPropertyAction.privilegedGetProperties(); final String strategy = props.getProperty("java.lang.invoke.stringConcat"); CACHE_ENABLE = Boolean.parseBoolean( diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/invoke/VarHandle.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/VarHandle.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/VarHandle.java Tue May 03 12:25:20 2016 -0700 @@ -136,6 +136,7 @@ * consists of the methods * {@link #compareAndSet compareAndSet}, * {@link #weakCompareAndSet weakCompareAndSet}, + * {@link #weakCompareAndSetVolatile weakCompareAndSetVolatile}, * {@link #weakCompareAndSetAcquire weakCompareAndSetAcquire}, * {@link #weakCompareAndSetRelease weakCompareAndSetRelease}, * {@link #compareAndExchangeAcquire compareAndExchangeAcquire}, @@ -458,7 +459,7 @@ * *

The symbolic type descriptor at the call site of {@code get} * must match the access mode type that is the result of calling - * {@code accessModeType(VarHandle.AccessMode.get)} on this VarHandle. + * {@code accessModeType(VarHandle.AccessMode.GET)} on this VarHandle. * *

This access mode is supported by all VarHandle instances and never * throws {@code UnsupportedOperationException}. @@ -488,7 +489,7 @@ * *

The symbolic type descriptor at the call site of {@code set} * must match the access mode type that is the result of calling - * {@code accessModeType(VarHandle.AccessMode.set)} on this VarHandle. + * {@code accessModeType(VarHandle.AccessMode.SET)} on this VarHandle. * * @param args the signature-polymorphic parameter list of the form * {@code (CT, T newValue)} @@ -516,7 +517,8 @@ * *

The symbolic type descriptor at the call site of {@code getVolatile} * must match the access mode type that is the result of calling - * {@code accessModeType(VarHandle.AccessMode.getVolatile)} on this VarHandle. + * {@code accessModeType(VarHandle.AccessMode.GET_VOLATILE)} on this + * VarHandle. * * @param args the signature-polymorphic parameter list of the form * {@code (CT)} @@ -544,7 +546,8 @@ * *

The symbolic type descriptor at the call site of {@code setVolatile} * must match the access mode type that is the result of calling - * {@code accessModeType(VarHandle.AccessMode.setVolatile)} on this VarHandle. + * {@code accessModeType(VarHandle.AccessMode.SET_VOLATILE)} on this + * VarHandle. * * @apiNote * Ignoring the many semantic differences from C and C++, this method has @@ -574,7 +577,8 @@ * *

The symbolic type descriptor at the call site of {@code getOpaque} * must match the access mode type that is the result of calling - * {@code accessModeType(VarHandle.AccessMode.getOpaque)} on this VarHandle. + * {@code accessModeType(VarHandle.AccessMode.GET_OPAQUE)} on this + * VarHandle. * * @param args the signature-polymorphic parameter list of the form * {@code (CT)} @@ -603,7 +607,8 @@ * *

The symbolic type descriptor at the call site of {@code setOpaque} * must match the access mode type that is the result of calling - * {@code accessModeType(VarHandle.AccessMode.setOpaque)} on this VarHandle. + * {@code accessModeType(VarHandle.AccessMode.SET_OPAQUE)} on this + * VarHandle. * * @param args the signature-polymorphic parameter list of the form * {@code (CT, T newValue)} @@ -631,7 +636,8 @@ * *

The symbolic type descriptor at the call site of {@code getAcquire} * must match the access mode type that is the result of calling - * {@code accessModeType(VarHandle.AccessMode.getAcquire)} on this VarHandle. + * {@code accessModeType(VarHandle.AccessMode.GET_ACQUIRE)} on this + * VarHandle. * * @apiNote * Ignoring the many semantic differences from C and C++, this method has @@ -664,7 +670,8 @@ * *

The symbolic type descriptor at the call site of {@code setRelease} * must match the access mode type that is the result of calling - * {@code accessModeType(VarHandle.AccessMode.setRelease)} on this VarHandle. + * {@code accessModeType(VarHandle.AccessMode.SET_RELEASE)} on this + * VarHandle. * * @apiNote * Ignoring the many semantic differences from C and C++, this method has @@ -700,7 +707,7 @@ * *

The symbolic type descriptor at the call site of {@code * compareAndSet} must match the access mode type that is the result of - * calling {@code accessModeType(VarHandle.AccessMode.compareAndSet)} on + * calling {@code accessModeType(VarHandle.AccessMode.COMPARE_AND_SET)} on * this VarHandle. * * @param args the signature-polymorphic parameter list of the form @@ -734,7 +741,7 @@ *

The symbolic type descriptor at the call site of {@code * compareAndExchangeVolatile} * must match the access mode type that is the result of calling - * {@code accessModeType(VarHandle.AccessMode.compareAndExchangeVolatile)} + * {@code accessModeType(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_VOLATILE)} * on this VarHandle. * * @param args the signature-polymorphic parameter list of the form @@ -769,7 +776,7 @@ *

The symbolic type descriptor at the call site of {@code * compareAndExchangeAcquire} * must match the access mode type that is the result of calling - * {@code accessModeType(VarHandle.AccessMode.compareAndExchangeAcquire)} on + * {@code accessModeType(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)} on * this VarHandle. * * @param args the signature-polymorphic parameter list of the form @@ -804,7 +811,8 @@ *

The symbolic type descriptor at the call site of {@code * compareAndExchangeRelease} * must match the access mode type that is the result of calling - * {@code accessModeType(VarHandle.AccessMode.compareAndExchangeRelease)} on this VarHandle. + * {@code accessModeType(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)} + * on this VarHandle. * * @param args the signature-polymorphic parameter list of the form * {@code (CT, T expectedValue, T newValue)} @@ -836,14 +844,14 @@ * {@link #get}. * *

This operation may fail spuriously (typically, due to memory - * contention) even if the current value does match the expected value. + * contention) even if the witness value does match the expected value. * *

The method signature is of the form {@code (CT, T expectedValue, T newValue)boolean}. * *

The symbolic type descriptor at the call site of {@code * weakCompareAndSet} must match the access mode type that is the result of - * calling {@code accessModeType(VarHandle.AccessMode.weakCompareAndSet)} on - * this VarHandle. + * calling {@code accessModeType(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)} + * on this VarHandle. * * @param args the signature-polymorphic parameter list of the form * {@code (CT, T expectedValue, T newValue)} @@ -867,20 +875,58 @@ /** * Possibly atomically sets the value of a variable to the {@code newValue} + * with the memory semantics of {@link #setVolatile} if the variable's + * current value, referred to as the witness value, {@code ==} the + * {@code expectedValue}, as accessed with the memory semantics of + * {@link #getVolatile}. + * + *

This operation may fail spuriously (typically, due to memory + * contention) even if the witness value does match the expected value. + * + *

The method signature is of the form {@code (CT, T expectedValue, T newValue)boolean}. + * + *

The symbolic type descriptor at the call site of {@code + * weakCompareAndSetVolatile} must match the access mode type that is the + * result of calling {@code accessModeType(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)} + * on this VarHandle. + * + * @param args the signature-polymorphic parameter list of the form + * {@code (CT, T expectedValue, T newValue)} + * , statically represented using varargs. + * @return {@code true} if successful, otherwise {@code false} if the + * witness value was not the same as the {@code expectedValue} or if this + * operation spuriously failed. + * @throws UnsupportedOperationException if the access mode is unsupported + * for this VarHandle. + * @throws WrongMethodTypeException if the access mode type is not + * compatible with the caller's symbolic type descriptor. + * @throws ClassCastException if the access mode type is compatible with the + * caller's symbolic type descriptor, but a reference cast fails. + * @see #setVolatile(Object...) + * @see #getVolatile(Object...) + */ + public final native + @MethodHandle.PolymorphicSignature + @HotSpotIntrinsicCandidate + boolean weakCompareAndSetVolatile(Object... args); + + /** + * Possibly atomically sets the value of a variable to the {@code newValue} * with the semantics of {@link #set} if the variable's current value, * referred to as the witness value, {@code ==} the * {@code expectedValue}, as accessed with the memory semantics of * {@link #getAcquire}. * *

This operation may fail spuriously (typically, due to memory - * contention) even if the current value does match the expected value. + * contention) even if the witness value does match the expected value. * *

The method signature is of the form {@code (CT, T expectedValue, T newValue)boolean}. * *

The symbolic type descriptor at the call site of {@code * weakCompareAndSetAcquire} * must match the access mode type that is the result of calling - * {@code accessModeType(VarHandle.AccessMode.weakCompareAndSetAcquire)} on this VarHandle. + * {@code accessModeType(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)} + * on this VarHandle. * * @param args the signature-polymorphic parameter list of the form * {@code (CT, T expectedValue, T newValue)} @@ -910,14 +956,15 @@ * {@link #get}. * *

This operation may fail spuriously (typically, due to memory - * contention) even if the current value does match the expected value. + * contention) even if the witness value does match the expected value. * *

The method signature is of the form {@code (CT, T expectedValue, T newValue)boolean}. * *

The symbolic type descriptor at the call site of {@code * weakCompareAndSetRelease} * must match the access mode type that is the result of calling - * {@code accessModeType(VarHandle.AccessMode.weakCompareAndSetRelease)} on this VarHandle. + * {@code accessModeType(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)} + * on this VarHandle. * * @param args the signature-polymorphic parameter list of the form * {@code (CT, T expectedValue, T newValue)} @@ -949,7 +996,8 @@ * *

The symbolic type descriptor at the call site of {@code getAndSet} * must match the access mode type that is the result of calling - * {@code accessModeType(VarHandle.AccessMode.getAndSet)} on this VarHandle. + * {@code accessModeType(VarHandle.AccessMode.GET_AND_SET)} on this + * VarHandle. * * @param args the signature-polymorphic parameter list of the form * {@code (CT, T newValue)} @@ -985,7 +1033,8 @@ * *

The symbolic type descriptor at the call site of {@code getAndAdd} * must match the access mode type that is the result of calling - * {@code accessModeType(VarHandle.AccessMode.getAndAdd)} on this VarHandle. + * {@code accessModeType(VarHandle.AccessMode.GET_AND_ADD)} on this + * VarHandle. * * @param args the signature-polymorphic parameter list of the form * {@code (CT, T value)} @@ -1017,7 +1066,8 @@ * *

The symbolic type descriptor at the call site of {@code addAndGet} * must match the access mode type that is the result of calling - * {@code accessModeType(VarHandle.AccessMode.addAndGet)} on this VarHandle. + * {@code accessModeType(VarHandle.AccessMode.ADD_AND_GET)} on this + * VarHandle. * * @param args the signature-polymorphic parameter list of the form * {@code (CT, T value)} @@ -1083,109 +1133,115 @@ * method * {@link VarHandle#get VarHandle.get} */ - GET("get", AccessType.GET, Object.class), // 0 + GET("get", AccessType.GET, Object.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#set VarHandle.set} */ - SET("set", AccessType.SET, void.class), // 1 + SET("set", AccessType.SET, void.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#getVolatile VarHandle.getVolatile} */ - GET_VOLATILE("getVolatile", AccessType.GET, Object.class), // 2 + GET_VOLATILE("getVolatile", AccessType.GET, Object.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#setVolatile VarHandle.setVolatile} */ - SET_VOLATILE("setVolatile", AccessType.SET, void.class), // 3 + SET_VOLATILE("setVolatile", AccessType.SET, void.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#getAcquire VarHandle.getAcquire} */ - GET_ACQUIRE("getAcquire", AccessType.GET, Object.class), // 4 + GET_ACQUIRE("getAcquire", AccessType.GET, Object.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#setRelease VarHandle.setRelease} */ - SET_RELEASE("setRelease", AccessType.SET, void.class), // 5 + SET_RELEASE("setRelease", AccessType.SET, void.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#getOpaque VarHandle.getOpaque} */ - GET_OPAQUE("getOpaque", AccessType.GET, Object.class), // 6 + GET_OPAQUE("getOpaque", AccessType.GET, Object.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#setOpaque VarHandle.setOpaque} */ - SET_OPAQUE("setOpaque", AccessType.SET, void.class), // 7 + SET_OPAQUE("setOpaque", AccessType.SET, void.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#compareAndSet VarHandle.compareAndSet} */ - COMPARE_AND_SET("compareAndSet", AccessType.COMPARE_AND_SWAP, boolean.class), // 8 + COMPARE_AND_SET("compareAndSet", AccessType.COMPARE_AND_SWAP, boolean.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#compareAndExchangeVolatile VarHandle.compareAndExchangeVolatile} */ - COMPARE_AND_EXCHANGE_VOLATILE("compareAndExchangeVolatile", AccessType.COMPARE_AND_EXCHANGE, Object.class), // 9 + COMPARE_AND_EXCHANGE_VOLATILE("compareAndExchangeVolatile", AccessType.COMPARE_AND_EXCHANGE, Object.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#compareAndExchangeAcquire VarHandle.compareAndExchangeAcquire} */ - COMPARE_AND_EXCHANGE_ACQUIRE("compareAndExchangeAcquire", AccessType.COMPARE_AND_EXCHANGE, Object.class), // 10 + COMPARE_AND_EXCHANGE_ACQUIRE("compareAndExchangeAcquire", AccessType.COMPARE_AND_EXCHANGE, Object.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#compareAndExchangeRelease VarHandle.compareAndExchangeRelease} */ - COMPARE_AND_EXCHANGE_RELEASE("compareAndExchangeRelease", AccessType.COMPARE_AND_EXCHANGE, Object.class), // 11 + COMPARE_AND_EXCHANGE_RELEASE("compareAndExchangeRelease", AccessType.COMPARE_AND_EXCHANGE, Object.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#weakCompareAndSet VarHandle.weakCompareAndSet} */ - WEAK_COMPARE_AND_SET("weakCompareAndSet", AccessType.COMPARE_AND_SWAP, boolean.class), // 12 + WEAK_COMPARE_AND_SET("weakCompareAndSet", AccessType.COMPARE_AND_SWAP, boolean.class), + /** + * The access mode whose access is specified by the corresponding + * method + * {@link VarHandle#weakCompareAndSetVolatile VarHandle.weakCompareAndSetVolatile} + */ + WEAK_COMPARE_AND_SET_VOLATILE("weakCompareAndSetVolatile", AccessType.COMPARE_AND_SWAP, boolean.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#weakCompareAndSetAcquire VarHandle.weakCompareAndSetAcquire} */ - WEAK_COMPARE_AND_SET_ACQUIRE("weakCompareAndSetAcquire", AccessType.COMPARE_AND_SWAP, boolean.class), // 13 + WEAK_COMPARE_AND_SET_ACQUIRE("weakCompareAndSetAcquire", AccessType.COMPARE_AND_SWAP, boolean.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#weakCompareAndSetRelease VarHandle.weakCompareAndSetRelease} */ - WEAK_COMPARE_AND_SET_RELEASE("weakCompareAndSetRelease", AccessType.COMPARE_AND_SWAP, boolean.class), // 14 + WEAK_COMPARE_AND_SET_RELEASE("weakCompareAndSetRelease", AccessType.COMPARE_AND_SWAP, boolean.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#getAndSet VarHandle.getAndSet} */ - GET_AND_SET("getAndSet", AccessType.GET_AND_UPDATE, Object.class), // 15 + GET_AND_SET("getAndSet", AccessType.GET_AND_UPDATE, Object.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#getAndAdd VarHandle.getAndAdd} */ - GET_AND_ADD("getAndAdd", AccessType.GET_AND_UPDATE, Object.class), // 16 + GET_AND_ADD("getAndAdd", AccessType.GET_AND_UPDATE, Object.class), /** * The access mode whose access is specified by the corresponding * method * {@link VarHandle#addAndGet VarHandle.addAndGet} */ - ADD_AND_GET("addAndGet", AccessType.GET_AND_UPDATE, Object.class), // 17 + ADD_AND_GET("addAndGet", AccessType.GET_AND_UPDATE, Object.class), ; static final Map methodNameToAccessMode; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template --- a/jdk/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template Tue May 03 12:25:20 2016 -0700 @@ -155,6 +155,15 @@ } @ForceInline + static boolean weakCompareAndSetVolatile(FieldInstanceReadWrite handle, Object holder, $type$ expected, $type$ value) { + // TODO defer to strong form until new Unsafe method is added + return UNSAFE.compareAndSwap$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)), + handle.fieldOffset, + {#if[Object]?handle.fieldType.cast(expected):expected}, + {#if[Object]?handle.fieldType.cast(value):value}); + } + + @ForceInline static boolean weakCompareAndSetAcquire(FieldInstanceReadWrite handle, Object holder, $type$ expected, $type$ value) { return UNSAFE.weakCompareAndSwap$Type$Acquire(Objects.requireNonNull(handle.receiverType.cast(holder)), handle.fieldOffset, @@ -319,6 +328,15 @@ } @ForceInline + static boolean weakCompareAndSetVolatile(FieldStaticReadWrite handle, $type$ expected, $type$ value) { + // TODO defer to strong form until new Unsafe method is added + return UNSAFE.compareAndSwap$Type$(handle.base, + handle.fieldOffset, + {#if[Object]?handle.fieldType.cast(expected):expected}, + {#if[Object]?handle.fieldType.cast(value):value}); + } + + @ForceInline static boolean weakCompareAndSetAcquire(FieldStaticReadWrite handle, $type$ expected, $type$ value) { return UNSAFE.weakCompareAndSwap$Type$Acquire(handle.base, handle.fieldOffset, @@ -535,6 +553,20 @@ } @ForceInline + static boolean weakCompareAndSetVolatile(Array handle, Object oarray, int index, $type$ expected, $type$ value) { +#if[Object] + Object[] array = (Object[]) handle.arrayType.cast(oarray); +#else[Object] + $type$[] array = ($type$[]) oarray; +#end[Object] + // TODO defer to strong form until new Unsafe method is added + return UNSAFE.compareAndSwap$Type$(array, + (((long) Objects.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase, + {#if[Object]?handle.componentType.cast(expected):expected}, + {#if[Object]?handle.componentType.cast(value):value}); + } + + @ForceInline static boolean weakCompareAndSetAcquire(Array handle, Object oarray, int index, $type$ expected, $type$ value) { #if[Object] Object[] array = (Object[]) handle.arrayType.cast(oarray); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template --- a/jdk/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template Tue May 03 12:25:20 2016 -0700 @@ -228,6 +228,16 @@ } @ForceInline + static boolean weakCompareAndSetVolatile(ArrayHandle handle, Object oba, int index, $type$ expected, $type$ value) { + byte[] ba = (byte[]) oba; + // TODO defer to strong form until new Unsafe method is added + return UNSAFE.compareAndSwap$RawType$( + ba, + address(ba, index(ba, index)), + convEndian(handle.be, expected), convEndian(handle.be, value)); + } + + @ForceInline static boolean weakCompareAndSetAcquire(ArrayHandle handle, Object oba, int index, $type$ expected, $type$ value) { byte[] ba = (byte[]) oba; return UNSAFE.weakCompareAndSwap$RawType$Acquire( @@ -444,6 +454,16 @@ } @ForceInline + static boolean weakCompareAndSetVolatile(ByteBufferHandle handle, Object obb, int index, $type$ expected, $type$ value) { + ByteBuffer bb = (ByteBuffer) obb; + // TODO defer to strong form until new Unsafe method is added + return UNSAFE.compareAndSwap$RawType$( + UNSAFE.getObject(bb, BYTE_BUFFER_HB), + address(bb, indexRO(bb, index)), + convEndian(handle.be, expected), convEndian(handle.be, value)); + } + + @ForceInline static boolean weakCompareAndSetAcquire(ByteBufferHandle handle, Object obb, int index, $type$ expected, $type$ value) { ByteBuffer bb = (ByteBuffer) obb; return UNSAFE.weakCompareAndSwap$RawType$Acquire( diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/module/Configuration.java --- a/jdk/src/java.base/share/classes/java/lang/module/Configuration.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/module/Configuration.java Tue May 03 12:25:20 2016 -0700 @@ -25,6 +25,7 @@ package java.lang.module; +import java.io.PrintStream; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -183,17 +184,20 @@ this.nameToModule = Collections.emptyMap(); } - private Configuration(Configuration parent, Resolver resolver) { - Map> graph = resolver.finish(this); + private Configuration(Configuration parent, + Resolver resolver, + boolean check) + { + Map> g = resolver.finish(this, check); Map nameToModule = new HashMap<>(); - for (ResolvedModule resolvedModule : graph.keySet()) { + for (ResolvedModule resolvedModule : g.keySet()) { nameToModule.put(resolvedModule.name(), resolvedModule); } this.parent = parent; - this.graph = graph; - this.modules = Collections.unmodifiableSet(graph.keySet()); + this.graph = g; + this.modules = Collections.unmodifiableSet(g.keySet()); this.nameToModule = Collections.unmodifiableMap(nameToModule); } @@ -283,10 +287,10 @@ Objects.requireNonNull(after); Objects.requireNonNull(roots); - Resolver resolver = new Resolver(before, this, after); + Resolver resolver = new Resolver(before, this, after, null); resolver.resolveRequires(roots); - return new Configuration(this, resolver); + return new Configuration(this, resolver, true); } @@ -340,10 +344,32 @@ Objects.requireNonNull(after); Objects.requireNonNull(roots); - Resolver resolver = new Resolver(before, this, after); + Resolver resolver = new Resolver(before, this, after, null); resolver.resolveRequires(roots).resolveUses(); - return new Configuration(this, resolver); + return new Configuration(this, resolver, true); + } + + + /** + * Resolves a collection of root modules, with service binding, and with + * the empty configuration as its parent. The post resolution checks + * are optionally run. + * + * This method is used to create the configuration for the boot layer. + */ + static Configuration resolveRequiresAndUses(ModuleFinder finder, + Collection roots, + boolean check, + PrintStream traceOutput) + { + Configuration parent = empty(); + + Resolver resolver + = new Resolver(finder, parent, ModuleFinder.empty(), traceOutput); + resolver.resolveRequires(roots).resolveUses(); + + return new Configuration(parent, resolver, check); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java --- a/jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java Tue May 03 12:25:20 2016 -0700 @@ -27,13 +27,17 @@ import java.io.InputStream; import java.io.IOException; +import java.io.PrintStream; import java.io.UncheckedIOException; +import java.net.URI; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -45,7 +49,7 @@ import static java.util.Objects.*; import jdk.internal.module.Checks; -import jdk.internal.module.Hasher.DependencyHashes; +import jdk.internal.module.ModuleHashes; /** @@ -372,8 +376,9 @@ private Provides(String service, Set providers, boolean check) { this.service = check ? requireServiceTypeName(service) : service; - providers = check ? Collections.unmodifiableSet(new HashSet<>(providers)) - : Collections.unmodifiableSet(providers); + providers = check + ? Collections.unmodifiableSet(new LinkedHashSet<>(providers)) + : Collections.unmodifiableSet(providers); if (providers.isEmpty()) throw new IllegalArgumentException("Empty providers set"); if (check) @@ -787,7 +792,7 @@ private final String osVersion; private final Set conceals; private final Set packages; - private final DependencyHashes hashes; + private final ModuleHashes hashes; private ModuleDescriptor(String name, boolean automatic, @@ -802,7 +807,7 @@ String osArch, String osVersion, Set conceals, - DependencyHashes hashes) + ModuleHashes hashes) { this.name = name; @@ -878,7 +883,8 @@ String osArch, String osVersion, Set conceals, - Set packages) { + Set packages, + ModuleHashes hashes) { this.name = name; this.automatic = automatic; this.synthetic = synthetic; @@ -894,7 +900,7 @@ this.osName = osName; this.osArch = osArch; this.osVersion = osVersion; - this.hashes = null; + this.hashes = hashes; } /** @@ -1063,9 +1069,9 @@ } /** - * Returns the object with the hashes of the dependences. + * Returns the object with the hashes of other modules */ - Optional hashes() { + Optional hashes() { return Optional.ofNullable(hashes); } @@ -1103,7 +1109,7 @@ String osArch; String osVersion; String mainClass; - DependencyHashes hashes; + ModuleHashes hashes; /** * Initializes a new builder with the given module name. @@ -1580,7 +1586,7 @@ return this; } - /* package */ Builder hashes(DependencyHashes hashes) { + /* package */ Builder hashes(ModuleHashes hashes) { this.hashes = hashes; return this; } @@ -1719,7 +1725,9 @@ hc = hc * 43 + Objects.hashCode(osVersion); hc = hc * 43 + Objects.hashCode(conceals); hc = hc * 43 + Objects.hashCode(hashes); - if (hc != 0) hash = hc; + if (hc == 0) + hc = -1; + hash = hc; } return hc; } @@ -1925,11 +1933,12 @@ static { /** - * Setup the shared secret to allow code in other packages create - * ModuleDescriptor and associated objects directly. + * Setup the shared secret to allow code in other packages access + * private package methods in java.lang.module. */ jdk.internal.misc.SharedSecrets .setJavaLangModuleAccess(new jdk.internal.misc.JavaLangModuleAccess() { + @Override public Requires newRequires(Set ms, String mn) { return new Requires(ms, mn, false); @@ -1974,7 +1983,8 @@ String osArch, String osVersion, Set conceals, - Set packages) { + Set packages, + ModuleHashes hashes) { return new ModuleDescriptor(name, automatic, synthetic, @@ -1988,7 +1998,29 @@ osArch, osVersion, conceals, - packages); + packages, + hashes); + } + + @Override + public Configuration resolveRequiresAndUses(ModuleFinder finder, + Collection roots, + boolean check, + PrintStream traceOutput) + { + return Configuration.resolveRequiresAndUses(finder, roots, check, traceOutput); + } + + @Override + public ModuleReference newPatchedModule(ModuleDescriptor descriptor, + URI location, + Supplier s) { + return new ModuleReference(descriptor, location, s, true, null); + } + + @Override + public Optional hashes(ModuleDescriptor descriptor) { + return descriptor.hashes(); } }); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/module/ModuleFinder.java diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/module/ModuleInfo.java --- a/jdk/src/java.base/share/classes/java/lang/module/ModuleInfo.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleInfo.java Tue May 03 12:25:20 2016 -0700 @@ -37,11 +37,12 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.function.Supplier; -import jdk.internal.module.Hasher.DependencyHashes; +import jdk.internal.module.ModuleHashes; import static jdk.internal.module.ClassFileConstants.*; @@ -337,7 +338,7 @@ // computeIfAbsent Set providers = pm.get(sn); if (providers == null) { - providers = new HashSet<>(); + providers = new LinkedHashSet<>(); // preserve order pm.put(sn, providers); } providers.add(cn); @@ -425,7 +426,7 @@ map.put(dn, hash); } - builder.hashes(new DependencyHashes(algorithm, map)); + builder.hashes(new ModuleHashes(algorithm, map)); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/module/ModulePath.java --- a/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java Tue May 03 12:25:20 2016 -0700 @@ -40,7 +40,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -52,7 +52,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -190,18 +189,16 @@ } } - if (attrs.isRegularFile() || attrs.isDirectory()) { - // packaged or exploded module - ModuleReference mref = readModule(entry, attrs); - if (mref != null) { - String name = mref.descriptor().name(); - return Collections.singletonMap(name, mref); - } + // packaged or exploded module + ModuleReference mref = readModule(entry, attrs); + if (mref != null) { + String name = mref.descriptor().name(); + return Collections.singletonMap(name, mref); + } else { + // skipped + return Collections.emptyMap(); } - // not recognized - throw new FindException("Unrecognized module: " + entry); - } catch (IOException ioe) { throw new FindException(ioe); } @@ -238,16 +235,13 @@ // module found if (mref != null) { - // can have at most one version of a module in the directory String name = mref.descriptor().name(); if (nameToReference.put(name, mref) != null) { throw new FindException("Two versions of module " - + name + " found in " + dir); + + name + " found in " + dir); } - } - } } @@ -257,28 +251,40 @@ /** * Locates a packaged or exploded module, returning a {@code ModuleReference} - * to the module. Returns {@code null} if the module is not recognized - * as a packaged or exploded module. + * to the module. Returns {@code null} if the entry is skipped because it is + * to a directory that does not contain a module-info.class or it's a hidden + * file. * * @throws IOException if an I/O error occurs - * @throws FindException if an error occurs parsing the module descriptor + * @throws FindException if the file is not recognized as a module or an + * error occurs parsing its module descriptor */ private ModuleReference readModule(Path entry, BasicFileAttributes attrs) throws IOException { try { - ModuleReference mref = null; if (attrs.isDirectory()) { - mref = readExplodedModule(entry); - } if (attrs.isRegularFile()) { - if (entry.toString().endsWith(".jar")) { - mref = readJar(entry); - } else if (isLinkPhase && entry.toString().endsWith(".jmod")) { - mref = readJMod(entry); + return readExplodedModule(entry); // may return null + } + + String fn = entry.getFileName().toString(); + if (attrs.isRegularFile()) { + if (fn.endsWith(".jar")) { + return readJar(entry); + } else if (fn.endsWith(".jmod")) { + if (isLinkPhase) + return readJMod(entry); + throw new FindException("JMOD files not supported: " + entry); } } - return mref; + + // skip hidden files + if (fn.startsWith(".") || Files.isHidden(entry)) { + return null; + } else { + throw new FindException("Unrecognized module: " + entry); + } } catch (InvalidModuleDescriptorException e) { throw new FindException("Error reading module: " + entry, e); @@ -292,15 +298,17 @@ return zf.stream() .filter(e -> e.getName().startsWith("classes/") && e.getName().endsWith(".class")) - .map(e -> toPackageName(e)) + .map(e -> toPackageName(e.getName().substring(8))) .filter(pkg -> pkg.length() > 0) // module-info - .distinct() .collect(Collectors.toSet()); } /** * Returns a {@code ModuleReference} to a module in jmod file on the * file system. + * + * @throws IOException + * @throws InvalidModuleDescriptorException */ private ModuleReference readJMod(Path file) throws IOException { try (ZipFile zf = new ZipFile(file.toString())) { @@ -419,13 +427,12 @@ // scan the entries in the JAR file to locate the .class and service // configuration file - Stream stream = jf.stream() - .map(e -> e.getName()) - .filter(e -> (e.endsWith(".class") || e.startsWith(SERVICES_PREFIX))) - .distinct(); - Map> map - = stream.collect(Collectors.partitioningBy(s -> s.endsWith(".class"), - Collectors.toSet())); + Map> map = + jf.stream() + .map(JarEntry::getName) + .filter(s -> (s.endsWith(".class") ^ s.startsWith(SERVICES_PREFIX))) + .collect(Collectors.partitioningBy(s -> s.endsWith(".class"), + Collectors.toSet())); Set classFiles = map.get(Boolean.TRUE); Set configFiles = map.get(Boolean.FALSE); @@ -433,19 +440,18 @@ classFiles.stream() .map(c -> toPackageName(c)) .distinct() - .forEach(p -> builder.exports(p)); + .forEach(builder::exports); // map names of service configuration files to service names Set serviceNames = configFiles.stream() .map(this::toServiceName) - .filter(Optional::isPresent) - .map(Optional::get) + .flatMap(Optional::stream) .collect(Collectors.toSet()); // parse each service configuration file for (String sn : serviceNames) { JarEntry entry = jf.getJarEntry(SERVICES_PREFIX + sn); - Set providerClasses = new HashSet<>(); + Set providerClasses = new LinkedHashSet<>(); try (InputStream in = jf.getInputStream(entry)) { BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); @@ -475,19 +481,25 @@ private Set jarPackages(JarFile jf) { return jf.stream() .filter(e -> e.getName().endsWith(".class")) - .map(e -> toPackageName(e)) + .map(e -> toPackageName(e.getName())) .filter(pkg -> pkg.length() > 0) // module-info - .distinct() .collect(Collectors.toSet()); } /** * Returns a {@code ModuleReference} to a module in modular JAR file on * the file system. + * + * @throws IOException + * @throws FindException + * @throws InvalidModuleDescriptorException */ private ModuleReference readJar(Path file) throws IOException { - try (JarFile jf = new JarFile(file.toString())) { - + try (JarFile jf = new JarFile(file.toFile(), + true, // verify + ZipFile.OPEN_READ, + JarFile.Release.RUNTIME)) + { ModuleDescriptor md; JarEntry entry = jf.getJarEntry(MODULE_INFO); if (entry == null) { @@ -520,7 +532,6 @@ path.toString().endsWith(".class"))) .map(path -> toPackageName(dir.relativize(path))) .filter(pkg -> pkg.length() > 0) // module-info - .distinct() .collect(Collectors.toSet()); } catch (IOException x) { throw new UncheckedIOException(x); @@ -530,6 +541,9 @@ /** * Returns a {@code ModuleReference} to an exploded module on the file * system or {@code null} if {@code module-info.class} not found. + * + * @throws IOException + * @throws InvalidModuleDescriptorException */ private ModuleReference readExplodedModule(Path dir) throws IOException { Path mi = dir.resolve(MODULE_INFO); @@ -559,19 +573,6 @@ } } - private String toPackageName(ZipEntry entry) { - String name = entry.getName(); - assert name.endsWith(".class"); - // jmod classes in classes/, jar in / - int start = name.startsWith("classes/") ? 8 : 0; - int index = name.lastIndexOf("/"); - if (index > start) { - return name.substring(start, index).replace('/', '.'); - } else { - return ""; - } - } - private String toPackageName(Path path) { String name = path.toString(); assert name.endsWith(".class"); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/module/ModuleReader.java --- a/jdk/src/java.base/share/classes/java/lang/module/ModuleReader.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleReader.java Tue May 03 12:25:20 2016 -0700 @@ -142,10 +142,11 @@ * @see ClassLoader#defineClass(String, ByteBuffer, java.security.ProtectionDomain) */ default Optional read(String name) throws IOException { - Optional in = open(name); - if (in.isPresent()) { - byte[] bytes = in.get().readAllBytes(); - return Optional.of(ByteBuffer.wrap(bytes)); + Optional oin = open(name); + if (oin.isPresent()) { + try (InputStream in = oin.get()) { + return Optional.of(ByteBuffer.wrap(in.readAllBytes())); + } } else { return Optional.empty(); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/module/ModuleReference.java --- a/jdk/src/java.base/share/classes/java/lang/module/ModuleReference.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleReference.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -32,7 +32,7 @@ import java.util.Optional; import java.util.function.Supplier; -import jdk.internal.module.Hasher.HashSupplier; +import jdk.internal.module.ModuleHashes.HashSupplier; /** @@ -54,12 +54,33 @@ private final URI location; private final Supplier readerSupplier; + // true if this is a reference to a patched module + private boolean patched; + // the function that computes the hash of this module reference private final HashSupplier hasher; // cached hash string to avoid needing to compute it many times private String cachedHash; + + /** + * Constructs a new instance of this class. + */ + ModuleReference(ModuleDescriptor descriptor, + URI location, + Supplier readerSupplier, + boolean patched, + HashSupplier hasher) + + { + this.descriptor = Objects.requireNonNull(descriptor); + this.location = location; + this.readerSupplier = Objects.requireNonNull(readerSupplier); + this.patched = patched; + this.hasher = hasher; + } + /** * Constructs a new instance of this class. */ @@ -67,11 +88,9 @@ URI location, Supplier readerSupplier, HashSupplier hasher) + { - this.descriptor = Objects.requireNonNull(descriptor); - this.location = location; - this.readerSupplier = Objects.requireNonNull(readerSupplier); - this.hasher = hasher; + this(descriptor, location, readerSupplier, false, hasher); } @@ -96,10 +115,9 @@ URI location, Supplier readerSupplier) { - this(descriptor, location, readerSupplier, null); + this(descriptor, location, readerSupplier, false, null); } - /** * Returns the module descriptor. * @@ -151,6 +169,20 @@ /** + * Returns {@code true} if this module has been patched via -Xpatch. + */ + boolean isPatched() { + return patched; + } + + /** + * Returns the hash supplier for this module. + */ + HashSupplier hasher() { + return hasher; + } + + /** * Computes the hash of this module, returning it as a hex string. * Returns {@code null} if the hash cannot be computed. * @@ -166,8 +198,6 @@ return result; } - private int hash; - /** * Computes a hash code for this module reference. * @@ -181,12 +211,17 @@ public int hashCode() { int hc = hash; if (hc == 0) { - hc = Objects.hash(descriptor, location, readerSupplier, hasher); - if (hc != 0) hash = hc; + hc = Objects.hash(descriptor, location, readerSupplier, hasher, + Boolean.valueOf(patched)); + if (hc == 0) + hc = -1; + hash = hc; } return hc; } + private int hash; + /** * Tests this module reference for equality with the given object. * @@ -214,7 +249,8 @@ return Objects.equals(this.descriptor, that.descriptor) && Objects.equals(this.location, that.location) && Objects.equals(this.readerSupplier, that.readerSupplier) - && Objects.equals(this.hasher, that.hasher); + && Objects.equals(this.hasher, that.hasher) + && this.patched == that.patched; } /** diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/module/ModuleReferences.java --- a/jdk/src/java.base/share/classes/java/lang/module/ModuleReferences.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleReferences.java Tue May 03 12:25:20 2016 -0700 @@ -48,8 +48,8 @@ import jdk.internal.misc.JavaLangAccess; import jdk.internal.misc.SharedSecrets; -import jdk.internal.module.Hasher; -import jdk.internal.module.Hasher.HashSupplier; +import jdk.internal.module.ModuleHashes; +import jdk.internal.module.ModuleHashes.HashSupplier; import jdk.internal.module.ModulePatcher; import sun.net.www.ParseUtil; @@ -89,7 +89,7 @@ static ModuleReference newJarModule(ModuleDescriptor md, Path file) { URI uri = file.toUri(); Supplier supplier = () -> new JarModuleReader(file, uri); - HashSupplier hasher = (algorithm) -> Hasher.generate(file, algorithm); + HashSupplier hasher = (a) -> ModuleHashes.computeHashAsString(file, a); return newModule(md, uri, supplier, hasher); } @@ -99,7 +99,7 @@ static ModuleReference newJModModule(ModuleDescriptor md, Path file) { URI uri = file.toUri(); Supplier supplier = () -> new JModModuleReader(file, uri); - HashSupplier hasher = (algorithm) -> Hasher.generate(file, algorithm); + HashSupplier hasher = (a) -> ModuleHashes.computeHashAsString(file, a); return newModule(md, file.toUri(), supplier, hasher); } @@ -122,7 +122,7 @@ private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); - private volatile boolean closed; + private boolean closed; SafeCloseModuleReader() { } @@ -198,7 +198,10 @@ static JarFile newJarFile(Path path) { try { - return new JarFile(path.toFile()); + return new JarFile(path.toFile(), + true, // verify + ZipFile.OPEN_READ, + JarFile.Release.RUNTIME); } catch (IOException ioe) { throw new UncheckedIOException(ioe); } @@ -219,6 +222,8 @@ if (je != null) { String encodedPath = ParseUtil.encodePath(name, false); String uris = "jar:" + uri + "!/" + encodedPath; + if (jf.isMultiRelease()) + uris += "#runtime"; return Optional.of(URI.create(uris)); } else { return Optional.empty(); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/module/Resolver.java --- a/jdk/src/java.base/share/classes/java/lang/module/Resolver.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/module/Resolver.java Tue May 03 12:25:20 2016 -0700 @@ -25,8 +25,8 @@ package java.lang.module; +import java.io.PrintStream; import java.lang.module.ModuleDescriptor.Requires.Modifier; -import java.lang.reflect.Layer; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; @@ -43,7 +43,7 @@ import java.util.StringJoiner; import java.util.stream.Collectors; -import jdk.internal.module.Hasher; +import jdk.internal.module.ModuleHashes; /** * The resolver used by {@link Configuration#resolveRequires} and @@ -55,6 +55,7 @@ private final ModuleFinder beforeFinder; private final Configuration parent; private final ModuleFinder afterFinder; + private final PrintStream traceOutput; // maps module name to module reference private final Map nameToReference = new HashMap<>(); @@ -62,10 +63,12 @@ Resolver(ModuleFinder beforeFinder, Configuration parent, - ModuleFinder afterFinder) { + ModuleFinder afterFinder, + PrintStream traceOutput) { this.beforeFinder = beforeFinder; this.parent = parent; this.afterFinder = afterFinder; + this.traceOutput = traceOutput; } @@ -76,8 +79,6 @@ */ Resolver resolveRequires(Collection roots) { - long start = trace_start("Resolve"); - // create the visit stack to get us started Deque q = new ArrayDeque<>(); for (String root : roots) { @@ -95,10 +96,9 @@ } } - if (TRACE) { + if (isTracing()) { trace("Root module %s located", root); - if (mref.location().isPresent()) - trace(" (%s)", mref.location().get()); + mref.location().ifPresent(uri -> trace(" (%s)", uri)); } assert mref.descriptor().name().equals(root); @@ -108,13 +108,6 @@ resolve(q); - if (TRACE) { - long duration = System.currentTimeMillis() - start; - Set names = nameToReference.keySet(); - trace("Resolver completed in %s ms", duration); - names.stream().sorted().forEach(name -> trace(" %s", name)); - } - return this; } @@ -153,11 +146,10 @@ q.offer(mref.descriptor()); resolved.add(mref.descriptor()); - if (TRACE) { + if (isTracing()) { trace("Module %s located, required by %s", dn, descriptor.name()); - if (mref.location().isPresent()) - trace(" (%s)", mref.location().get()); + mref.location().ifPresent(uri -> trace(" (%s)", uri)); } } @@ -175,8 +167,6 @@ */ Resolver resolveUses() { - long start = trace_start("Bind"); - // Scan the finders for all available service provider modules. As // java.base uses services then then module finders will be scanned // anyway. @@ -230,10 +220,10 @@ String pn = provider.name(); if (!nameToReference.containsKey(pn)) { - - if (TRACE && mref.location().isPresent()) - trace(" (%s)", mref.location().get()); - + if (isTracing()) { + mref.location() + .ifPresent(uri -> trace(" (%s)", uri)); + } nameToReference.put(pn, mref); q.push(provider); } @@ -248,14 +238,6 @@ } while (!candidateConsumers.isEmpty()); - - if (TRACE) { - long duration = System.currentTimeMillis() - start; - Set names = nameToReference.keySet(); - trace("Bind completed in %s ms", duration); - names.stream().sorted().forEach(name -> trace(" %s", name)); - } - return this; } @@ -264,23 +246,33 @@ * Execute post-resolution checks and returns the module graph of resolved * modules as {@code Map}. The resolved modules will be in the given * configuration. + * + * @param check {@true} to execute the post resolution checks */ - Map> finish(Configuration cf) { + Map> finish(Configuration cf, + boolean check) + { + if (isTracing()) { + trace("Result:"); + Set names = nameToReference.keySet(); + names.stream().sorted().forEach(name -> trace(" %s", name)); + } - detectCycles(); - - checkPlatformConstraints(); - - checkHashes(); + if (check) { + detectCycles(); + checkPlatformConstraints(); + checkHashes(); + } Map> graph = makeGraph(cf); - checkExportSuppliers(graph); + if (check) { + checkExportSuppliers(graph); + } return graph; } - /** * Checks the given module graph for cycles. * @@ -420,52 +412,44 @@ } - /** * Checks the hashes in the module descriptor to ensure that they match - * the hash of the dependency's module reference. + * any recorded hashes. */ private void checkHashes() { - for (ModuleReference mref : nameToReference.values()) { ModuleDescriptor descriptor = mref.descriptor(); - // get map of module names to hash - Optional ohashes = descriptor.hashes(); + // get map of module hashes + Optional ohashes = descriptor.hashes(); if (!ohashes.isPresent()) continue; - Hasher.DependencyHashes hashes = ohashes.get(); - - // check dependences - for (ModuleDescriptor.Requires d : descriptor.requires()) { - String dn = d.name(); - String recordedHash = hashes.hashFor(dn); - - if (recordedHash != null) { + ModuleHashes hashes = ohashes.get(); - ModuleReference other = nameToReference.get(dn); - if (other == null) { - other = parent.findModule(dn) - .map(ResolvedModule::reference) - .orElse(null); - } - if (other == null) - throw new InternalError(dn + " not found"); + String algorithm = hashes.algorithm(); + for (String dn : hashes.names()) { + ModuleReference other = nameToReference.get(dn); + if (other == null) { + other = parent.findModule(dn) + .map(ResolvedModule::reference) + .orElse(null); + } - String actualHash = other.computeHash(hashes.algorithm()); + // skip checking the hash if the module has been patched + if (other != null && !other.isPatched()) { + String recordedHash = hashes.hashFor(dn); + String actualHash = other.computeHash(algorithm); if (actualHash == null) fail("Unable to compute the hash of module %s", dn); - if (!recordedHash.equals(actualHash)) { - fail("Hash of %s (%s) differs to expected hash (%s)", - dn, actualHash, recordedHash); + fail("Hash of %s (%s) differs to expected hash (%s)" + + " recorded in %s", dn, actualHash, recordedHash, + descriptor.name()); } + } + } - } - - } } - } @@ -666,7 +650,7 @@ // source is exported to descriptor2 String source = export.source(); ModuleDescriptor other - = packageToExporter.put(source, descriptor2); + = packageToExporter.put(source, descriptor2); if (other != null && other != descriptor2) { // package might be local to descriptor1 @@ -690,33 +674,38 @@ } } - // uses S - for (String service : descriptor1.uses()) { - String pn = packageName(service); - if (!packageToExporter.containsKey(pn)) { - fail("Module %s does not read a module that exports %s", - descriptor1.name(), pn); - } - } + // uses/provides checks not applicable to automatic modules + if (!descriptor1.isAutomatic()) { - // provides S - for (Map.Entry entry : - descriptor1.provides().entrySet()) { - String service = entry.getKey(); - ModuleDescriptor.Provides provides = entry.getValue(); - - String pn = packageName(service); - if (!packageToExporter.containsKey(pn)) { - fail("Module %s does not read a module that exports %s", - descriptor1.name(), pn); + // uses S + for (String service : descriptor1.uses()) { + String pn = packageName(service); + if (!packageToExporter.containsKey(pn)) { + fail("Module %s does not read a module that exports %s", + descriptor1.name(), pn); + } } - for (String provider : provides.providers()) { - if (!packages.contains(packageName(provider))) { - fail("Provider %s not in module %s", - provider, descriptor1.name()); + // provides S + for (Map.Entry entry : + descriptor1.provides().entrySet()) { + String service = entry.getKey(); + ModuleDescriptor.Provides provides = entry.getValue(); + + String pn = packageName(service); + if (!packageToExporter.containsKey(pn)) { + fail("Module %s does not read a module that exports %s", + descriptor1.name(), pn); + } + + for (String provider : provides.providers()) { + if (!packages.contains(packageName(provider))) { + fail("Provider %s not in module %s", + provider, descriptor1.name()); + } } } + } } @@ -796,27 +785,18 @@ throw new ResolutionException(msg); } - /** - * Tracing support, limited to boot layer for now. + * Tracing support */ - private final static boolean TRACE - = Boolean.getBoolean("jdk.launcher.traceResolver") - && (Layer.boot() == null); - - private String op; - - private long trace_start(String op) { - this.op = op; - return System.currentTimeMillis(); + private boolean isTracing() { + return traceOutput != null; } private void trace(String fmt, Object ... args) { - if (TRACE) { - System.out.print("[" + op + "] "); - System.out.format(fmt, args); - System.out.println(); + if (traceOutput != null) { + traceOutput.format("[Resolver] " + fmt, args); + traceOutput.println(); } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java --- a/jdk/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java Tue May 03 12:25:20 2016 -0700 @@ -44,6 +44,7 @@ import jdk.internal.jimage.ImageLocation; import jdk.internal.jimage.ImageReader; import jdk.internal.jimage.ImageReaderFactory; +import jdk.internal.module.ModuleHashes; import jdk.internal.module.SystemModules; import jdk.internal.module.ModulePatcher; import jdk.internal.perf.PerfCounter; @@ -101,13 +102,16 @@ for (int i = 0; i < n; i++) { String mn = moduleNames[i]; ModuleDescriptor md; + String hash; if (fastLoad) { md = descriptors[i]; + hash = SystemModules.MODULES_TO_HASH[i]; } else { // fallback to read module-info.class // if fast loading of ModuleDescriptors is disabled ImageLocation location = imageReader.findLocation(mn, "module-info.class"); md = ModuleDescriptor.read(imageReader.getResourceBuffer(location)); + hash = null; } if (!md.name().equals(mn)) throw new InternalError(); @@ -123,7 +127,8 @@ } }; - ModuleReference mref = new ModuleReference(md, uri, readerSupplier); + ModuleReference mref = + new ModuleReference(md, uri, readerSupplier, hashSupplier(hash)); // may need a reference to a patched module if -Xpatch specified mref = ModulePatcher.interposeIfNeeded(mref); @@ -142,6 +147,18 @@ initTime.addElapsedTimeFrom(t0); } + private static ModuleHashes.HashSupplier hashSupplier(String hash) { + if (hash == null) + return null; + + return new ModuleHashes.HashSupplier() { + @Override + public String generate(String algorithm) { + return hash; + } + }; + } + SystemModuleFinder() { } @Override diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/reflect/Layer.java --- a/jdk/src/java.base/share/classes/java/lang/reflect/Layer.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/reflect/Layer.java Tue May 03 12:25:20 2016 -0700 @@ -27,6 +27,7 @@ import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleDescriptor.Provides; import java.lang.module.ResolvedModule; import java.util.Collections; import java.util.HashMap; @@ -41,6 +42,8 @@ import jdk.internal.loader.Loader; import jdk.internal.loader.LoaderPool; import jdk.internal.misc.SharedSecrets; +import jdk.internal.module.ServicesCatalog; +import jdk.internal.module.ServicesCatalog.ServiceProvider; import sun.security.util.SecurityConstants; @@ -549,4 +552,55 @@ public static Layer boot() { return SharedSecrets.getJavaLangAccess().getBootLayer(); } + + + /** + * Returns the ServicesCatalog for this Layer, creating it if not + * already created. + */ + ServicesCatalog getServicesCatalog() { + ServicesCatalog servicesCatalog = this.servicesCatalog; + if (servicesCatalog != null) + return servicesCatalog; + + Map> map = new HashMap<>(); + for (Module m : nameToModule.values()) { + ModuleDescriptor descriptor = m.getDescriptor(); + for (Provides provides : descriptor.provides().values()) { + String service = provides.service(); + Set providers + = map.computeIfAbsent(service, k -> new HashSet<>()); + for (String pn : provides.providers()) { + providers.add(new ServiceProvider(m, pn)); + } + } + } + + ServicesCatalog catalog = new ServicesCatalog() { + @Override + public void register(Module module) { + throw new UnsupportedOperationException(); + } + @Override + public Set findServices(String service) { + Set providers = map.get(service); + if (providers == null) { + return Collections.emptySet(); + } else { + return Collections.unmodifiableSet(providers); + } + } + }; + + synchronized (this) { + servicesCatalog = this.servicesCatalog; + if (servicesCatalog == null) { + this.servicesCatalog = servicesCatalog = catalog; + } + } + + return servicesCatalog; + } + + private volatile ServicesCatalog servicesCatalog; } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/reflect/Module.java --- a/jdk/src/java.base/share/classes/java/lang/reflect/Module.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/reflect/Module.java Tue May 03 12:25:20 2016 -0700 @@ -43,11 +43,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; import java.util.stream.Stream; @@ -142,9 +138,6 @@ this.name = null; this.loader = loader; this.descriptor = null; - - // unnamed modules are loose - this.loose = true; } @@ -245,17 +238,27 @@ } - // -- readability -- + // -- + + // the special Module to mean reads or exported to "all unnamed modules" + private static final Module ALL_UNNAMED_MODULE = new Module(null); - // true if this module reads all unnamed modules (a.k.a. loose module) - private volatile boolean loose; + // special Module to mean exported to "everyone" + private static final Module EVERYONE_MODULE = new Module(null); + + // exported to all modules + private static final Set EVERYONE = Collections.singleton(EVERYONE_MODULE); + + + // -- readability -- // the modules that this module permanently reads // (will be final when the modules are defined in reverse topology order) private volatile Set reads; - // created lazily, additional modules that this module reflectively reads - private volatile WeakSet transientReads; + // additional module (2nd key) that some module (1st key) reflectively reads + private static final WeakPairMap transientReads + = new WeakPairMap<>(); /** @@ -284,22 +287,19 @@ // check if this module reads other if (other.isNamed()) { - Set reads = this.reads; // volatile read if (reads != null && reads.contains(other)) return true; - - } else { - - // loose modules read all unnamed modules - if (this.loose) - return true; - } // check if this module reads the other module reflectively - WeakSet tr = this.transientReads; // volatile read - if (tr != null && tr.contains(other)) + if (transientReads.containsKeyPair(this, other)) + return true; + + // if other is an unnamed module then check if this module reads + // all unnamed modules + if (!other.isNamed() + && transientReads.containsKeyPair(this, ALL_UNNAMED_MODULE)) return true; return false; @@ -346,8 +346,7 @@ } /** - * Makes the given {@code Module} readable to this module without - * notifying the VM. + * Updates this module to read another module without notifying the VM. * * @apiNote This method is for VM white-box testing. */ @@ -361,40 +360,28 @@ * If {@code syncVM} is {@code true} then the VM is notified. */ private void implAddReads(Module other, boolean syncVM) { + Objects.requireNonNull(other); // nothing to do if (other == this || !this.isNamed()) return; - // if the other is null then change this module to be loose. - if (other == null) { - if (syncVM) - addReads0(this, null); - this.loose = true; - return; - } - // check if we already read this module Set reads = this.reads; if (reads != null && reads.contains(other)) return; // update VM first, just in case it fails - if (syncVM) - addReads0(this, other); + if (syncVM) { + if (other == ALL_UNNAMED_MODULE) { + addReads0(this, null); + } else { + addReads0(this, other); + } + } // add reflective read - WeakSet tr = this.transientReads; - if (tr == null) { - synchronized (this) { - tr = this.transientReads; - if (tr == null) { - tr = new WeakSet<>(); - this.transientReads = tr; - } - } - } - tr.add(other); + transientReads.putIfAbsent(this, other, Boolean.TRUE); } @@ -404,15 +391,10 @@ // (will be final when the modules are defined in reverse topology order) private volatile Map> exports; - // created lazily, additional exports added at run-time - private volatile Map> transientExports; - - // the special Module to mean exported to all modules - private static final Module EVERYONE_MODULE = new Module(null); - private static final Set EVERYONE = Collections.singleton(EVERYONE_MODULE); - - // the special Module to mean exported to all unnamed modules - private static final Module ALL_UNNAMED_MODULE = new Module(null); + // additional exports added at run-time + // this module (1st key), other module (2nd key), exported packages (value) + private static final WeakPairMap> + transientExports = new WeakPairMap<>(); /** @@ -489,23 +471,9 @@ if (exports != null) { Set targets = exports.get(pn); - if (targets != null) { - - // exported to all modules - if (targets.contains(EVERYONE_MODULE)) - return true; - - if (other != EVERYONE_MODULE) { - // exported to other - if (targets.contains(other)) - return true; - - // other is an unnamed module && exported to all unnamed - if (!other.isNamed() && targets.contains(ALL_UNNAMED_MODULE)) - return true; - } - - } + if ((targets != null) + && (targets.contains(other) || targets.contains(EVERYONE_MODULE))) + return true; } return false; } @@ -515,29 +483,27 @@ * package package to the given module. */ private boolean isExportedReflectively(String pn, Module other) { - Map> te = this.transientExports; - if (te != null) { - WeakSet targets = te.get(pn); + // exported to all modules + Map exports = transientExports.get(this, EVERYONE_MODULE); + if (exports != null && exports.containsKey(pn)) + return true; - if (targets != null) { - - // exported to all modules - if (targets.contains(EVERYONE_MODULE)) - return true; + if (other != EVERYONE_MODULE) { - if (other != EVERYONE_MODULE) { + // exported to other + exports = transientExports.get(this, other); + if (exports != null && exports.containsKey(pn)) + return true; - // exported to other - if (targets.contains(other)) - return true; - - // other is an unnamed module && exported to all unnamed - if (!other.isNamed() && targets.contains(ALL_UNNAMED_MODULE)) - return true; - } + // other is an unnamed module && exported to all unnamed + if (!other.isNamed()) { + exports = transientExports.get(this, ALL_UNNAMED_MODULE); + if (exports != null && exports.containsKey(pn)) + return true; } } + return false; } @@ -638,34 +604,19 @@ } } - // create transientExports if needed - Map> te = this.transientExports; // read - if (te == null) { - synchronized (this) { - te = this.transientExports; - if (te == null) { - te = new ConcurrentHashMap<>(); - this.transientExports = te; // volatile write - } - } - } - // add package name to transientExports if absent - WeakSet s = te.get(pn); - if (s == null) { - s = new WeakSet<>(); - WeakSet prev = te.putIfAbsent(pn, s); - if (prev != null) - s = prev; - } - s.add(other); + transientExports + .computeIfAbsent(this, other, + (_this, _other) -> new ConcurrentHashMap<>()) + .putIfAbsent(pn, Boolean.TRUE); } // -- services -- - // created lazily, additional service types that this module uses - private volatile WeakSet> transientUses; + // additional service type (2nd key) that some module (1st key) uses + private static final WeakPairMap, Boolean> transientUses + = new WeakPairMap<>(); /** * If the caller's module is this module then update this module to add a @@ -702,17 +653,7 @@ } if (!canUse(st)) { - WeakSet> uses = this.transientUses; - if (uses == null) { - synchronized (this) { - uses = this.transientUses; - if (uses == null) { - uses = new WeakSet<>(); - this.transientUses = uses; - } - } - } - uses.add(st); + transientUses.putIfAbsent(this, st, Boolean.TRUE); } } @@ -746,11 +687,7 @@ return true; // uses added via addUses - WeakSet> uses = this.transientUses; - if (uses != null && uses.contains(st)) - return true; - - return false; + return transientUses.containsKeyPair(this, st); } @@ -885,7 +822,7 @@ // -- creating Module objects -- /** - * Find the runtime Module corresponding to the given ReadDependence + * Find the runtime Module corresponding to the given ResolvedModule * in the given parent Layer (or its parents). */ private static Module find(ResolvedModule resolvedModule, Layer layer) { @@ -969,7 +906,7 @@ // automatic modules reads all unnamed modules if (descriptor.isAutomatic()) { - m.implAddReads(null, true); + m.implAddReads(ALL_UNNAMED_MODULE, true); } // exports @@ -1097,7 +1034,7 @@ * the representation is the string {@code "module"}, followed by a space, * and then the module name. For an unnamed module, the representation is * the string {@code "unnamed module"}, followed by a space, and then an - * implementation specific identifier for the unnamed module. + * implementation specific string that identifies the unnamed module. * * @return The string representation of this module */ @@ -1112,46 +1049,6 @@ } - // -- supporting classes -- - - - /** - * A "not-a-Set" set of weakly referenced objects that supports concurrent - * access. - */ - private static class WeakSet { - private final ReadWriteLock lock = new ReentrantReadWriteLock(); - private final Lock readLock = lock.readLock(); - private final Lock writeLock = lock.writeLock(); - - private final WeakHashMap map = new WeakHashMap<>(); - - /** - * Adds the specified element to the set. - */ - void add(E e) { - writeLock.lock(); - try { - map.put(e, Boolean.TRUE); - } finally { - writeLock.unlock(); - } - } - - /** - * Returns {@code true} if this set contains the specified element. - */ - boolean contains(E e) { - readLock.lock(); - try { - return map.containsKey(e); - } finally { - readLock.unlock(); - } - } - } - - // -- native methods -- // JVM_DefineModule @@ -1196,8 +1093,12 @@ m1.implAddReads(m2, true); } @Override + public void addReadsAllUnnamed(Module m) { + m.implAddReads(Module.ALL_UNNAMED_MODULE); + } + @Override public void addExports(Module m, String pn, Module other) { - m.implAddExports(pn, Objects.requireNonNull(other), true); + m.implAddExports(pn, other, true); } @Override public void addExportsToAll(Module m, String pn) { @@ -1211,6 +1112,10 @@ public void addPackage(Module m, String pn) { m.implAddPackage(pn, true); } + @Override + public ServicesCatalog getServicesCatalog(Layer layer) { + return layer.getServicesCatalog(); + } }); } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/reflect/Proxy.java --- a/jdk/src/java.base/share/classes/java/lang/reflect/Proxy.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/reflect/Proxy.java Tue May 03 12:25:20 2016 -0700 @@ -582,7 +582,7 @@ } private static final String DEBUG = - GetPropertyAction.getProperty("jdk.proxy.debug", ""); + GetPropertyAction.privilegedGetProperty("jdk.proxy.debug", ""); private static boolean isDebug() { return !DEBUG.isEmpty(); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/lang/reflect/WeakPairMap.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/reflect/WeakPairMap.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,354 @@ +/* + * 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 java.lang.reflect; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.Collection; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiFunction; + +/** + * A WeakHashMap-like data structure that uses a pair of weakly-referenced keys + * with identity equality semantics to associate a strongly-referenced value. + * Unlike WeakHashMap, this data structure is thread-safe. + * + * @param the type of 1st key in key pair + * @param the type of 2nd key in key pair + * @param the type of value + * @author Peter Levart + */ +final class WeakPairMap { + + private final ConcurrentHashMap, V> map = new ConcurrentHashMap<>(); + private final ReferenceQueue queue = new ReferenceQueue<>(); + + /** + * Tests if the specified pair of keys are associated with a value + * in the WeakPairMap. + * + * @param k1 the 1st of the pair of keys + * @param k2 the 2nd of the pair of keys + * @return true if and only if the specified key pair is in this WeakPairMap, + * as determined by the identity comparison; false otherwise + * @throws NullPointerException if any of the specified keys is null + */ + public boolean containsKeyPair(K1 k1, K2 k2) { + expungeStaleAssociations(); + return map.containsKey(Pair.lookup(k1, k2)); + } + + /** + * Returns the value to which the specified pair of keys is mapped, or null + * if this WeakPairMap contains no mapping for the key pair. + *

More formally, if this WeakPairMap contains a mapping from a key pair + * {@code (_k1, _k2)} to a value {@code v} such that + * {@code k1 == _k1 && k2 == _k2}, then this method returns {@code v}; + * otherwise it returns {@code null}. + * (There can be at most one such mapping.) + * + * @param k1 the 1st of the pair of keys for which the mapped value is to + * be returned + * @param k2 the 2nd of the pair of keys for which the mapped value is to + * be returned + * @return the value to which the specified key pair is mapped, or null if + * this map contains no mapping for the key pair + * @throws NullPointerException if any of the specified keys is null + */ + public V get(K1 k1, K2 k2) { + expungeStaleAssociations(); + return map.get(Pair.lookup(k1, k2)); + } + + /** + * Maps the specified key pair to the specified value in this WeakPairMap. + * Neither the keys nor the value can be null. + *

The value can be retrieved by calling the {@link #get} method + * with the the same keys (compared by identity). + * + * @param k1 the 1st of the pair of keys with which the specified value is to + * be associated + * @param k2 the 2nd of the pair of keys with which the specified value is to + * be associated + * @param v value to be associated with the specified key pair + * @return the previous value associated with key pair, or {@code null} if + * there was no mapping for key pair + * @throws NullPointerException if any of the specified keys or value is null + */ + public V put(K1 k1, K2 k2, V v) { + expungeStaleAssociations(); + return map.put(Pair.weak(k1, k2, queue), v); + } + + /** + * If the specified key pair is not already associated with a value, + * associates it with the given value and returns {@code null}, else does + * nothing and returns the currently associated value. + * + * @param k1 the 1st of the pair of keys with which the specified value is to + * be associated + * @param k2 the 2nd of the pair of keys with which the specified value is to + * be associated + * @param v value to be associated with the specified key pair + * @return the previous value associated with key pair, or {@code null} if + * there was no mapping for key pair + * @throws NullPointerException if any of the specified keys or value is null + */ + public V putIfAbsent(K1 k1, K2 k2, V v) { + expungeStaleAssociations(); + return map.putIfAbsent(Pair.weak(k1, k2, queue), v); + } + + /** + * If the specified key pair is not already associated with a value, + * attempts to compute its value using the given mapping function + * and enters it into this WeakPairMap unless {@code null}. The entire + * method invocation is performed atomically, so the function is + * applied at most once per key pair. Some attempted update operations + * on this WeakPairMap by other threads may be blocked while computation + * is in progress, so the computation should be short and simple, + * and must not attempt to update any other mappings of this WeakPairMap. + * + * @param k1 the 1st of the pair of keys with which the + * computed value is to be associated + * @param k2 the 2nd of the pair of keys with which the + * computed value is to be associated + * @param mappingFunction the function to compute a value + * @return the current (existing or computed) value associated with + * the specified key pair, or null if the computed value is null + * @throws NullPointerException if any of the specified keys or + * mappingFunction is null + * @throws IllegalStateException if the computation detectably + * attempts a recursive update to this map + * that would otherwise never complete + * @throws RuntimeException or Error if the mappingFunction does so, in + * which case the mapping is left unestablished + */ + public V computeIfAbsent(K1 k1, K2 k2, + BiFunction + mappingFunction) { + expungeStaleAssociations(); + try { + return map.computeIfAbsent( + Pair.weak(k1, k2, queue), + pair -> mappingFunction.apply(pair.first(), pair.second())); + } finally { + Reference.reachabilityFence(k1); + Reference.reachabilityFence(k2); + } + } + + /** + * Returns a {@link Collection} view of the values contained in this + * WeakPairMap. The collection is backed by the WeakPairMap, so changes to + * the map are reflected in the collection, and vice-versa. The collection + * supports element removal, which removes the corresponding + * mapping from this map, via the {@code Iterator.remove}, + * {@code Collection.remove}, {@code removeAll}, + * {@code retainAll}, and {@code clear} operations. It does not + * support the {@code add} or {@code addAll} operations. + * + * @return the collection view + */ + public Collection values() { + expungeStaleAssociations(); + return map.values(); + } + + /** + * Removes associations from this WeakPairMap for which at least one of the + * keys in key pair has been found weakly-reachable and corresponding + * WeakRefPeer(s) enqueued. Called as part of each public operation. + */ + private void expungeStaleAssociations() { + WeakRefPeer peer; + while ((peer = (WeakRefPeer) queue.poll()) != null) { + map.remove(peer.weakPair()); + } + } + + /** + * Common interface of both {@link Weak} and {@link Lookup} key pairs. + */ + private interface Pair { + + static Pair weak(K1 k1, K2 k2, + ReferenceQueue queue) { + return new Weak<>(k1, k2, queue); + } + + static Pair lookup(K1 k1, K2 k2) { + return new Lookup<>(k1, k2); + } + + /** + * @return The 1st of the pair of keys (may be null for {@link Weak} + * when it gets cleared) + */ + K1 first(); + + /** + * @return The 2nd of the pair of keys (may be null for {@link Weak} + * when it gets cleared) + */ + K2 second(); + + static int hashCode(Object first, Object second) { + // assert first != null && second != null; + return System.identityHashCode(first) ^ + System.identityHashCode(second); + } + + static boolean equals(Object first, Object second, Pair p) { + return first != null && second != null && + first == p.first() && second == p.second(); + } + + /** + * A Pair where both keys are weakly-referenced. + * It is composed of two instances of {@link WeakRefPeer}s: + *
{@code
+         *
+         *     +-referent-> [K1]                +-referent-> [K2]
+         *     |                                |
+         *   +----------------+               +----------------+
+         *   | Pair.Weak <:   |-----peer----->| (anonymous) <: |
+         *   | WeakRefPeer,   |               | WeakRefPeer    |
+         *   | Pair           |<--weakPair()--|                |
+         *   +----------------+               +----------------+
+         *     |            ^
+         *     |            |
+         *     +-weakPair()-+
+         *
+         * }
+ *

+ * Pair.Weak is used for CHM keys. Both peers are associated with the + * same {@link ReferenceQueue} so when either of their referents + * becomes weakly-reachable, the corresponding entries can be + * {@link #expungeStaleAssociations() expunged} from the map. + */ + final class Weak extends WeakRefPeer implements Pair { + + // saved hash so it can be retrieved after the reference is cleared + private final int hash; + // link to peer + private final WeakRefPeer peer; + + Weak(K1 k1, K2 k2, ReferenceQueue queue) { + super(k1, queue); + hash = Pair.hashCode(k1, k2); + peer = new WeakRefPeer<>(k2, queue) { + // link back to peer + @Override + Weak weakPair() { return Weak.this; } + }; + } + + @Override + Weak weakPair() { + return this; + } + + @Override + public K1 first() { + return get(); + } + + @Override + public K2 second() { + return peer.get(); + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public boolean equals(Object obj) { + return this == obj || + (obj instanceof Pair && + Pair.equals(first(), second(), (Pair) obj)); + } + } + + /** + * Optimized lookup Pair, used as lookup key in methods like + * {@link java.util.Map#get(Object)} or + * {@link java.util.Map#containsKey(Object)}) where + * there is a great chance its allocation is eliminated + * by escape analysis when such lookups are inlined by JIT. + * All its methods are purposely designed so that 'this' is never + * passed to any other method or used as identity. + */ + final class Lookup implements Pair { + private final K1 k1; + private final K2 k2; + + Lookup(K1 k1, K2 k2) { + this.k1 = Objects.requireNonNull(k1); + this.k2 = Objects.requireNonNull(k2); + } + + @Override + public K1 first() { + return k1; + } + + @Override + public K2 second() { + return k2; + } + + @Override + public int hashCode() { + return Pair.hashCode(k1, k2); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof Pair && + Pair.equals(k1, k2, (Pair) obj); + } + } + } + + /** + * Common abstract supertype of a pair of WeakReference peers. + */ + private static abstract class WeakRefPeer extends WeakReference { + + WeakRefPeer(K k, ReferenceQueue queue) { + super(Objects.requireNonNull(k), queue); + } + + /** + * @return the {@link Pair.Weak} side of the pair of peers. + */ + abstract Pair.Weak weakPair(); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java --- a/jdk/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java Tue May 03 12:25:20 2016 -0700 @@ -52,7 +52,8 @@ protected InetAddress connectedAddress = null; private int connectedPort = -1; - private static final String os = GetPropertyAction.getProperty("os.name"); + private static final String os = + GetPropertyAction.privilegedGetProperty("os.name"); /** * flag set if the native connect() call not to be used diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/net/InetAddress.java --- a/jdk/src/java.base/share/classes/java/net/InetAddress.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/net/InetAddress.java Tue May 03 12:25:20 2016 -0700 @@ -1124,7 +1124,7 @@ private static NameService createNameService() { String hostsFileName = - GetPropertyAction.getProperty("jdk.net.hosts.file"); + GetPropertyAction.privilegedGetProperty("jdk.net.hosts.file"); NameService theNameService; if (hostsFileName != null) { theNameService = new HostsFileNameService(hostsFileName); @@ -1643,9 +1643,11 @@ * property can vary across implementations of the java. * classes. The default is an empty String "". */ - String prefix = GetPropertyAction.getProperty("impl.prefix", ""); + String prefix = GetPropertyAction.privilegedGetProperty("impl.prefix", ""); try { - impl = Class.forName("java.net." + prefix + implName).newInstance(); + @SuppressWarnings("deprecation") + Object tmp = Class.forName("java.net." + prefix + implName).newInstance(); + impl = tmp; } catch (ClassNotFoundException e) { System.err.println("Class not found: java.net." + prefix + implName + ":\ncheck impl.prefix property " + @@ -1662,7 +1664,9 @@ if (impl == null) { try { - impl = Class.forName(implName).newInstance(); + @SuppressWarnings("deprecation") + Object tmp = Class.forName(implName).newInstance(); + impl = tmp; } catch (Exception e) { throw new Error("System property impl.prefix incorrect"); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/net/ProxySelector.java --- a/jdk/src/java.base/share/classes/java/net/ProxySelector.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/net/ProxySelector.java Tue May 03 12:25:20 2016 -0700 @@ -71,7 +71,9 @@ try { Class c = Class.forName("sun.net.spi.DefaultProxySelector"); if (c != null && ProxySelector.class.isAssignableFrom(c)) { - theProxySelector = (ProxySelector) c.newInstance(); + @SuppressWarnings("deprecation") + ProxySelector tmp = (ProxySelector) c.newInstance(); + theProxySelector = tmp; } } catch (Exception e) { theProxySelector = null; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/net/SocksSocketImpl.java --- a/jdk/src/java.base/share/classes/java/net/SocksSocketImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/net/SocksSocketImpl.java Tue May 03 12:25:20 2016 -0700 @@ -178,7 +178,7 @@ userName = pw.getUserName(); password = new String(pw.getPassword()); } else { - userName = GetPropertyAction.getProperty("user.name"); + userName = GetPropertyAction.privilegedGetProperty("user.name"); } if (userName == null) return false; @@ -1088,7 +1088,7 @@ userName = System.getProperty("user.name"); } catch (SecurityException se) { /* swallow Exception */ } } else { - userName = GetPropertyAction.getProperty("user.name"); + userName = GetPropertyAction.privilegedGetProperty("user.name"); } return userName; } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/net/URL.java --- a/jdk/src/java.base/share/classes/java/net/URL.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/net/URL.java Tue May 03 12:25:20 2016 -0700 @@ -1198,8 +1198,9 @@ public URLStreamHandler createURLStreamHandler(String protocol) { String name = PREFIX + "." + protocol + ".Handler"; try { - Class c = Class.forName(name); - return (URLStreamHandler)c.newInstance(); + @SuppressWarnings("deprecation") + Object o = Class.forName(name).newInstance(); + return (URLStreamHandler)o; } catch (ClassNotFoundException x) { // ignore } catch (Exception e) { @@ -1212,7 +1213,7 @@ private static URLStreamHandler lookupViaProperty(String protocol) { String packagePrefixList = - GetPropertyAction.getProperty(protocolPathProp); + GetPropertyAction.privilegedGetProperty(protocolPathProp); if (packagePrefixList == null) { // not set return null; @@ -1234,7 +1235,9 @@ } } if (cls != null) { - handler = (URLStreamHandler)cls.newInstance(); + @SuppressWarnings("deprecation") + Object tmp = cls.newInstance(); + handler = (URLStreamHandler)tmp; } } catch (Exception e) { // any number of exceptions can get thrown here diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/net/URLConnection.java --- a/jdk/src/java.base/share/classes/java/net/URLConnection.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/net/URLConnection.java Tue May 03 12:25:20 2016 -0700 @@ -1323,7 +1323,9 @@ } } if (cls != null) { - return (ContentHandler) cls.newInstance(); + @SuppressWarnings("deprecation") + Object tmp = cls.newInstance(); + return (ContentHandler) tmp; } } catch(Exception ignored) { } } @@ -1397,7 +1399,7 @@ */ private String getContentHandlerPkgPrefixes() { String packagePrefixList = - GetPropertyAction.getProperty(contentPathProp, ""); + GetPropertyAction.privilegedGetProperty(contentPathProp, ""); if (packagePrefixList != "") { packagePrefixList += "|"; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/net/URLEncoder.java --- a/jdk/src/java.base/share/classes/java/net/URLEncoder.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/net/URLEncoder.java Tue May 03 12:25:20 2016 -0700 @@ -133,7 +133,7 @@ dontNeedEncoding.set('.'); dontNeedEncoding.set('*'); - dfltEncName = GetPropertyAction.getProperty("file.encoding"); + dfltEncName = GetPropertyAction.privilegedGetProperty("file.encoding"); } /** diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/nio/channels/spi/AsynchronousChannelProvider.java --- a/jdk/src/java.base/share/classes/java/nio/channels/spi/AsynchronousChannelProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/nio/channels/spi/AsynchronousChannelProvider.java Tue May 03 12:25:20 2016 -0700 @@ -94,9 +94,10 @@ if (cn == null) return null; try { - Class c = Class.forName(cn, true, - ClassLoader.getSystemClassLoader()); - return (AsynchronousChannelProvider)c.newInstance(); + @SuppressWarnings("deprecation") + Object tmp = Class.forName(cn, true, + ClassLoader.getSystemClassLoader()).newInstance(); + return (AsynchronousChannelProvider)tmp; } catch (ClassNotFoundException x) { throw new ServiceConfigurationError(null, x); } catch (IllegalAccessException x) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/nio/channels/spi/SelectorProvider.java --- a/jdk/src/java.base/share/classes/java/nio/channels/spi/SelectorProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/nio/channels/spi/SelectorProvider.java Tue May 03 12:25:20 2016 -0700 @@ -95,9 +95,10 @@ if (cn == null) return false; try { - Class c = Class.forName(cn, true, - ClassLoader.getSystemClassLoader()); - provider = (SelectorProvider)c.newInstance(); + @SuppressWarnings("deprecation") + Object tmp = Class.forName(cn, true, + ClassLoader.getSystemClassLoader()).newInstance(); + provider = (SelectorProvider)tmp; return true; } catch (ClassNotFoundException x) { throw new ServiceConfigurationError(null, x); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/nio/charset/Charset.java --- a/jdk/src/java.base/share/classes/java/nio/charset/Charset.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/nio/charset/Charset.java Tue May 03 12:25:20 2016 -0700 @@ -283,8 +283,8 @@ if (level == null) { if (!VM.isBooted()) return false; - bugLevel = level = - GetPropertyAction.getProperty("sun.nio.cs.bugLevel", ""); + bugLevel = level = GetPropertyAction + .privilegedGetProperty("sun.nio.cs.bugLevel", ""); } return level.equals(bl); } @@ -609,7 +609,8 @@ public static Charset defaultCharset() { if (defaultCharset == null) { synchronized (Charset.class) { - String csn = GetPropertyAction.getProperty("file.encoding"); + String csn = GetPropertyAction + .privilegedGetProperty("file.encoding"); Charset cs = lookup(csn); if (cs != null) defaultCharset = cs; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/nio/file/TempFileHelper.java --- a/jdk/src/java.base/share/classes/java/nio/file/TempFileHelper.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/nio/file/TempFileHelper.java Tue May 03 12:25:20 2016 -0700 @@ -46,7 +46,7 @@ // temporary directory location private static final Path tmpdir = - Paths.get(GetPropertyAction.getProperty("java.io.tmpdir")); + Paths.get(GetPropertyAction.privilegedGetProperty("java.io.tmpdir")); private static final boolean isPosix = FileSystems.getDefault().supportedFileAttributeViews().contains("posix"); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/security/Policy.java --- a/jdk/src/java.base/share/classes/java/security/Policy.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/security/Policy.java Tue May 03 12:25:20 2016 -0700 @@ -222,8 +222,9 @@ public Policy run() { try { ClassLoader scl = ClassLoader.getSystemClassLoader(); - Class c = Class.forName(policyProvider, true, scl); - return (Policy)c.newInstance(); + @SuppressWarnings("deprecation") + Object o = Class.forName(policyProvider, true, scl).newInstance(); + return (Policy)o; } catch (Exception e) { if (debug != null) { debug.println("policy provider " + policyProvider + diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/time/zone/ZoneRulesProvider.java --- a/jdk/src/java.base/share/classes/java/time/zone/ZoneRulesProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/time/zone/ZoneRulesProvider.java Tue May 03 12:25:20 2016 -0700 @@ -147,6 +147,7 @@ if (prop != null) { try { Class c = Class.forName(prop, true, ClassLoader.getSystemClassLoader()); + @SuppressWarnings("deprecation") ZoneRulesProvider provider = ZoneRulesProvider.class.cast(c.newInstance()); registerProvider(provider); loaded.add(provider); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/util/Locale.java --- a/jdk/src/java.base/share/classes/java/util/Locale.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/Locale.java Tue May 03 12:25:20 2016 -0700 @@ -858,7 +858,7 @@ private static Locale initDefault() { String language, region, script, country, variant; - Properties props = GetPropertyAction.getProperties(); + Properties props = GetPropertyAction.privilegedGetProperties(); language = props.getProperty("user.language", "en"); // for compatibility, check for old user.region property region = props.getProperty("user.region"); @@ -883,7 +883,7 @@ } private static Locale initDefault(Locale.Category category) { - Properties props = GetPropertyAction.getProperties(); + Properties props = GetPropertyAction.privilegedGetProperties(); return getInstance( props.getProperty(category.languageKey, defaultLocale.getLanguage()), diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/util/PropertyResourceBundle.java --- a/jdk/src/java.base/share/classes/java/util/PropertyResourceBundle.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/PropertyResourceBundle.java Tue May 03 12:25:20 2016 -0700 @@ -140,9 +140,8 @@ // Check whether the strict encoding is specified. // The possible encoding is either "ISO-8859-1" or "UTF-8". - private static final String encoding = - GetPropertyAction - .getProperty("java.util.PropertyResourceBundle.encoding", "") + private static final String encoding = GetPropertyAction + .privilegedGetProperty("java.util.PropertyResourceBundle.encoding", "") .toUpperCase(Locale.ROOT); /** diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/util/ServiceLoader.java --- a/jdk/src/java.base/share/classes/java/util/ServiceLoader.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/ServiceLoader.java Tue May 03 12:25:20 2016 -0700 @@ -29,8 +29,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.lang.module.ModuleDescriptor; -import java.lang.module.ModuleDescriptor.Provides; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Layer; @@ -85,7 +83,7 @@ * and deployed as a named module must have an appropriate uses clause * in its module descriptor to declare that the module uses * implementations of the service. A corresponding requirement is that a - * provider deployed as a named modules must have an appropriate + * provider deployed as a named module must have an appropriate * provides clause in its module descriptor to declare that the module * provides an implementation of the service. The uses and * provides allow consumers of a service to be linked to @@ -550,35 +548,29 @@ /** * Implements lazy service provider lookup of service providers that * are provided by modules in a module Layer. - * - * For now, this iterator examines all modules in each Layer. This will - * be replaced once we decide on how the service-use graph is exposed - * in the module API. */ private class LayerLookupIterator extends RestrictedIterator { final String serviceName; Layer currentLayer; - Iterator descriptorIterator; - Iterator providersIterator; - - Module nextModule; - String nextProvider; + Iterator iterator; + ServiceProvider nextProvider; LayerLookupIterator() { serviceName = service.getName(); currentLayer = layer; // need to get us started - descriptorIterator = descriptors(layer, serviceName); + iterator = providers(currentLayer, serviceName); } - Iterator descriptors(Layer layer, String service) { - return layer.modules().stream() - .map(Module::getDescriptor) - .filter(d -> d.provides().get(service) != null) - .iterator(); + Iterator providers(Layer layer, String service) { + ServicesCatalog catalog = SharedSecrets + .getJavaLangReflectModuleAccess() + .getServicesCatalog(layer); + + return catalog.findServices(serviceName).iterator(); } @Override @@ -591,30 +583,18 @@ while (true) { // next provider - if (providersIterator != null && providersIterator.hasNext()) { - nextProvider = providersIterator.next(); + if (iterator != null && iterator.hasNext()) { + nextProvider = iterator.next(); return true; } - // next descriptor - if (descriptorIterator.hasNext()) { - ModuleDescriptor descriptor = descriptorIterator.next(); - - nextModule = currentLayer.findModule(descriptor.name()).get(); - - Provides provides = descriptor.provides().get(serviceName); - providersIterator = provides.providers().iterator(); - - continue; - } - // next layer Layer parent = currentLayer.parent().orElse(null); if (parent == null) return false; currentLayer = parent; - descriptorIterator = descriptors(currentLayer, serviceName); + iterator = providers(currentLayer, serviceName); } } @@ -623,13 +603,14 @@ if (!hasNextService()) throw new NoSuchElementException(); - assert nextModule != null && nextProvider != null; - - String cn = nextProvider; + ServiceProvider provider = nextProvider; nextProvider = null; + Module module = provider.module(); + String cn = provider.providerName(); + // attempt to load the provider - Class c = loadClassInModule(nextModule, cn); + Class c = loadClassInModule(module, cn); if (c == null) fail(service, "Provider " + cn + " not found"); if (!service.isAssignableFrom(c)) @@ -830,7 +811,9 @@ } S p = null; try { - p = service.cast(c.newInstance()); + @SuppressWarnings("deprecation") + Object tmp = c.newInstance(); + p = service.cast(tmp); } catch (Throwable x) { fail(service, "Provider " + cn + " could not be instantiated", diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/util/TimeZone.java --- a/jdk/src/java.base/share/classes/java/util/TimeZone.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/TimeZone.java Tue May 03 12:25:20 2016 -0700 @@ -42,6 +42,7 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.time.ZoneId; +import java.util.Properties; import sun.security.action.GetPropertyAction; import sun.util.calendar.ZoneInfo; import sun.util.calendar.ZoneInfoFile; @@ -660,12 +661,13 @@ private static synchronized TimeZone setDefaultZone() { TimeZone tz; // get the time zone ID from the system properties - String zoneID = GetPropertyAction.getProperty("user.timezone"); + Properties props = GetPropertyAction.privilegedGetProperties(); + String zoneID = props.getProperty("user.timezone"); // if the time zone ID is not set (yet), perform the // platform to Java time zone ID mapping. if (zoneID == null || zoneID.isEmpty()) { - String javaHome = GetPropertyAction.getProperty("java.home"); + String javaHome = props.getProperty("java.home"); try { zoneID = getSystemTimeZoneID(javaHome); if (zoneID == null) { @@ -693,13 +695,7 @@ assert tz != null; final String id = zoneID; - AccessController.doPrivileged(new PrivilegedAction<>() { - @Override - public Void run() { - System.setProperty("user.timezone", id); - return null; - } - }); + props.setProperty("user.timezone", id); defaultTimeZone = tz; return tz; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java Tue May 03 12:25:20 2016 -0700 @@ -3507,6 +3507,7 @@ * Creates and returns the common pool, respecting user settings * specified via system properties. */ + @SuppressWarnings("deprecation") // Class.newInstance static ForkJoinPool makeCommonPool() { int parallelism = -1; ForkJoinWorkerThreadFactory factory = null; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/util/jar/JarFile.java --- a/jdk/src/java.base/share/classes/java/util/jar/JarFile.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/jar/JarFile.java Tue May 03 12:25:20 2016 -0700 @@ -155,7 +155,7 @@ BASE_VERSION = 8; // one less than lowest version for versioned entries int runtimeVersion = jdk.Version.current().major(); String jarVersion = - GetPropertyAction.getProperty("jdk.util.jar.version"); + GetPropertyAction.privilegedGetProperty("jdk.util.jar.version"); if (jarVersion != null) { int jarVer = Integer.parseInt(jarVersion); runtimeVersion = (jarVer > runtimeVersion) @@ -163,7 +163,7 @@ } RUNTIME_VERSION = runtimeVersion; String enableMultiRelease = GetPropertyAction - .getProperty("jdk.util.jar.enableMultiRelease", "true"); + .privilegedGetProperty("jdk.util.jar.enableMultiRelease", "true"); switch (enableMultiRelease) { case "true": default: diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/util/jar/Pack200.java --- a/jdk/src/java.base/share/classes/java/util/jar/Pack200.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/jar/Pack200.java Tue May 03 12:25:20 2016 -0700 @@ -695,7 +695,7 @@ Class impl = (PACK_PROVIDER.equals(prop))? packerImpl: unpackerImpl; if (impl == null) { // The first time, we must decide which class to use. - implName = GetPropertyAction.getProperty(prop,""); + implName = GetPropertyAction.privilegedGetProperty(prop,""); if (implName != null && !implName.equals("")) impl = Class.forName(implName); else if (PACK_PROVIDER.equals(prop)) @@ -704,7 +704,9 @@ impl = com.sun.java.util.jar.pack.UnpackerImpl.class; } // We have a class. Now instantiate it. - return impl.newInstance(); + @SuppressWarnings("deprecation") + Object result = impl.newInstance(); + return result; } catch (ClassNotFoundException e) { throw new Error("Class not found: " + implName + ":\ncheck property " + prop + diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/util/regex/PatternSyntaxException.java --- a/jdk/src/java.base/share/classes/java/util/regex/PatternSyntaxException.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/regex/PatternSyntaxException.java Tue May 03 12:25:20 2016 -0700 @@ -94,7 +94,7 @@ } private static final String nl = - GetPropertyAction.getProperty("line.separator"); + GetPropertyAction.privilegedGetProperty("line.separator"); /** * Returns a multi-line string containing the description of the syntax diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/util/stream/DoublePipeline.java --- a/jdk/src/java.base/share/classes/java/util/stream/DoublePipeline.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/stream/DoublePipeline.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -167,6 +167,19 @@ return Nodes.doubleBuilder(exactSizeIfKnown); } + private Stream mapToObj(DoubleFunction mapper, int opFlags) { + return new ReferencePipeline.StatelessOp(this, StreamShape.DOUBLE_VALUE, opFlags) { + @Override + Sink opWrapSink(int flags, Sink sink) { + return new Sink.ChainedDouble(sink) { + @Override + public void accept(double t) { + downstream.accept(mapper.apply(t)); + } + }; + } + }; + } // DoubleStream @@ -184,7 +197,7 @@ @Override public final Stream boxed() { - return mapToObj(Double::valueOf); + return mapToObj(Double::valueOf, 0); } @Override @@ -207,18 +220,7 @@ @Override public final Stream mapToObj(DoubleFunction mapper) { Objects.requireNonNull(mapper); - return new ReferencePipeline.StatelessOp(this, StreamShape.DOUBLE_VALUE, - StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { - @Override - Sink opWrapSink(int flags, Sink sink) { - return new Sink.ChainedDouble(sink) { - @Override - public void accept(double t) { - downstream.accept(mapper.apply(t)); - } - }; - } - }; + return mapToObj(mapper, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT); } @Override diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/util/stream/IntPipeline.java --- a/jdk/src/java.base/share/classes/java/util/stream/IntPipeline.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/stream/IntPipeline.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -170,6 +170,19 @@ return Nodes.intBuilder(exactSizeIfKnown); } + private Stream mapToObj(IntFunction mapper, int opFlags) { + return new ReferencePipeline.StatelessOp(this, StreamShape.INT_VALUE, opFlags) { + @Override + Sink opWrapSink(int flags, Sink sink) { + return new Sink.ChainedInt(sink) { + @Override + public void accept(int t) { + downstream.accept(mapper.apply(t)); + } + }; + } + }; + } // IntStream @@ -187,8 +200,7 @@ @Override public final LongStream asLongStream() { - return new LongPipeline.StatelessOp(this, StreamShape.INT_VALUE, - StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { + return new LongPipeline.StatelessOp(this, StreamShape.INT_VALUE, 0) { @Override Sink opWrapSink(int flags, Sink sink) { return new Sink.ChainedInt(sink) { @@ -203,8 +215,7 @@ @Override public final DoubleStream asDoubleStream() { - return new DoublePipeline.StatelessOp(this, StreamShape.INT_VALUE, - StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { + return new DoublePipeline.StatelessOp(this, StreamShape.INT_VALUE, 0) { @Override Sink opWrapSink(int flags, Sink sink) { return new Sink.ChainedInt(sink) { @@ -219,7 +230,7 @@ @Override public final Stream boxed() { - return mapToObj(Integer::valueOf); + return mapToObj(Integer::valueOf, 0); } @Override @@ -242,18 +253,7 @@ @Override public final Stream mapToObj(IntFunction mapper) { Objects.requireNonNull(mapper); - return new ReferencePipeline.StatelessOp(this, StreamShape.INT_VALUE, - StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { - @Override - Sink opWrapSink(int flags, Sink sink) { - return new Sink.ChainedInt(sink) { - @Override - public void accept(int t) { - downstream.accept(mapper.apply(t)); - } - }; - } - }; + return mapToObj(mapper, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT); } @Override diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/util/stream/LongPipeline.java --- a/jdk/src/java.base/share/classes/java/util/stream/LongPipeline.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/stream/LongPipeline.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -167,6 +167,19 @@ return Nodes.longBuilder(exactSizeIfKnown); } + private Stream mapToObj(LongFunction mapper, int opFlags) { + return new ReferencePipeline.StatelessOp(this, StreamShape.LONG_VALUE, opFlags) { + @Override + Sink opWrapSink(int flags, Sink sink) { + return new Sink.ChainedLong(sink) { + @Override + public void accept(long t) { + downstream.accept(mapper.apply(t)); + } + }; + } + }; + } // LongStream @@ -184,8 +197,7 @@ @Override public final DoubleStream asDoubleStream() { - return new DoublePipeline.StatelessOp(this, StreamShape.LONG_VALUE, - StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { + return new DoublePipeline.StatelessOp(this, StreamShape.LONG_VALUE, StreamOpFlag.NOT_DISTINCT) { @Override Sink opWrapSink(int flags, Sink sink) { return new Sink.ChainedLong(sink) { @@ -200,7 +212,7 @@ @Override public final Stream boxed() { - return mapToObj(Long::valueOf); + return mapToObj(Long::valueOf, 0); } @Override @@ -223,18 +235,7 @@ @Override public final Stream mapToObj(LongFunction mapper) { Objects.requireNonNull(mapper); - return new ReferencePipeline.StatelessOp(this, StreamShape.LONG_VALUE, - StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { - @Override - Sink opWrapSink(int flags, Sink sink) { - return new Sink.ChainedLong(sink) { - @Override - public void accept(long t) { - downstream.accept(mapper.apply(t)); - } - }; - } - }; + return mapToObj(mapper, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT); } @Override diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/util/stream/StreamSpliterators.java --- a/jdk/src/java.base/share/classes/java/util/stream/StreamSpliterators.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/stream/StreamSpliterators.java Tue May 03 12:25:20 2016 -0700 @@ -28,6 +28,7 @@ import java.util.Objects; import java.util.Spliterator; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ForkJoinPool; import java.util.concurrent.atomic.AtomicLong; import java.util.function.BooleanSupplier; import java.util.function.Consumer; @@ -905,6 +906,7 @@ // The spliterator to slice protected final T_SPLITR s; protected final boolean unlimited; + protected final int chunkSize; private final long skipThreshold; private final AtomicLong permits; @@ -912,6 +914,8 @@ this.s = s; this.unlimited = limit < 0; this.skipThreshold = limit >= 0 ? limit : 0; + this.chunkSize = limit >= 0 ? (int)Math.min(CHUNK_SIZE, + ((skip + limit) / AbstractTask.LEAF_TARGET) + 1) : CHUNK_SIZE; this.permits = new AtomicLong(limit >= 0 ? skip + limit : skip); } @@ -921,6 +925,7 @@ this.unlimited = parent.unlimited; this.permits = parent.permits; this.skipThreshold = parent.skipThreshold; + this.chunkSize = parent.chunkSize; } /** @@ -1029,13 +1034,13 @@ PermitStatus permitStatus; while ((permitStatus = permitStatus()) != PermitStatus.NO_MORE) { if (permitStatus == PermitStatus.MAYBE_MORE) { - // Optimistically traverse elements up to a threshold of CHUNK_SIZE + // Optimistically traverse elements up to a threshold of chunkSize if (sb == null) - sb = new ArrayBuffer.OfRef<>(CHUNK_SIZE); + sb = new ArrayBuffer.OfRef<>(chunkSize); else sb.reset(); long permitsRequested = 0; - do { } while (s.tryAdvance(sb) && ++permitsRequested < CHUNK_SIZE); + do { } while (s.tryAdvance(sb) && ++permitsRequested < chunkSize); if (permitsRequested == 0) return; sb.forEach(action, acquirePermits(permitsRequested)); @@ -1102,15 +1107,15 @@ PermitStatus permitStatus; while ((permitStatus = permitStatus()) != PermitStatus.NO_MORE) { if (permitStatus == PermitStatus.MAYBE_MORE) { - // Optimistically traverse elements up to a threshold of CHUNK_SIZE + // Optimistically traverse elements up to a threshold of chunkSize if (sb == null) - sb = bufferCreate(CHUNK_SIZE); + sb = bufferCreate(chunkSize); else sb.reset(); @SuppressWarnings("unchecked") T_CONS sbc = (T_CONS) sb; long permitsRequested = 0; - do { } while (s.tryAdvance(sbc) && ++permitsRequested < CHUNK_SIZE); + do { } while (s.tryAdvance(sbc) && ++permitsRequested < chunkSize); if (permitsRequested == 0) return; sb.forEach(action, acquirePermits(permitsRequested)); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/java/util/zip/ZipOutputStream.java --- a/jdk/src/java.base/share/classes/java/util/zip/ZipOutputStream.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipOutputStream.java Tue May 03 12:25:20 2016 -0700 @@ -55,7 +55,7 @@ */ private static final boolean inhibitZip64 = Boolean.parseBoolean( - GetPropertyAction.getProperty("jdk.util.zip.inhibitZip64")); + GetPropertyAction.privilegedGetProperty("jdk.util.zip.inhibitZip64")); private static class XEntry { final ZipEntry entry; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/javax/net/ssl/SSLServerSocketFactory.java --- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLServerSocketFactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLServerSocketFactory.java Tue May 03 12:25:20 2016 -0700 @@ -97,6 +97,7 @@ } } log("class " + clsName + " is loaded"); + @SuppressWarnings("deprecation") SSLServerSocketFactory fac = (SSLServerSocketFactory)cls.newInstance(); log("instantiated an instance of class " + clsName); theFactory = fac; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/javax/net/ssl/SSLSocketFactory.java --- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLSocketFactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLSocketFactory.java Tue May 03 12:25:20 2016 -0700 @@ -51,7 +51,7 @@ static final boolean DEBUG; static { - String s = GetPropertyAction.getProperty("javax.net.debug", "") + String s = GetPropertyAction.privilegedGetProperty("javax.net.debug", "") .toLowerCase(Locale.ENGLISH); DEBUG = s.contains("all") || s.contains("ssl"); @@ -106,6 +106,7 @@ } } log("class " + clsName + " is loaded"); + @SuppressWarnings("deprecation") SSLSocketFactory fac = (SSLSocketFactory)cls.newInstance(); log("instantiated an instance of class " + clsName); theFactory = fac; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/javax/security/auth/login/Configuration.java --- a/jdk/src/java.base/share/classes/javax/security/auth/login/Configuration.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/javax/security/auth/login/Configuration.java Tue May 03 12:25:20 2016 -0700 @@ -250,7 +250,9 @@ finalClass, false, Thread.currentThread().getContextClassLoader() ).asSubclass(Configuration.class); - return implClass.newInstance(); + @SuppressWarnings("deprecation") + Configuration result = implClass.newInstance(); + return result; } }); AccessController.doPrivileged( diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/javax/security/auth/login/LoginContext.java --- a/jdk/src/java.base/share/classes/javax/security/auth/login/LoginContext.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/javax/security/auth/login/LoginContext.java Tue May 03 12:25:20 2016 -0700 @@ -304,7 +304,9 @@ Class c = Class.forName( defaultHandler, true, finalLoader).asSubclass(CallbackHandler.class); - return c.newInstance(); + @SuppressWarnings("deprecation") + CallbackHandler result = c.newInstance(); + return result; } }); } catch (java.security.PrivilegedActionException pae) { @@ -697,8 +699,9 @@ if (moduleStack[i].module == null) { try { - moduleStack[i].module = (LoginModule) Class.forName( - name, false, contextClassLoader).newInstance(); + @SuppressWarnings("deprecation") + Object tmp = Class.forName(name, false, contextClassLoader).newInstance(); + moduleStack[i].module = (LoginModule) tmp; if (debug != null) { debug.println(name + " loaded via reflection"); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/Version.java --- a/jdk/src/java.base/share/classes/jdk/Version.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/Version.java Tue May 03 12:25:20 2016 -0700 @@ -273,7 +273,8 @@ */ public static Version current() { if (current == null) { - current = parse(GetPropertyAction.getProperty("java.version")); + current = parse( + GetPropertyAction.privilegedGetProperty("java.version")); } return current; } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java --- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java Tue May 03 12:25:20 2016 -0700 @@ -124,7 +124,9 @@ ClassLoader cl = newJrtFsLoader(jrtfs); try { Class c = Class.forName(JrtFileSystemProvider.class.getName(), false, cl); - return ((FileSystemProvider)c.newInstance()).newFileSystem(uri, newEnv); + @SuppressWarnings("deprecation") + Object tmp = c.newInstance(); + return ((FileSystemProvider)tmp).newFileSystem(uri, newEnv); } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/loader/BootLoader.java --- a/jdk/src/java.base/share/classes/jdk/internal/loader/BootLoader.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/loader/BootLoader.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -68,7 +68,7 @@ } // ServiceCatalog for the boot class loader - private static final ServicesCatalog SERVICES_CATALOG = new ServicesCatalog(); + private static final ServicesCatalog SERVICES_CATALOG = ServicesCatalog.create(); // ClassLoaderValue map for boot class loader private static final ConcurrentHashMap CLASS_LOADER_VALUE_MAP = diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java --- a/jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java Tue May 03 12:25:20 2016 -0700 @@ -104,7 +104,7 @@ * A module defined/loaded by a built-in class loader. * * A LoadedModule encapsulates a ModuleReference along with its CodeSource - * URL to avoid needing to create this URL when define classes. + * URL to avoid needing to create this URL when defining classes. */ private static class LoadedModule { private final BuiltinClassLoader loader; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java --- a/jdk/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java Tue May 03 12:25:20 2016 -0700 @@ -85,7 +85,7 @@ private static final boolean DISABLE_JAR_CHECKING; static { - Properties props = GetPropertyAction.getProperties(); + Properties props = GetPropertyAction.privilegedGetProperties(); JAVA_VERSION = props.getProperty("java.version"); DEBUG = (props.getProperty("sun.misc.URLClassPath.debug") != null); String p = props.getProperty("sun.misc.URLClassPath.disableJarChecking"); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/logger/LoggerFinderLoader.java --- a/jdk/src/java.base/share/classes/jdk/internal/logger/LoggerFinderLoader.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/logger/LoggerFinderLoader.java Tue May 03 12:25:20 2016 -0700 @@ -81,7 +81,7 @@ // Get configuration error policy private static ErrorPolicy configurationErrorPolicy() { String errorPolicy = - GetPropertyAction.getProperty("jdk.logger.finder.error"); + GetPropertyAction.privilegedGetProperty("jdk.logger.finder.error"); if (errorPolicy == null || errorPolicy.isEmpty()) { return ErrorPolicy.WARNING; } @@ -96,7 +96,7 @@ // This is further submitted to the configuration error policy. private static boolean ensureSingletonProvider() { return Boolean.parseBoolean( - GetPropertyAction.getProperty("jdk.logger.finder.singleton")); + GetPropertyAction.privilegedGetProperty("jdk.logger.finder.singleton")); } private static Iterator findLoggerFinderProviders() { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/logger/SimpleConsoleLogger.java --- a/jdk/src/java.base/share/classes/jdk/internal/logger/SimpleConsoleLogger.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/logger/SimpleConsoleLogger.java Tue May 03 12:25:20 2016 -0700 @@ -56,7 +56,7 @@ static Level getDefaultLevel() { String levelName = GetPropertyAction - .getProperty("jdk.system.logger.level", "INFO"); + .privilegedGetProperty("jdk.system.logger.level", "INFO"); try { return Level.valueOf(levelName); } catch (IllegalArgumentException iae) { @@ -426,7 +426,7 @@ static private final String[] skips; static { String additionalPkgs = - GetPropertyAction.getProperty("jdk.logger.packages"); + GetPropertyAction.privilegedGetProperty("jdk.logger.packages"); skips = additionalPkgs == null ? new String[0] : additionalPkgs.split(","); } @@ -485,7 +485,7 @@ // jdk/test/java/lang/invoke/lambda/LogGeneratedClassesTest.java // to fail - because that test has a testcase which somehow references // PlatformLogger and counts the number of generated lambda classes. - String format = GetPropertyAction.getProperty(key); + String format = GetPropertyAction.privilegedGetProperty(key); if (format == null && defaultPropertyGetter != null) { format = defaultPropertyGetter.apply(key); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java --- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -25,13 +25,24 @@ package jdk.internal.misc; +import java.io.PrintStream; +import java.lang.module.Configuration; +import jdk.internal.module.ModuleHashes; + import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleDescriptor.Exports; import java.lang.module.ModuleDescriptor.Requires; import java.lang.module.ModuleDescriptor.Provides; import java.lang.module.ModuleDescriptor.Version; +import java.lang.module.ModuleFinder; +import java.util.Collection; +import java.lang.module.ModuleReader; +import java.lang.module.ModuleReference; +import java.net.URI; import java.util.Map; +import java.util.Optional; import java.util.Set; +import java.util.function.Supplier; /** * Provides access to non-public methods in java.lang.module. @@ -89,5 +100,29 @@ String osArch, String osVersion, Set conceals, - Set packages); + Set packages, + ModuleHashes hashes); + + /** + * Resolves a collection of root modules, with service binding + * and the empty configuration as the parent. The post resolution + * checks are optionally run. + */ + Configuration resolveRequiresAndUses(ModuleFinder finder, + Collection roots, + boolean check, + PrintStream traceOutput); + + /** + * Creates a ModuleReference to a "patched" module. + */ + ModuleReference newPatchedModule(ModuleDescriptor descriptor, + URI location, + Supplier readerSupplier); + + /** + * Returns the object with the hashes of other modules + */ + Optional hashes(ModuleDescriptor descriptor); + } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangReflectModuleAccess.java --- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangReflectModuleAccess.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangReflectModuleAccess.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -26,9 +26,12 @@ package jdk.internal.misc; import java.lang.module.ModuleDescriptor; +import java.lang.reflect.Layer; import java.lang.reflect.Module; import java.net.URI; +import jdk.internal.module.ServicesCatalog; + /** * Provides access to non-public methods in java.lang.reflect.Module */ @@ -57,6 +60,11 @@ void addReads(Module m1, Module m2); /** + * Updates module m to read all unnamed modules. + */ + void addReadsAllUnnamed(Module m); + + /** * Updates module m1 to export a package to module m2. The export does * not result in a strong reference to m2 (m2 can be GC'ed). */ @@ -76,4 +84,10 @@ * Add a package to the given module. */ void addPackage(Module m, String pkg); -} + + /** + * Returns the ServicesCatalog for the given Layer. + */ + ServicesCatalog getServicesCatalog(Layer layer); + +} \ No newline at end of file diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/module/Builder.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/Builder.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/Builder.java Tue May 03 12:25:20 2016 -0700 @@ -74,6 +74,8 @@ String osName; String osArch; String osVersion; + String algorithm; + Map hashes; Builder(String name, int reqs, int exports, int provides, int conceals, int packages) { @@ -252,6 +254,25 @@ } /** + * Sets the algorithm of the module hashes + */ + public Builder algorithm(String algorithm) { + this.algorithm = algorithm; + return this; + } + + /** + * Sets the module hash for the given module name + */ + public Builder moduleHash(String mn, String hash) { + if (hashes == null) + hashes = new HashMap<>(); + + hashes.put(mn, hash); + return this; + } + + /** * Returns the set of packages that is the union of the exported and * concealed packages. */ @@ -273,6 +294,9 @@ public ModuleDescriptor build() { assert name != null; + ModuleHashes moduleHashes = + hashes != null ? new ModuleHashes(algorithm, hashes) : null; + return jlma.newModuleDescriptor(name, false, // automatic false, // assume not synthetic for now @@ -286,6 +310,7 @@ osArch, osVersion, conceals, - computePackages(exports, conceals)); + computePackages(exports, conceals), + moduleHashes); } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java Tue May 03 12:25:20 2016 -0700 @@ -34,6 +34,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; @@ -42,7 +43,6 @@ import jdk.internal.org.objectweb.asm.ClassReader; import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.Label; -import jdk.internal.module.Hasher.DependencyHashes; import static jdk.internal.module.ClassFileConstants.*; @@ -148,7 +148,7 @@ for (int i=0; i new HashSet<>()).add(cn); + provides.computeIfAbsent(sn, k -> new LinkedHashSet<>()).add(cn); off += 4; } provides.entrySet().forEach(e -> builder.provides(e.getKey(), @@ -281,10 +281,10 @@ * u4 attribute_length; * * // the number of entries in the packages table - * u2 package_count; + * u2 packages_count; * { // index to CONSTANT_CONSTANT_utf8_info structure with the package name * u2 package_index - * } package[package_count]; + * } packages[package_count]; * * } */ @@ -579,9 +579,9 @@ * alternative is to store it as an array of u1. */ static class HashesAttribute extends Attribute { - private final DependencyHashes hashes; + private final ModuleHashes hashes; - HashesAttribute(DependencyHashes hashes) { + HashesAttribute(ModuleHashes hashes) { super(HASHES); this.hashes = hashes; } @@ -613,7 +613,7 @@ map.put(dn, hash); } - DependencyHashes hashes = new DependencyHashes(algorithm, map); + ModuleHashes hashes = new ModuleHashes(algorithm, map); return new HashesAttribute(hashes); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/module/Hasher.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/Hasher.java Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2015, 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.module; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.file.Path; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Base64; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * Supporting class for computing, encoding and decoding hashes (message - * digests). - */ - -public class Hasher { - private Hasher() { } - - /** - * A supplier of an encoded message digest. - */ - public static interface HashSupplier { - String generate(String algorithm); - } - - /** - * Encapsulates the result of hashing the contents of a number of module - * artifacts. - */ - public static class DependencyHashes { - private final String algorithm; - private final Map nameToHash; - - public DependencyHashes(String algorithm, Map nameToHash) { - this.algorithm = algorithm; - this.nameToHash = nameToHash; - } - - /** - * Returns the algorithm used to hash the dependences ("SHA-256" or - * "MD5" for example). - */ - public String algorithm() { - return algorithm; - } - - /** - * Returns the set of module names for which hashes are recorded. - */ - public Set names() { - return nameToHash.keySet(); - } - - /** - * Retruns the hash string for the given module name, {@code null} - * if there is no hash recorded for the module. - */ - public String hashFor(String dn) { - return nameToHash.get(dn); - } - } - - - /** - * Computes the hash for the given file with the given message digest - * algorithm. Returns the results a base64-encoded String. - * - * @throws UncheckedIOException if an I/O error occurs - * @throws RuntimeException if the algorithm is not available - */ - public static String generate(Path file, String algorithm) { - try { - MessageDigest md = MessageDigest.getInstance(algorithm); - - // Ideally we would just mmap the file but this consumes too much - // memory when jlink is running concurrently on very large jmods - try (FileChannel fc = FileChannel.open(file)) { - ByteBuffer bb = ByteBuffer.allocate(32*1024); - int nread; - while ((nread = fc.read(bb)) > 0) { - bb.flip(); - md.update(bb); - assert bb.remaining() == 0; - bb.clear(); - } - } - - byte[] bytes = md.digest(); - return Base64.getEncoder().encodeToString(bytes); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } catch (IOException ioe) { - throw new UncheckedIOException(ioe); - } - } - - /** - * Computes the hash for every entry in the given map, returning a - * {@code DependencyHashes} to encapsulate the result. The map key is - * the entry name, typically the module name. The map value is the file - * path to the entry (module artifact). - * - * @return DependencyHashes encapsulate the hashes - */ - public static DependencyHashes generate(Map map, String algorithm) { - Map nameToHash = new HashMap<>(); - for (Map.Entry entry: map.entrySet()) { - String name = entry.getKey(); - Path path = entry.getValue(); - nameToHash.put(name, generate(path, algorithm)); - } - return new DependencyHashes(algorithm, nameToHash); - } -} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java Tue May 03 12:25:20 2016 -0700 @@ -26,12 +26,15 @@ package jdk.internal.module; import java.io.File; +import java.io.PrintStream; import java.lang.module.Configuration; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; import java.lang.module.ModuleReference; -import java.lang.module.ModuleFinder; import java.lang.module.ResolvedModule; import java.lang.reflect.Layer; import java.lang.reflect.Module; +import java.net.URI; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; @@ -41,10 +44,10 @@ import java.util.Optional; import java.util.Set; import java.util.function.Function; -import java.util.stream.Collectors; import jdk.internal.loader.BootLoader; import jdk.internal.loader.BuiltinClassLoader; +import jdk.internal.misc.SharedSecrets; import jdk.internal.perf.PerfCounter; /** @@ -54,10 +57,9 @@ * the module system. In summary, the boot method creates a Configuration by * resolving a set of module names specified via the launcher (or equivalent) * -m and -addmods options. The modules are located on a module path that is - * constructed from the upgrade, system and application module paths. The - * Configuration is reified by creating the boot Layer with each module in the - * the configuration defined to one of the built-in class loaders. The mapping - * of modules to class loaders is statically mapped in a helper class. + * constructed from the upgrade module path, system modules, and application + * module path. The Configuration is instantiated as the boot Layer with each + * module in the the configuration defined to one of the built-in class loaders. */ public final class ModuleBootstrap { @@ -65,6 +67,11 @@ private static final String JAVA_BASE = "java.base"; + private static final String JAVA_SE = "java.se"; + + // the token for "all default modules" + private static final String ALL_DEFAULT = "ALL-DEFAULT"; + // the token for "all unnamed modules" private static final String ALL_UNNAMED = "ALL-UNNAMED"; @@ -94,47 +101,65 @@ long t0 = System.nanoTime(); - // system module path - ModuleFinder systemModulePath = ModuleFinder.ofSystem(); + // system modules + ModuleFinder systemModules = ModuleFinder.ofSystem(); + + PerfCounters.systemModulesTime.addElapsedTimeFrom(t0); - // Once we have the system module path then we define the base module. - // We do this here so that java.base is defined to the VM as early as + + long t1 = System.nanoTime(); + + // Once we have the system modules then we define the base module to + // the VM. We do this here so that java.base is defined as early as // possible and also that resources in the base module can be located // for error messages that may happen from here on. - Optional obase = systemModulePath.find(JAVA_BASE); - if (!obase.isPresent()) + ModuleReference base = systemModules.find(JAVA_BASE).orElse(null); + if (base == null) throw new InternalError(JAVA_BASE + " not found"); - ModuleReference base = obase.get(); + URI baseUri = base.location().orElse(null); + if (baseUri == null) + throw new InternalError(JAVA_BASE + " does not have a location"); BootLoader.loadModule(base); - Modules.defineModule(null, base.descriptor(), base.location().orElse(null)); + Modules.defineModule(null, base.descriptor(), baseUri); + PerfCounters.defineBaseTime.addElapsedTimeFrom(t1); + + + long t2 = System.nanoTime(); // -upgrademodulepath option specified to launcher ModuleFinder upgradeModulePath = createModulePathFinder("jdk.upgrade.module.path"); + if (upgradeModulePath != null) + systemModules = ModuleFinder.compose(upgradeModulePath, systemModules); // -modulepath option specified to the launcher ModuleFinder appModulePath = createModulePathFinder("jdk.module.path"); - // The module finder: [-upgrademodulepath] system-module-path [-modulepath] - ModuleFinder finder = systemModulePath; - if (upgradeModulePath != null) - finder = ModuleFinder.compose(upgradeModulePath, finder); + // The module finder: [-upgrademodulepath] system [-modulepath] + ModuleFinder finder = systemModules; if (appModulePath != null) finder = ModuleFinder.compose(finder, appModulePath); - // launcher -m option to specify the initial module + // The root modules to resolve + Set roots = new HashSet<>(); + + // launcher -m option to specify the main/initial module String mainModule = System.getProperty("jdk.module.main"); + if (mainModule != null) + roots.add(mainModule); // additional module(s) specified by -addmods + boolean addAllDefaultModules = false; boolean addAllSystemModules = false; boolean addAllApplicationModules = false; - Set addModules = null; String propValue = System.getProperty("jdk.launcher.addmods"); if (propValue != null) { - addModules = new HashSet<>(); for (String mod: propValue.split(",")) { switch (mod) { + case ALL_DEFAULT: + addAllDefaultModules = true; + break; case ALL_SYSTEM: addAllSystemModules = true; break; @@ -142,28 +167,12 @@ addAllApplicationModules = true; break; default : - addModules.add(mod); + roots.add(mod); } } } - // The root modules to resolve - Set roots = new HashSet<>(); - - // main/initial module - if (mainModule != null) { - roots.add(mainModule); - if (addAllApplicationModules) - fail(ALL_MODULE_PATH + " not allowed with initial module"); - } - - // If -addmods is specified then those modules need to be resolved - if (addModules != null) - roots.addAll(addModules); - - // -limitmods - boolean limitmods = false; propValue = System.getProperty("jdk.launcher.limitmods"); if (propValue != null) { Set mods = new HashSet<>(); @@ -171,62 +180,101 @@ mods.add(mod); } finder = limitFinder(finder, mods, roots); - limitmods = true; } - - // If there is no initial module specified then assume that the - // initial module is the unnamed module of the application class - // loader. By convention, and for compatibility, this is - // implemented by putting the names of all modules on the system - // module path into the set of modules to resolve. - // - // If `-addmods ALL-SYSTEM` is used then all modules on the system - // module path will be resolved, irrespective of whether an initial - // module is specified. - // - // If `-addmods ALL-MODULE-PATH` is used, and no initial module is - // specified, then all modules on the application module path will - // be resolved. - // - if (mainModule == null || addAllSystemModules) { - Set mrefs; - if (addAllApplicationModules) { - assert mainModule == null; - mrefs = finder.findAll(); - } else { - mrefs = systemModulePath.findAll(); - if (limitmods) { - ModuleFinder f = finder; - mrefs = mrefs.stream() - .filter(m -> f.find(m.descriptor().name()).isPresent()) - .collect(Collectors.toSet()); + // If there is no initial module specified then assume that the initial + // module is the unnamed module of the application class loader. This + // is implemented by resolving "java.se" and all (non-java.*) modules + // that export an API. If "java.se" is not observable then all java.* + // modules are resolved. + if (mainModule == null || addAllDefaultModules) { + boolean hasJava = false; + if (systemModules.find(JAVA_SE).isPresent()) { + // java.se is a system module + if (finder == systemModules || finder.find(JAVA_SE).isPresent()) { + // java.se is observable + hasJava = true; + roots.add(JAVA_SE); } } - // map to module names - for (ModuleReference mref : mrefs) { - roots.add(mref.descriptor().name()); + + for (ModuleReference mref : systemModules.findAll()) { + String mn = mref.descriptor().name(); + if (hasJava && mn.startsWith("java.")) + continue; + + // add as root if observable and exports at least one package + if ((finder == systemModules || finder.find(mn).isPresent())) { + ModuleDescriptor descriptor = mref.descriptor(); + for (ModuleDescriptor.Exports e : descriptor.exports()) { + if (!e.isQualified()) { + roots.add(mn); + break; + } + } + } } } - long t1 = System.nanoTime(); + // If `-addmods ALL-SYSTEM` is specified then all observable system + // modules will be resolved. + if (addAllSystemModules) { + ModuleFinder f = finder; // observable modules + systemModules.findAll() + .stream() + .map(ModuleReference::descriptor) + .map(ModuleDescriptor::name) + .filter(mn -> f.find(mn).isPresent()) // observable + .forEach(mn -> roots.add(mn)); + } + + // If `-addmods ALL-MODULE-PATH` is specified then all observable + // modules on the application module path will be resolved. + if (appModulePath != null && addAllApplicationModules) { + ModuleFinder f = finder; // observable modules + appModulePath.findAll() + .stream() + .map(ModuleReference::descriptor) + .map(ModuleDescriptor::name) + .filter(mn -> f.find(mn).isPresent()) // observable + .forEach(mn -> roots.add(mn)); + } + + PerfCounters.optionsAndRootsTime.addElapsedTimeFrom(t2); + + + long t3 = System.nanoTime(); + + // determine if post resolution checks are needed + boolean needPostResolutionChecks = true; + if (baseUri.getScheme().equals("jrt") // toLowerCase not needed here + && (upgradeModulePath == null) + && (appModulePath == null) + && (System.getProperty("jdk.launcher.patch.0") == null)) { + needPostResolutionChecks = false; + } + + PrintStream traceOutput = null; + if (Boolean.getBoolean("jdk.launcher.traceResolver")) + traceOutput = System.out; // run the resolver to create the configuration - - Configuration cf = Configuration.empty() + Configuration cf = SharedSecrets.getJavaLangModuleAccess() .resolveRequiresAndUses(finder, - ModuleFinder.empty(), - roots); + roots, + needPostResolutionChecks, + traceOutput); // time to create configuration - PerfCounters.resolveTime.addElapsedTimeFrom(t1); + PerfCounters.resolveTime.addElapsedTimeFrom(t3); + // mapping of modules to class loaders Function clf = ModuleLoaderMap.mappingFunction(cf); // check that all modules to be mapped to the boot loader will be - // loaded from the system module path - if (finder != systemModulePath) { + // loaded from the runtime image + if (needPostResolutionChecks) { for (ResolvedModule resolvedModule : cf.modules()) { ModuleReference mref = resolvedModule.reference(); String name = mref.descriptor().name(); @@ -237,20 +285,22 @@ && upgradeModulePath.find(name).isPresent()) fail(name + ": cannot be loaded from upgrade module path"); - if (!systemModulePath.find(name).isPresent()) + if (!systemModules.find(name).isPresent()) fail(name + ": cannot be loaded from application module path"); } } } - long t2 = System.nanoTime(); + + long t4 = System.nanoTime(); // define modules to VM/runtime Layer bootLayer = Layer.empty().defineModules(cf, clf); - PerfCounters.layerCreateTime.addElapsedTimeFrom(t2); + PerfCounters.layerCreateTime.addElapsedTimeFrom(t4); - long t3 = System.nanoTime(); + + long t5 = System.nanoTime(); // define the module to its class loader, except java.base for (ResolvedModule resolvedModule : cf.modules()) { @@ -264,7 +314,8 @@ } } - PerfCounters.loadModulesTime.addElapsedTimeFrom(t3); + PerfCounters.loadModulesTime.addElapsedTimeFrom(t5); + // -XaddReads and -XaddExports addExtraReads(bootLayer); @@ -295,25 +346,21 @@ // module name -> reference Map map = new HashMap<>(); + + // root modules and their transitive dependences cf.modules().stream() .map(ResolvedModule::reference) .forEach(mref -> map.put(mref.descriptor().name(), mref)); + // additional modules + otherMods.stream() + .map(finder::find) + .flatMap(Optional::stream) + .forEach(mref -> map.putIfAbsent(mref.descriptor().name(), mref)); + // set of modules that are observable Set mrefs = new HashSet<>(map.values()); - // add the other modules - for (String mod : otherMods) { - Optional omref = finder.find(mod); - if (omref.isPresent()) { - ModuleReference mref = omref.get(); - map.putIfAbsent(mod, mref); - mrefs.add(mref); - } else { - // no need to fail - } - } - return new ModuleFinder() { @Override public Optional find(String name) { @@ -369,15 +416,15 @@ Module other; if (ALL_UNNAMED.equals(name)) { - other = null; // loose + Modules.addReadsAllUnnamed(m); } else { om = bootLayer.findModule(name); if (!om.isPresent()) fail("Unknown module: " + name); other = om.get(); + Modules.addReads(m, other); } - Modules.addReads(m, other); } } } @@ -439,10 +486,6 @@ * Decodes the values of -XaddReads or -XaddExports options * * The format of the options is: $KEY=$MODULE(,$MODULE)* - * - * For transition purposes, this method allows the first usage to be - * $KEY=$MODULE(,$KEY=$MODULE) - * This format will eventually be removed. */ private static Map> decode(String prefix) { int index = 0; @@ -467,42 +510,15 @@ if (rhs.isEmpty()) fail("Unable to parse: " + value); - // new format $MODULE(,$MODULE)* or old format $(MODULE)=... - pos = rhs.indexOf('='); - // old format only allowed in first -X option - if (pos >= 0 && index > 0) - fail("Unable to parse: " + value); - - if (pos == -1) { - - // new format: $KEY=$MODULE(,$MODULE)* - - Set values = map.get(key); - if (values != null) - fail(key + " specified more than once"); + // value is (,)* + if (map.containsKey(key)) + fail(key + " specified more than once"); - values = new HashSet<>(); - map.put(key, values); - for (String s : rhs.split(",")) { - if (s.length() > 0) values.add(s); - } - - } else { - - // old format: $KEY=$MODULE(,$KEY=$MODULE)* - - assert index == 0; // old format only allowed in first usage - - for (String expr : value.split(",")) { - if (expr.length() > 0) { - String[] s = expr.split("="); - if (s.length != 2) - fail("Unable to parse: " + expr); - - map.computeIfAbsent(s[0], k -> new HashSet<>()).add(s[1]); - } - } + Set values = new HashSet<>(); + map.put(key, values); + for (String s : rhs.split(",")) { + if (s.length() > 0) values.add(s); } index++; @@ -521,6 +537,13 @@ } static class PerfCounters { + + static PerfCounter systemModulesTime + = PerfCounter.newPerfCounter("jdk.module.bootstrap.systemModulesTime"); + static PerfCounter defineBaseTime + = PerfCounter.newPerfCounter("jdk.module.bootstrap.defineBaseTime"); + static PerfCounter optionsAndRootsTime + = PerfCounter.newPerfCounter("jdk.module.bootstrap.optionsAndRootsTime"); static PerfCounter resolveTime = PerfCounter.newPerfCounter("jdk.module.bootstrap.resolveTime"); static PerfCounter layerCreateTime diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/module/ModuleHashes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleHashes.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2015, 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.module; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * The result of hashing the contents of a number of module artifacts. + */ + +public final class ModuleHashes { + + /** + * A supplier of an encoded message digest. + */ + public static interface HashSupplier { + String generate(String algorithm); + } + + + private final String algorithm; + private final Map nameToHash; + + /** + * Creates a {@code ModuleHashes}. + * + * @param algorithm the algorithm used to create the hashes + * @param nameToHash the map of module name to hash value (in string form) + */ + public ModuleHashes(String algorithm, Map nameToHash) { + this.algorithm = algorithm; + this.nameToHash = Collections.unmodifiableMap(nameToHash); + } + + /** + * Returns the algorithm used to hash the modules ("SHA-256" for example). + */ + public String algorithm() { + return algorithm; + } + + /** + * Returns the set of module names for which hashes are recorded. + */ + public Set names() { + return nameToHash.keySet(); + } + + /** + * Returns the hash string for the given module name, {@code null} + * if there is no hash recorded for the module. + */ + public String hashFor(String mn) { + return nameToHash.get(mn); + } + + /** + * Returns unmodifiable map of module name to hash string. + */ + public Map hashes() { + return nameToHash; + } + + /** + * Computes the hash for the given file with the given message digest + * algorithm. Returns the results a base64-encoded String. + * + * @throws UncheckedIOException if an I/O error occurs + * @throws RuntimeException if the algorithm is not available + */ + public static String computeHashAsString(Path file, String algorithm) { + try { + MessageDigest md = MessageDigest.getInstance(algorithm); + + // Ideally we would just mmap the file but this consumes too much + // memory when jlink is running concurrently on very large jmods + try (FileChannel fc = FileChannel.open(file)) { + ByteBuffer bb = ByteBuffer.allocate(32*1024); + while (fc.read(bb) > 0) { + bb.flip(); + md.update(bb); + assert bb.remaining() == 0; + bb.clear(); + } + } + + byte[] bytes = md.digest(); + return Base64.getEncoder().encodeToString(bytes); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } + + /** + * Computes the hash for every entry in the given map, returning a + * {@code ModuleHashes} to encapsulate the result. The map key is + * the entry name, typically the module name. The map value is the file + * path to the entry (module artifact). + * + * @return ModuleHashes encapsulate the hashes + */ + public static ModuleHashes generate(Map map, String algorithm) { + Map nameToHash = new HashMap<>(); + for (Map.Entry entry: map.entrySet()) { + String name = entry.getKey(); + Path path = entry.getValue(); + nameToHash.put(name, computeHashAsString(path, algorithm)); + } + return new ModuleHashes(algorithm, nameToHash); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -41,7 +41,6 @@ import jdk.internal.org.objectweb.asm.ClassVisitor; import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.Opcodes; -import jdk.internal.module.Hasher.DependencyHashes; import static jdk.internal.module.ClassFileAttributes.*; @@ -69,7 +68,7 @@ private String osVersion; // the hashes for the Hashes attribute - private DependencyHashes hashes; + private ModuleHashes hashes; private ModuleInfoExtender(InputStream in) { this.in = in; @@ -113,10 +112,10 @@ /** * The Hashes attribute will be emitted to the module-info with - * the hashes encapsulated in the given {@code DependencyHashes} + * the hashes encapsulated in the given {@code ModuleHashes} * object. */ - public ModuleInfoExtender hashes(DependencyHashes hashes) { + public ModuleInfoExtender hashes(ModuleHashes hashes) { this.hashes = hashes; return this; } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -49,28 +49,22 @@ * Writes the given module descriptor to a module-info.class file, * returning it in a byte array. */ - private static byte[] toModuleInfo(ModuleDescriptor descriptor) { + private static byte[] toModuleInfo(ModuleDescriptor md) { ClassWriter cw = new ClassWriter(0); - String name = descriptor.name().replace('.', '/') + "/module-info"; + String name = md.name().replace('.', '/') + "/module-info"; cw.visit(Opcodes.V1_8, ACC_MODULE, name, null, null, null); - cw.visitAttribute(new ModuleAttribute(descriptor)); - cw.visitAttribute(new ConcealedPackagesAttribute(descriptor.conceals())); - - Optional oversion = descriptor.version(); - if (oversion.isPresent()) - cw.visitAttribute(new VersionAttribute(oversion.get())); - - Optional omain = descriptor.mainClass(); - if (omain.isPresent()) - cw.visitAttribute(new MainClassAttribute(omain.get())); + cw.visitAttribute(new ModuleAttribute(md)); + cw.visitAttribute(new ConcealedPackagesAttribute(md.conceals())); + md.version().ifPresent(v -> cw.visitAttribute(new VersionAttribute(v))); + md.mainClass().ifPresent(mc -> cw.visitAttribute(new MainClassAttribute(mc))); // write the TargetPlatform attribute if have any of OS name/arch/version - String osName = descriptor.osName().orElse(null); - String osArch = descriptor.osArch().orElse(null); - String osVersion = descriptor.osVersion().orElse(null); + String osName = md.osName().orElse(null); + String osArch = md.osArch().orElse(null); + String osVersion = md.osVersion().orElse(null); if (osName != null || osArch != null || osVersion != null) { cw.visitAttribute(new TargetPlatformAttribute(osName, osArch, diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java Tue May 03 12:25:20 2016 -0700 @@ -91,56 +91,29 @@ Map> map = new HashMap<>(); while (value != null) { - int pos = value.indexOf('='); + + // =(:)* - if (pos == -1 && index > 0) + int pos = value.indexOf('='); + if (pos == -1) throwIAE("Unable to parse: " + value); - if (pos == 0) throwIAE("Missing module name: " + value); - if (pos > 0) { - - // new format: =(:)* - - String mn = value.substring(0, pos); - List list = map.get(mn); - if (list != null) - throwIAE("Module " + mn + " specified more than once"); - list = new ArrayList<>(); - map.put(mn, list); - - String paths = value.substring(pos+1); - for (String path : paths.split(File.pathSeparator)) { - if (!path.isEmpty()) { - list.add(Paths.get(path)); - } - } - - } else { + String mn = value.substring(0, pos); + List list = map.get(mn); + if (list != null) + throwIAE("Module " + mn + " specified more than once"); + list = new ArrayList<>(); + map.put(mn, list); - // old format: (:)* - - assert index == 0; // old format only allowed in first -Xpatch - - String[] dirs = value.split(File.pathSeparator); - for (String d : dirs) { - if (d.length() > 0) { - Path top = Paths.get(d); - try { - Files.list(top).forEach(e -> { - String mn = e.getFileName().toString(); - Path dir = top.resolve(mn); - map.computeIfAbsent(mn, k -> new ArrayList<>()) - .add(dir); - }); - } catch (IOException ignore) { } - } + String paths = value.substring(pos+1); + for (String path : paths.split(File.pathSeparator)) { + if (!path.isEmpty()) { + list.add(Paths.get(path)); } - } - index++; value = System.getProperty(PATCH_PROPERTY_PREFIX + index); } @@ -175,7 +148,8 @@ for (Path file : paths) { if (Files.isRegularFile(file)) { - // JAR file + // JAR file - do not open as a multi-release JAR as this + // is not supported by the boot class loader try (JarFile jf = new JarFile(file.toFile())) { jf.stream() .filter(e -> e.getName().endsWith(".class")) @@ -209,10 +183,11 @@ descriptor = JLMA.newModuleDescriptor(descriptor, packages); } - // return a new module reference + // return a module reference to the patched module URI location = mref.location().orElse(null); - return new ModuleReference(descriptor, location, - () -> new PatchedModuleReader(paths, mref)); + return JLMA.newPatchedModule(descriptor, + location, + () -> new PatchedModuleReader(paths, mref)); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/module/Modules.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/Modules.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/Modules.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -58,7 +58,7 @@ * Creates a new Module. The module has the given ModuleDescriptor and * is defined to the given class loader. * - * The resulting Module is in a larva state in that it does not not read + * The resulting Module is in a larval state in that it does not not read * any other module and does not have any exports. * * The URI is for information purposes only. @@ -74,7 +74,7 @@ * Define a new module to the VM. The module has the given set of * concealed packages and is defined to the given class loader. * - * The resulting Module is in a larva state in that it does not not read + * The resulting Module is in a larval state in that it does not not read * any other module and does not have any exports. */ public static Module defineModule(ClassLoader loader, @@ -96,6 +96,13 @@ } /** + * Update module {@code m} to read all unnamed modules. + */ + public static void addReadsAllUnnamed(Module m) { + JLRMA.addReadsAllUnnamed(m); + } + + /** * Updates module m1 to export a package to module m2. * Same as m1.addExports(pkg, m2) but without a caller check. */ diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/module/ServicesCatalog.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/ServicesCatalog.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ServicesCatalog.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -29,94 +29,105 @@ import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleDescriptor.Provides; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Objects; import java.util.Set; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.ConcurrentHashMap; /** - * A services catalog. Each {@code ClassLoader} has an optional {@code - * ServicesCatalog} for modules that provide services. This is to support - * ClassLoader centric ServiceLoader.load methods. + * A services catalog. Each {@code ClassLoader} and {@code Layer} has + * an optional {@code ServicesCatalog} for modules that provide services. + * + * @see java.util.ServiceLoader */ -public class ServicesCatalog { - - // use RW locks as register is rare - private final ReadWriteLock lock = new ReentrantReadWriteLock(); - private final Lock readLock = lock.readLock(); - private final Lock writeLock = lock.writeLock(); +public interface ServicesCatalog { /** * Represents a service provider in the services catalog. */ - public class ServiceProvider { + public final class ServiceProvider { private final Module module; private final String providerName; - ServiceProvider(Module module, String providerName) { + + public ServiceProvider(Module module, String providerName) { this.module = module; this.providerName = providerName; } + public Module module() { return module; } + public String providerName() { return providerName; } - } - // service providers - private final Map> loaderServices = new HashMap<>(); - - /** - * Creates a new module catalog. - */ - public ServicesCatalog() { } - - /** - * Registers the module in this module catalog. - */ - public void register(Module m) { - ModuleDescriptor descriptor = m.getDescriptor(); + @Override + public int hashCode() { + return Objects.hash(module, providerName); + } - writeLock.lock(); - try { - // extend the services map - for (Provides ps : descriptor.provides().values()) { - String service = ps.service(); - Set providerNames = ps.providers(); - - // create a new set to replace the existing - Set result = new HashSet<>(); - Set providers = loaderServices.get(service); - if (providers != null) { - result.addAll(providers); - } - for (String pn : providerNames) { - result.add(new ServiceProvider(m, pn)); - } - loaderServices.put(service, Collections.unmodifiableSet(result)); - } - - } finally { - writeLock.unlock(); + @Override + public boolean equals(Object ob) { + if (!(ob instanceof ServiceProvider)) + return false; + ServiceProvider that = (ServiceProvider)ob; + return Objects.equals(this.module, that.module) + && Objects.equals(this.providerName, that.providerName); } } /** + * Registers the providers in the given module in this services catalog. + * + * @throws UnsupportedOperationException + * If this services catalog is immutable + */ + void register(Module module); + + /** * Returns the (possibly empty) set of service providers that implement the * given service type. - * - * @see java.util.ServiceLoader + */ + Set findServices(String service); + + /** + * Creates a ServicesCatalog that supports concurrent registration and + * and lookup. */ - public Set findServices(String service) { - readLock.lock(); - try { - return loaderServices.getOrDefault(service, Collections.emptySet()); - } finally { - readLock.unlock(); - } + static ServicesCatalog create() { + return new ServicesCatalog() { + + private Map> map = new ConcurrentHashMap<>(); + + @Override + public void register(Module m) { + ModuleDescriptor descriptor = m.getDescriptor(); + + for (Provides provides : descriptor.provides().values()) { + String service = provides.service(); + Set providerNames = provides.providers(); + + // create a new set to replace the existing + Set result = new HashSet<>(); + Set providers = map.get(service); + if (providers != null) { + result.addAll(providers); + } + for (String pn : providerNames) { + result.add(new ServiceProvider(m, pn)); + } + map.put(service, Collections.unmodifiableSet(result)); + } + + } + + @Override + public Set findServices(String service) { + return map.getOrDefault(service, Collections.emptySet()); + } + + }; } -} +} \ No newline at end of file diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/module/SystemModules.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/SystemModules.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/SystemModules.java Tue May 03 12:25:20 2016 -0700 @@ -40,21 +40,26 @@ */ public final class SystemModules { /** - * Name of the installed modules. + * Name of the system modules. * - * This array provides a way for InstalledModuleFinder to fallback + * This array provides a way for SystemModuleFinder to fallback * and read module-info.class from the run-time image instead of * the fastpath. */ public static final String[] MODULE_NAMES = new String[1]; /** + * Hash of system modules. + */ + public static String[] MODULES_TO_HASH = new String[1]; + + /** * Number of packages in the boot layer from the installed modules. * * Don't make it final to avoid inlining during compile time as * the value will be changed at jlink time. */ - public static final int PACKAGES_IN_BOOT_LAYER = 1024; + public static int PACKAGES_IN_BOOT_LAYER = 1024; /** * Returns a non-empty array of ModuleDescriptors in the run-time image. @@ -64,4 +69,5 @@ public static ModuleDescriptor[] modules() { return new ModuleDescriptor[0]; } -} \ No newline at end of file + +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/reflect/MethodAccessorGenerator.java --- a/jdk/src/java.base/share/classes/jdk/internal/reflect/MethodAccessorGenerator.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/reflect/MethodAccessorGenerator.java Tue May 03 12:25:20 2016 -0700 @@ -392,6 +392,7 @@ // matter. return AccessController.doPrivileged( new PrivilegedAction() { + @SuppressWarnings("deprecation") // Class.newInstance public MagicAccessorImpl run() { try { return (MagicAccessorImpl) diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/reflect/Reflection.java --- a/jdk/src/java.base/share/classes/jdk/internal/reflect/Reflection.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/reflect/Reflection.java Tue May 03 12:25:20 2016 -0700 @@ -343,8 +343,8 @@ private static void printStackTraceIfNeeded(Throwable e) { if (!printStackWhenAccessFailsSet && VM.initLevel() >= 1) { - String s = GetPropertyAction - .getProperty("sun.reflect.debugModuleAccessChecks"); + String s = GetPropertyAction.privilegedGetProperty( + "sun.reflect.debugModuleAccessChecks"); printStackWhenAccessFails = (s != null && !s.equalsIgnoreCase("false")); printStackWhenAccessFailsSet = true; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java --- a/jdk/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java Tue May 03 12:25:20 2016 -0700 @@ -398,7 +398,7 @@ return; } - Properties props = GetPropertyAction.getProperties(); + Properties props = GetPropertyAction.privilegedGetProperties(); String val = props.getProperty("sun.reflect.noInflation"); if (val != null && val.equals("true")) { noInflation = true; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/module-info.java --- a/jdk/src/java.base/share/classes/module-info.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/module-info.java Tue May 03 12:25:20 2016 -0700 @@ -166,6 +166,8 @@ java.sql, java.xml, jdk.charsets, + jdk.jartool, + jdk.jlink, jdk.net, jdk.scripting.nashorn, jdk.unsupported, diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/launcher/resources/launcher.properties --- a/jdk/src/java.base/share/classes/sun/launcher/resources/launcher.properties Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/launcher/resources/launcher.properties Tue May 03 12:25:20 2016 -0700 @@ -27,7 +27,7 @@ java.launcher.opt.header = Usage: {0} [options] class [args...]\n\ \ (to execute a class)\n or {0} [options] -jar jarfile [args...]\n\ \ (to execute a jar file)\n\ -\ or {0} [-options] -mp -m | /\n\ +\ or {0} [options] -mp -m [/] [args...]\n\ \ (to execute the main class in a module)\n\ where options include:\n @@ -51,8 +51,9 @@ \ A {0} separated list of directories, each directory\n\ \ is a directory of modules that replace upgradeable\n\ \ modules in the runtime image\n\ -\ -m | /\n\ -\ the initial or main module to resolve\n\ +\ -m [/]\n\ +\ the initial module to resolve, and the name of the main class\n\ +\ to execute if not specified by the module\n\ \ -addmods [,...]\n\ \ root modules to resolve in addition to the initial module\n\ \ -limitmods [,...]\n\ diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/net/ResourceManager.java --- a/jdk/src/java.base/share/classes/sun/net/ResourceManager.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/net/ResourceManager.java Tue May 03 12:25:20 2016 -0700 @@ -53,8 +53,8 @@ private static final AtomicInteger numSockets; static { - String prop = - GetPropertyAction.getProperty("sun.net.maxDatagramSockets"); + String prop = GetPropertyAction + .privilegedGetProperty("sun.net.maxDatagramSockets"); int defmax = DEFAULT_MAX_SOCKETS; try { if (prop != null) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/net/ftp/FtpClientProvider.java --- a/jdk/src/java.base/share/classes/sun/net/ftp/FtpClientProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/net/ftp/FtpClientProvider.java Tue May 03 12:25:20 2016 -0700 @@ -67,8 +67,9 @@ return false; } try { - Class c = Class.forName(cm, true, null); - provider = (FtpClientProvider) c.newInstance(); + @SuppressWarnings("deprecation") + Object o = Class.forName(cm, true, null).newInstance(); + provider = (FtpClientProvider)o; return true; } catch (ClassNotFoundException | IllegalAccessException | diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/net/sdp/SdpSupport.java --- a/jdk/src/java.base/share/classes/sun/net/sdp/SdpSupport.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/net/sdp/SdpSupport.java Tue May 03 12:25:20 2016 -0700 @@ -40,7 +40,7 @@ */ public final class SdpSupport { - private static final String os = GetPropertyAction.getProperty("os.name"); + private static final String os = GetPropertyAction.privilegedGetProperty("os.name"); private static final boolean isSupported = (os.equals("SunOS") || (os.equals("Linux"))); private static final JavaIOFileDescriptorAccess fdAccess = SharedSecrets.getJavaIOFileDescriptorAccess(); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/net/smtp/SmtpClient.java --- a/jdk/src/java.base/share/classes/sun/net/smtp/SmtpClient.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/net/smtp/SmtpClient.java Tue May 03 12:25:20 2016 -0700 @@ -157,7 +157,7 @@ } try { String s; - mailhost = GetPropertyAction.getProperty("mail.host"); + mailhost = GetPropertyAction.privilegedGetProperty("mail.host"); if (mailhost != null) { openServer(mailhost); return; @@ -183,7 +183,7 @@ setConnectTimeout(to); try { String s; - mailhost = GetPropertyAction.getProperty("mail.host"); + mailhost = GetPropertyAction.privilegedGetProperty("mail.host"); if (mailhost != null) { openServer(mailhost); return; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/net/www/MimeLauncher.java --- a/jdk/src/java.base/share/classes/sun/net/www/MimeLauncher.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/net/www/MimeLauncher.java Tue May 03 12:25:20 2016 -0700 @@ -183,7 +183,7 @@ } String execPathList; - execPathList = GetPropertyAction.getProperty("exec.path"); + execPathList = GetPropertyAction.privilegedGetProperty("exec.path"); if (execPathList == null) { // exec.path property not set return false; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/net/www/http/HttpClient.java --- a/jdk/src/java.base/share/classes/sun/net/www/http/HttpClient.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/net/www/http/HttpClient.java Tue May 03 12:25:20 2016 -0700 @@ -145,7 +145,7 @@ } static { - Properties props = GetPropertyAction.getProperties(); + Properties props = GetPropertyAction.privilegedGetProperties(); String keepAlive = props.getProperty("http.keepAlive"); String retryPost = props.getProperty("sun.net.http.retryPost"); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java --- a/jdk/src/java.base/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java Tue May 03 12:25:20 2016 -0700 @@ -278,7 +278,7 @@ if (user == null) { user = "anonymous"; - Properties props = GetPropertyAction.getProperties(); + Properties props = GetPropertyAction.privilegedGetProperties(); String vers = props.getProperty("java.version"); password = props.getProperty("ftp.protocol.user", "Java" + vers + "@"); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationHeader.java --- a/jdk/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationHeader.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationHeader.java Tue May 03 12:25:20 2016 -0700 @@ -94,7 +94,7 @@ } static { - authPref = GetPropertyAction.getProperty("http.auth.preference"); + authPref = GetPropertyAction.privilegedGetProperty("http.auth.preference"); // http.auth.preference can be set to SPNEGO or Kerberos. // In fact they means "Negotiate with SPNEGO" and "Negotiate with diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java --- a/jdk/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Tue May 03 12:25:20 2016 -0700 @@ -207,9 +207,9 @@ }; static { - Properties props = GetPropertyAction.getProperties(); - maxRedirects = GetIntegerAction.getProperty("http.maxRedirects", - defaultmaxRedirects); + Properties props = GetPropertyAction.privilegedGetProperties(); + maxRedirects = GetIntegerAction.privilegedGetProperty( + "http.maxRedirects", defaultmaxRedirects); version = props.getProperty("java.version"); String agent = props.getProperty("http.agent"); if (agent == null) { @@ -225,14 +225,14 @@ enableESBuffer = Boolean.parseBoolean( props.getProperty("sun.net.http.errorstream.enableBuffering")); - timeout4ESBuffer = GetIntegerAction - .getProperty("sun.net.http.errorstream.timeout", 300); + timeout4ESBuffer = GetIntegerAction.privilegedGetProperty( + "sun.net.http.errorstream.timeout", 300); if (timeout4ESBuffer <= 0) { timeout4ESBuffer = 300; // use the default } - bufSize4ES = GetIntegerAction - .getProperty("sun.net.http.errorstream.bufferSize", 4096); + bufSize4ES = GetIntegerAction.privilegedGetProperty( + "sun.net.http.errorstream.bufferSize", 4096); if (bufSize4ES <= 0) { bufSize4ES = 4096; // use the default } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java --- a/jdk/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java Tue May 03 12:25:20 2016 -0700 @@ -139,7 +139,7 @@ // String ciphers []; String cipherString = - GetPropertyAction.getProperty("https.cipherSuites"); + GetPropertyAction.privilegedGetProperty("https.cipherSuites"); if (cipherString == null || "".equals(cipherString)) { ciphers = null; @@ -163,7 +163,7 @@ // String protocols []; String protocolString = - GetPropertyAction.getProperty("https.protocols"); + GetPropertyAction.privilegedGetProperty("https.protocols"); if (protocolString == null || "".equals(protocolString)) { protocols = null; @@ -183,7 +183,8 @@ } private String getUserAgent() { - String userAgent = GetPropertyAction.getProperty("https.agent"); + String userAgent = + GetPropertyAction.privilegedGetProperty("https.agent"); if (userAgent == null || userAgent.length() == 0) { userAgent = "JSSE"; } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/net/www/protocol/jrt/JavaRuntimeURLConnection.java --- a/jdk/src/java.base/share/classes/sun/net/www/protocol/jrt/JavaRuntimeURLConnection.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/net/www/protocol/jrt/JavaRuntimeURLConnection.java Tue May 03 12:25:20 2016 -0700 @@ -161,7 +161,7 @@ public Permission getPermission() throws IOException { Permission p = permission; if (p == null) { - String home = GetPropertyAction.getProperty("java.home"); + String home = GetPropertyAction.privilegedGetProperty("java.home"); p = new FilePermission(home + File.separator + "-", "read"); permission = p; } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/net/www/protocol/netdoc/Handler.java --- a/jdk/src/java.base/share/classes/sun/net/www/protocol/netdoc/Handler.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/net/www/protocol/netdoc/Handler.java Tue May 03 12:25:20 2016 -0700 @@ -56,9 +56,9 @@ URL ru; boolean localonly = Boolean.parseBoolean( - GetPropertyAction.getProperty("newdoc.localonly")); + GetPropertyAction.privilegedGetProperty("newdoc.localonly")); - String docurl = GetPropertyAction.getProperty("doc.url"); + String docurl = GetPropertyAction.privilegedGetProperty("doc.url"); String file = u.getFile(); if (!localonly) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java --- a/jdk/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java Tue May 03 12:25:20 2016 -0700 @@ -1019,7 +1019,7 @@ if (!propertyChecked) { synchronized (FileChannelImpl.class) { if (!propertyChecked) { - String value = GetPropertyAction.getProperty( + String value = GetPropertyAction.privilegedGetProperty( "sun.nio.ch.disableSystemWideOverlappingFileLockCheck"); isSharedFileLockTable = ((value == null) || value.equals("false")); propertyChecked = true; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/nio/ch/Net.java --- a/jdk/src/java.base/share/classes/sun/nio/ch/Net.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/ch/Net.java Tue May 03 12:25:20 2016 -0700 @@ -374,8 +374,8 @@ } public static boolean isFastTcpLoopbackRequested() { - String loopbackProp = - GetPropertyAction.getProperty("jdk.net.useFastTcpLoopback"); + String loopbackProp = GetPropertyAction + .privilegedGetProperty("jdk.net.useFastTcpLoopback"); boolean enable; if ("".equals(loopbackProp)) { enable = true; @@ -633,8 +633,8 @@ static { int availLevel = isExclusiveBindAvailable(); if (availLevel >= 0) { - String exclBindProp = - GetPropertyAction.getProperty("sun.net.useExclusiveBind"); + String exclBindProp = GetPropertyAction + .privilegedGetProperty("sun.net.useExclusiveBind"); if (exclBindProp != null) { exclusiveBind = exclBindProp.isEmpty() ? true : Boolean.parseBoolean(exclBindProp); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/nio/ch/ThreadPool.java --- a/jdk/src/java.base/share/classes/sun/nio/ch/ThreadPool.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/ch/ThreadPool.java Tue May 03 12:25:20 2016 -0700 @@ -165,14 +165,11 @@ GetPropertyAction(DEFAULT_THREAD_POOL_THREAD_FACTORY)); if (propValue != null) { try { - Class c = Class - .forName(propValue, true, ClassLoader.getSystemClassLoader()); - return ((ThreadFactory)c.newInstance()); - } catch (ClassNotFoundException x) { - throw new Error(x); - } catch (InstantiationException x) { - throw new Error(x); - } catch (IllegalAccessException x) { + @SuppressWarnings("deprecation") + Object tmp = Class + .forName(propValue, true, ClassLoader.getSystemClassLoader()).newInstance(); + return (ThreadFactory)tmp; + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException x) { throw new Error(x); } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/nio/ch/Util.java --- a/jdk/src/java.base/share/classes/sun/nio/ch/Util.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java Tue May 03 12:25:20 2016 -0700 @@ -64,7 +64,8 @@ * for potential future-proofing. */ private static long getMaxCachedBufferSize() { - String s = GetPropertyAction.getProperty("jdk.nio.maxCachedBufferSize"); + String s = GetPropertyAction + .privilegedGetProperty("jdk.nio.maxCachedBufferSize"); if (s != null) { try { long m = Long.parseLong(s); @@ -465,7 +466,8 @@ if (bugLevel == null) { if (!jdk.internal.misc.VM.isBooted()) return false; - String value = GetPropertyAction.getProperty("sun.nio.ch.bugLevel"); + String value = GetPropertyAction + .privilegedGetProperty("sun.nio.ch.bugLevel"); bugLevel = (value != null) ? value : ""; } return bugLevel.equals(bl); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/nio/cs/FastCharsetProvider.java --- a/jdk/src/java.base/share/classes/sun/nio/cs/FastCharsetProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/FastCharsetProvider.java Tue May 03 12:25:20 2016 -0700 @@ -115,10 +115,11 @@ // Instantiate the charset and cache it try { - Class c = Class.forName(packagePrefix + "." + cln, + @SuppressWarnings("deprecation") + Object o= Class.forName(packagePrefix + "." + cln, true, - this.getClass().getClassLoader()); - cs = (Charset)c.newInstance(); + this.getClass().getClassLoader()).newInstance(); + cs = (Charset)o; cache.put(csn, cs); return cs; } catch (ClassNotFoundException | diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/nio/cs/StandardCharsets.java.template --- a/jdk/src/java.base/share/classes/sun/nio/cs/StandardCharsets.java.template Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/StandardCharsets.java.template Tue May 03 12:25:20 2016 -0700 @@ -110,10 +110,11 @@ // Instantiate the charset and cache it try { - Class c = Class.forName(packagePrefix + "." + cln, - true, - this.getClass().getClassLoader()); - cs = (Charset)c.newInstance(); + @SuppressWarnings("deprecation") + Object o = Class.forName(packagePrefix + "." + cln, + true, + this.getClass().getClassLoader()).newInstance(); + cs = (Charset)o; cache.put(csn, cs); return cs; } catch (ClassNotFoundException | @@ -164,7 +165,7 @@ return; initialized = true; - String map = getProperty("sun.nio.cs.map"); + String map = GetPropertyAction.privilegedGetProperty("sun.nio.cs.map"); if (map != null) { String[] maps = map.split(","); for (int i = 0; i < maps.length; i++) { @@ -199,9 +200,4 @@ } } - private static String getProperty(String key) { - return GetPropertyAction.getProperty(key); - } - - } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/nio/fs/Util.java --- a/jdk/src/java.base/share/classes/sun/nio/fs/Util.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/nio/fs/Util.java Tue May 03 12:25:20 2016 -0700 @@ -38,7 +38,7 @@ private Util() { } private static final Charset jnuEncoding = Charset.forName( - GetPropertyAction.getProperty("sun.jnu.encoding")); + GetPropertyAction.privilegedGetProperty("sun.jnu.encoding")); /** * Returns {@code Charset} corresponding to the sun.jnu.encoding property diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/action/GetIntegerAction.java --- a/jdk/src/java.base/share/classes/sun/security/action/GetIntegerAction.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/action/GetIntegerAction.java Tue May 03 12:25:20 2016 -0700 @@ -118,9 +118,14 @@ * if no security manager is present. This is unsafe for inclusion in a * public API but allowable here since this class is now encapsulated. * + * Note that this method performs a privileged action using caller-provided + * inputs. The caller of this method should take care to ensure that the + * inputs are not tainted and the returned property is not made accessible + * to untrusted code if it contains sensitive information. + * * @param theProp the name of the system property. */ - public static Integer getProperty(String theProp) { + public static Integer privilegedGetProperty(String theProp) { if (System.getSecurityManager() == null) { return Integer.getInteger(theProp); } else { @@ -134,10 +139,16 @@ * if no security manager is present. This is unsafe for inclusion in a * public API but allowable here since this class is now encapsulated. * + * Note that this method performs a privileged action using caller-provided + * inputs. The caller of this method should take care to ensure that the + * inputs are not tainted and the returned property is not made accessible + * to untrusted code if it contains sensitive information. + * * @param theProp the name of the system property. * @param defaultVal the default value. */ - public static Integer getProperty(String theProp, int defaultVal) { + public static Integer privilegedGetProperty(String theProp, + int defaultVal) { Integer value; if (System.getSecurityManager() == null) { value = Integer.getInteger(theProp); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/action/GetPropertyAction.java --- a/jdk/src/java.base/share/classes/sun/security/action/GetPropertyAction.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/action/GetPropertyAction.java Tue May 03 12:25:20 2016 -0700 @@ -93,9 +93,14 @@ * if no security manager is present. This is unsafe for inclusion in a * public API but allowable here since this class is now encapsulated. * + * Note that this method performs a privileged action using caller-provided + * inputs. The caller of this method should take care to ensure that the + * inputs are not tainted and the returned property is not made accessible + * to untrusted code if it contains sensitive information. + * * @param theProp the name of the system property. */ - public static String getProperty(String theProp) { + public static String privilegedGetProperty(String theProp) { if (System.getSecurityManager() == null) { return System.getProperty(theProp); } else { @@ -109,10 +114,16 @@ * if no security manager is present. This is unsafe for inclusion in a * public API but allowable here since this class is now encapsulated. * + * Note that this method performs a privileged action using caller-provided + * inputs. The caller of this method should take care to ensure that the + * inputs are not tainted and the returned property is not made accessible + * to untrusted code if it contains sensitive information. + * * @param theProp the name of the system property. * @param defaultVal the default value. */ - public static String getProperty(String theProp, String defaultVal) { + public static String privilegedGetProperty(String theProp, + String defaultVal) { if (System.getSecurityManager() == null) { return System.getProperty(theProp, defaultVal); } else { @@ -126,8 +137,13 @@ * having to go through doPrivileged if no security manager is present. * This is unsafe for inclusion in a public API but allowable here since * this class is now encapsulated. + * + * Note that this method performs a privileged action, and callers of + * this method should take care to ensure that the returned properties + * are not made accessible to untrusted code since it may contain + * sensitive information. */ - public static Properties getProperties() { + public static Properties privilegedGetProperties() { if (System.getSecurityManager() == null) { return System.getProperties(); } else { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/jca/ProviderConfig.java --- a/jdk/src/java.base/share/classes/sun/security/jca/ProviderConfig.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/jca/ProviderConfig.java Tue May 03 12:25:20 2016 -0700 @@ -185,7 +185,9 @@ try { Class c = Class.forName("apple.security.AppleProvider"); if (Provider.class.isAssignableFrom(c)) { - return (Provider) c.newInstance(); + @SuppressWarnings("deprecation") + Object tmp = c.newInstance(); + return (Provider) tmp; } else { return null; } @@ -386,6 +388,7 @@ Provider p = AccessController.doPrivileged (new PrivilegedExceptionAction() { + @SuppressWarnings("deprecation") // Class.newInstance public Provider run() throws Exception { return (Provider) provClass.newInstance(); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java --- a/jdk/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java Tue May 03 12:25:20 2016 -0700 @@ -218,11 +218,10 @@ } } - Object inst = null; + @SuppressWarnings("deprecation") + Object inst = (keyClass != null) ? keyClass.newInstance() : null; PKCS8Key result; - if (keyClass != null) - inst = keyClass.newInstance(); if (inst instanceof PKCS8Key) { result = (PKCS8Key) inst; result.algid = algid; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/provider/DSAKeyFactory.java --- a/jdk/src/java.base/share/classes/sun/security/provider/DSAKeyFactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/provider/DSAKeyFactory.java Tue May 03 12:25:20 2016 -0700 @@ -70,7 +70,7 @@ * By default this is false. * This incompatibility was introduced by 4532506. */ - String prop = GetPropertyAction.getProperty(SERIAL_PROP); + String prop = GetPropertyAction.privilegedGetProperty(SERIAL_PROP); SERIAL_INTEROP = "true".equalsIgnoreCase(prop); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java --- a/jdk/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -31,12 +31,10 @@ import java.util.Collections; import java.util.Set; import java.util.EnumSet; -import java.util.HashSet; import java.math.BigInteger; import java.security.PublicKey; import java.security.KeyFactory; import java.security.AlgorithmParameters; -import java.security.NoSuchAlgorithmException; import java.security.GeneralSecurityException; import java.security.cert.Certificate; import java.security.cert.X509CRL; @@ -48,10 +46,13 @@ import java.security.cert.CertPathValidatorException; import java.security.cert.CertPathValidatorException.BasicReason; import java.security.cert.PKIXReason; -import java.io.IOException; -import java.security.interfaces.*; -import java.security.spec.*; +import java.security.interfaces.DSAParams; +import java.security.interfaces.DSAPublicKey; +import java.security.spec.DSAPublicKeySpec; +import sun.security.util.AnchorCertificates; +import sun.security.util.CertConstraintParameters; +import sun.security.util.Debug; import sun.security.util.DisabledAlgorithmConstraints; import sun.security.x509.X509CertImpl; import sun.security.x509.X509CRLImpl; @@ -69,6 +70,7 @@ * @see PKIXParameters */ public final class AlgorithmChecker extends PKIXCertPathChecker { + private static final Debug debug = Debug.getInstance("certpath"); private final AlgorithmConstraints constraints; private final PublicKey trustedPubKey; @@ -88,6 +90,14 @@ certPathDefaultConstraints = new DisabledAlgorithmConstraints( DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS); + // If there is no "cacerts" keyword, then disable anchor checking + private static final boolean publicCALimits = + certPathDefaultConstraints.checkProperty("jdkCA"); + + // If anchor checking enabled, this will be true if the trust anchor + // has a match in the cacerts file + private boolean trustedMatch = false; + /** * Create a new AlgorithmChecker with the algorithm * constraints specified in security property @@ -136,6 +146,11 @@ if (anchor.getTrustedCert() != null) { this.trustedPubKey = anchor.getTrustedCert().getPublicKey(); + // Check for anchor certificate restrictions + trustedMatch = checkFingerprint(anchor.getTrustedCert()); + if (trustedMatch && debug != null) { + debug.println("trustedMatch = true"); + } } else { this.trustedPubKey = anchor.getCAPublicKey(); } @@ -144,6 +159,19 @@ this.constraints = constraints; } + // Check this 'cert' for restrictions in the AnchorCertificates + // trusted certificates list + private static boolean checkFingerprint(X509Certificate cert) { + if (!publicCALimits) { + return false; + } + + if (debug != null) { + debug.println("AlgorithmChecker.contains: " + cert.getSigAlgName()); + } + return AnchorCertificates.contains(cert); + } + @Override public void init(boolean forward) throws CertPathValidatorException { // Note that this class does not support forward mode. @@ -181,36 +209,8 @@ return; } - X509CertImpl x509Cert = null; - try { - x509Cert = X509CertImpl.toImpl((X509Certificate)cert); - } catch (CertificateException ce) { - throw new CertPathValidatorException(ce); - } - - PublicKey currPubKey = x509Cert.getPublicKey(); - String currSigAlg = x509Cert.getSigAlgName(); - - AlgorithmId algorithmId = null; - try { - algorithmId = (AlgorithmId)x509Cert.get(X509CertImpl.SIG_ALG); - } catch (CertificateException ce) { - throw new CertPathValidatorException(ce); - } - - AlgorithmParameters currSigAlgParams = algorithmId.getParameters(); - - // Check the current signature algorithm - if (!constraints.permits( - SIGNATURE_PRIMITIVE_SET, - currSigAlg, currSigAlgParams)) { - throw new CertPathValidatorException( - "Algorithm constraints check failed: " + currSigAlg, - null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); - } - // check the key usage and key size - boolean[] keyUsage = x509Cert.getKeyUsage(); + boolean[] keyUsage = ((X509Certificate) cert).getKeyUsage(); if (keyUsage != null && keyUsage.length < 9) { throw new CertPathValidatorException( "incorrect KeyUsage extension", @@ -248,27 +248,67 @@ if (primitives.isEmpty()) { throw new CertPathValidatorException( - "incorrect KeyUsage extension", + "incorrect KeyUsage extension bits", null, null, -1, PKIXReason.INVALID_KEY_USAGE); } } - if (!constraints.permits(primitives, currPubKey)) { - throw new CertPathValidatorException( - "algorithm constraints check failed", - null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); + PublicKey currPubKey = cert.getPublicKey(); + + // Check against DisabledAlgorithmConstraints certpath constraints. + // permits() will throw exception on failure. + certPathDefaultConstraints.permits(primitives, + new CertConstraintParameters((X509Certificate)cert, + trustedMatch)); + // new CertConstraintParameters(x509Cert, trustedMatch)); + // If there is no previous key, set one and exit + if (prevPubKey == null) { + prevPubKey = currPubKey; + return; + } + + X509CertImpl x509Cert; + AlgorithmId algorithmId; + try { + x509Cert = X509CertImpl.toImpl((X509Certificate)cert); + algorithmId = (AlgorithmId)x509Cert.get(X509CertImpl.SIG_ALG); + } catch (CertificateException ce) { + throw new CertPathValidatorException(ce); + } + + AlgorithmParameters currSigAlgParams = algorithmId.getParameters(); + String currSigAlg = x509Cert.getSigAlgName(); + + // If 'constraints' is not of DisabledAlgorithmConstraints, check all + // everything individually + if (!(constraints instanceof DisabledAlgorithmConstraints)) { + // Check the current signature algorithm + if (!constraints.permits( + SIGNATURE_PRIMITIVE_SET, + currSigAlg, currSigAlgParams)) { + throw new CertPathValidatorException( + "Algorithm constraints check failed on signature " + + "algorithm: " + currSigAlg, null, null, -1, + BasicReason.ALGORITHM_CONSTRAINED); + } + + if (!constraints.permits(primitives, currPubKey)) { + throw new CertPathValidatorException( + "Algorithm constraints check failed on keysize: " + + sun.security.util.KeyUtil.getKeySize(currPubKey), + null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); + } } // Check with previous cert for signature algorithm and public key if (prevPubKey != null) { - if (currSigAlg != null) { - if (!constraints.permits( - SIGNATURE_PRIMITIVE_SET, - currSigAlg, prevPubKey, currSigAlgParams)) { - throw new CertPathValidatorException( - "Algorithm constraints check failed: " + currSigAlg, - null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); - } + if (!constraints.permits( + SIGNATURE_PRIMITIVE_SET, + currSigAlg, prevPubKey, currSigAlgParams)) { + throw new CertPathValidatorException( + "Algorithm constraints check failed on " + + "signature algorithm: " + currSigAlg, + null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); } // Inherit key parameters from previous key @@ -282,7 +322,7 @@ DSAParams params = ((DSAPublicKey)prevPubKey).getParams(); if (params == null) { throw new CertPathValidatorException( - "Key parameters missing"); + "Key parameters missing from public key."); } try { @@ -330,6 +370,11 @@ // Don't bother to change the trustedPubKey. if (anchor.getTrustedCert() != null) { prevPubKey = anchor.getTrustedCert().getPublicKey(); + // Check for anchor certificate restrictions + trustedMatch = checkFingerprint(anchor.getTrustedCert()); + if (trustedMatch && debug != null) { + debug.println("trustedMatch = true"); + } } else { prevPubKey = anchor.getCAPublicKey(); } @@ -370,7 +415,8 @@ if (!certPathDefaultConstraints.permits( SIGNATURE_PRIMITIVE_SET, sigAlgName, key, sigAlgParams)) { throw new CertPathValidatorException( - "algorithm check failed: " + sigAlgName + " is disabled", + "Algorithm constraints check failed on signature algorithm: " + + sigAlgName + " is disabled", null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/provider/certpath/PKIXMasterCertPathValidator.java --- a/jdk/src/java.base/share/classes/sun/security/provider/certpath/PKIXMasterCertPathValidator.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/provider/certpath/PKIXMasterCertPathValidator.java Tue May 03 12:25:20 2016 -0700 @@ -131,8 +131,8 @@ } catch (CertPathValidatorException cpve) { throw new CertPathValidatorException(cpve.getMessage(), - cpve.getCause(), cpOriginal, cpSize - (i + 1), - cpve.getReason()); + (cpve.getCause() != null) ? cpve.getCause() : cpve, + cpOriginal, cpSize - (i + 1), cpve.getReason()); } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/provider/certpath/RevocationChecker.java --- a/jdk/src/java.base/share/classes/sun/security/provider/certpath/RevocationChecker.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/provider/certpath/RevocationChecker.java Tue May 03 12:25:20 2016 -0700 @@ -43,7 +43,6 @@ import static sun.security.provider.certpath.OCSP.*; import static sun.security.provider.certpath.PKIX.*; -import sun.security.action.GetPropertyAction; import sun.security.x509.*; import static sun.security.x509.PKIXExtensions.*; import sun.security.util.Debug; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java --- a/jdk/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java Tue May 03 12:25:20 2016 -0700 @@ -84,7 +84,7 @@ public static final int MAX_RESTRICTED_EXPLEN = 64; private static final boolean restrictExpLen = - "true".equalsIgnoreCase(GetPropertyAction.getProperty( + "true".equalsIgnoreCase(GetPropertyAction.privilegedGetProperty( "sun.security.rsa.restrictRSAExponent", "true")); // instance used for static translateKey(); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/ssl/ClientKeyExchangeService.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/ClientKeyExchangeService.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientKeyExchangeService.java Tue May 03 12:25:20 2016 -0700 @@ -50,7 +50,7 @@ providers = new HashMap<>(); static { - String path = GetPropertyAction.getProperty("java.home"); + String path = GetPropertyAction.privilegedGetProperty("java.home"); ServiceLoader sc = AccessController.doPrivileged( (PrivilegedAction>) diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/ssl/Debug.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/Debug.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/Debug.java Tue May 03 12:25:20 2016 -0700 @@ -45,7 +45,7 @@ private static String args; static { - args = GetPropertyAction.getProperty("javax.net.debug", ""); + args = GetPropertyAction.privilegedGetProperty("javax.net.debug", ""); args = args.toLowerCase(Locale.ENGLISH); if (args.equals("help")) { Help(); @@ -178,11 +178,11 @@ /** * Return the value of the boolean System property propName. * - * Note use of doPrivileged(). Do make accessible to applications. + * Note use of privileged action. Do NOT make accessible to applications. */ static boolean getBooleanProperty(String propName, boolean defaultValue) { // if set, require value of either true or false - String b = GetPropertyAction.getProperty(propName); + String b = GetPropertyAction.privilegedGetProperty(propName); if (b == null) { return defaultValue; } else if (b.equalsIgnoreCase("false")) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java Tue May 03 12:25:20 2016 -0700 @@ -656,7 +656,8 @@ // the provider service. Instead, please handle the initialization // exception in the caller's constructor. static { - String property = GetPropertyAction.getProperty(PROPERTY_NAME); + String property = GetPropertyAction + .privilegedGetProperty(PROPERTY_NAME); if (property != null && property.length() != 0) { // remove double quote marks from beginning/end of the property if (property.length() > 1 && property.charAt(0) == '"' && diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java Tue May 03 12:25:20 2016 -0700 @@ -119,8 +119,8 @@ private long statusRespTimeout; static { - String property = - GetPropertyAction.getProperty("jdk.tls.ephemeralDHKeySize"); + String property = GetPropertyAction + .privilegedGetProperty("jdk.tls.ephemeralDHKeySize"); if (property == null || property.length() == 0) { useLegacyEphemeralDHKeys = false; useSmartEphemeralDHKeys = false; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java Tue May 03 12:25:20 2016 -0700 @@ -73,8 +73,8 @@ DEFAULT_CACHE_LIFETIME)); cacheLifetime = life > 0 ? life : 0; - String uriStr = - GetPropertyAction.getProperty("jdk.tls.stapling.responderURI"); + String uriStr = GetPropertyAction + .privilegedGetProperty("jdk.tls.stapling.responderURI"); URI tmpURI; try { tmpURI = ((uriStr != null && !uriStr.isEmpty()) ? diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/tools/keytool/Main.java --- a/jdk/src/java.base/share/classes/sun/security/tools/keytool/Main.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/tools/keytool/Main.java Tue May 03 12:25:20 2016 -0700 @@ -728,6 +728,7 @@ provClass = Class.forName(provName); } + @SuppressWarnings("deprecation") Object obj = provClass.newInstance(); if (!(obj instanceof Provider)) { MessageFormat form = new MessageFormat diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java --- a/jdk/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -29,7 +29,6 @@ import java.security.AlgorithmConstraints; import java.security.PrivilegedAction; import java.security.Security; -import java.util.Map; import java.util.Set; /** @@ -45,8 +44,7 @@ } // Get algorithm constraints from the specified security property. - private static void loadAlgorithmsMap(Map algorithmsMap, - String propertyName) { + static String[] getAlgorithms(String propertyName) { String property = AccessController.doPrivileged( (PrivilegedAction) () -> Security.getProperty( propertyName)); @@ -68,18 +66,7 @@ if (algorithmsInProperty == null) { algorithmsInProperty = new String[0]; } - algorithmsMap.put(propertyName, algorithmsInProperty); - } - - static String[] getAlgorithms(Map algorithmsMap, - String propertyName) { - synchronized (algorithmsMap) { - if (!algorithmsMap.containsKey(propertyName)) { - loadAlgorithmsMap(algorithmsMap, propertyName); - } - - return algorithmsMap.get(propertyName); - } + return algorithmsInProperty; } static boolean checkAlgorithm(String[] algorithms, String algorithm, diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java --- a/jdk/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -40,19 +40,7 @@ private static final Pattern pattern = Pattern.compile("with|and|(? - * For example, we need to decompose "SHA1WithRSA" into "SHA1" and "RSA" - * so that we can check the "SHA1" and "RSA" algorithm constraints - * separately. - *

- * Please override the method if need to support more name pattern. - */ - public Set decompose(String algorithm) { - if (algorithm == null || algorithm.length() == 0) { - return new HashSet<>(); - } + private static Set decomposeImpl(String algorithm) { // algorithm/mode/padding String[] transTockens = transPattern.split(algorithm); @@ -79,6 +67,24 @@ elements.add(token); } } + return elements; + } + + /** + * Decompose the standard algorithm name into sub-elements. + *

+ * For example, we need to decompose "SHA1WithRSA" into "SHA1" and "RSA" + * so that we can check the "SHA1" and "RSA" algorithm constraints + * separately. + *

+ * Please override the method if need to support more name pattern. + */ + public Set decompose(String algorithm) { + if (algorithm == null || algorithm.length() == 0) { + return new HashSet<>(); + } + + Set elements = decomposeImpl(algorithm); // In Java standard algorithm name specification, for different // purpose, the SHA-1 and SHA-2 algorithm names are different. For @@ -130,4 +136,40 @@ return elements; } + private static void hasLoop(Set elements, String find, String replace) { + if (elements.contains(find)) { + if (!elements.contains(replace)) { + elements.add(replace); + } + elements.remove(find); + } + } + + /* + * This decomposes a standard name into sub-elements with a consistent + * message digest algorithm name to avoid overly complicated checking. + */ + public static Set decomposeOneHash(String algorithm) { + if (algorithm == null || algorithm.length() == 0) { + return new HashSet<>(); + } + + Set elements = decomposeImpl(algorithm); + + hasLoop(elements, "SHA-1", "SHA1"); + hasLoop(elements, "SHA-224", "SHA224"); + hasLoop(elements, "SHA-256", "SHA256"); + hasLoop(elements, "SHA-384", "SHA384"); + hasLoop(elements, "SHA-512", "SHA512"); + + return elements; + } + + /* + * The provided message digest algorithm name will return a consistent + * naming scheme. + */ + public static String hashName(String algorithm) { + return algorithm.replace("-", ""); + } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/util/AnchorCertificates.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/util/AnchorCertificates.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,101 @@ +/* + * 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 sun.security.util; + +import java.io.File; +import java.io.FileInputStream; +import java.security.AccessController; +import java.security.KeyStore; +import java.security.PrivilegedAction; +import java.security.cert.X509Certificate; +import java.util.Enumeration; +import java.util.HashSet; + +import sun.security.x509.X509CertImpl; + +/** + * The purpose of this class is to determine the trust anchor certificates is in + * the cacerts file. This is used for PKIX CertPath checking. + */ +public class AnchorCertificates { + + private static final Debug debug = Debug.getInstance("certpath"); + private static final String HASH = "SHA-256"; + private static HashSet certs; + + static { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + File f = new File(System.getProperty("java.home"), + "lib/security/cacerts"); + KeyStore cacerts; + try { + cacerts = KeyStore.getInstance("JKS"); + try (FileInputStream fis = new FileInputStream(f)) { + cacerts.load(fis, "changeit".toCharArray()); + certs = new HashSet<>(); + Enumeration list = cacerts.aliases(); + String alias; + while (list.hasMoreElements()) { + alias = list.nextElement(); + // Check if this cert is labeled a trust anchor. + if (alias.contains(" [jdk")) { + X509Certificate cert = (X509Certificate) cacerts + .getCertificate(alias); + certs.add(X509CertImpl.getFingerprint(HASH, cert)); + } + } + } + } catch (Exception e) { + if (debug != null) { + debug.println("Error parsing cacerts"); + } + e.printStackTrace(); + } + return null; + } + }); + } + + /** + * Checks if a certificate is a trust anchor. + * + * @param cert the certificate to check + * @return true if the certificate is trusted. + */ + public static boolean contains(X509Certificate cert) { + String key = X509CertImpl.getFingerprint(HASH, cert); + boolean result = certs.contains(key); + if (result && debug != null) { + debug.println("AnchorCertificate.contains: matched " + + cert.getSubjectDN()); + } + return result; + } + + private AnchorCertificates() {} +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/util/CertConstraintParameters.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/util/CertConstraintParameters.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,59 @@ +/* + * 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 sun.security.util; + +import java.security.cert.X509Certificate; + +/** + * This class is a wrapper for keeping state and passing objects between PKIX, + * AlgorithmChecker, and DisabledAlgorithmConstraints. + */ +public class CertConstraintParameters { + // A certificate being passed to check against constraints. + private final X509Certificate cert; + + // This is true if the trust anchor in the certificate chain matches a cert + // in AnchorCertificates + private final boolean trustedMatch; + + public CertConstraintParameters(X509Certificate c, boolean match) { + cert = c; + trustedMatch = match; + } + + public CertConstraintParameters(X509Certificate c) { + this(c, false); + } + + // Returns if the trust anchor has a match if anchor checking is enabled. + public boolean isTrustedMatch() { + return trustedMatch; + } + + public X509Certificate getCertificate() { + return cert; + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/util/Debug.java --- a/jdk/src/java.base/share/classes/sun/security/util/Debug.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/util/Debug.java Tue May 03 12:25:20 2016 -0700 @@ -43,10 +43,10 @@ private static String args; static { - args = GetPropertyAction.getProperty("java.security.debug"); + args = GetPropertyAction.privilegedGetProperty("java.security.debug"); - String args2 = - GetPropertyAction.getProperty("java.security.auth.debug"); + String args2 = GetPropertyAction + .privilegedGetProperty("java.security.auth.debug"); if (args == null) { args = args2; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java --- a/jdk/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -28,12 +28,14 @@ import java.security.CryptoPrimitive; import java.security.AlgorithmParameters; import java.security.Key; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertPathValidatorException.BasicReason; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.HashSet; import java.util.Locale; +import java.util.Map; import java.util.Set; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.HashMap; import java.util.regex.Pattern; import java.util.regex.Matcher; @@ -44,6 +46,7 @@ * for the syntax of the disabled algorithm string. */ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints { + private static final Debug debug = Debug.getInstance("certpath"); // the known security property, jdk.certpath.disabledAlgorithms public static final String PROPERTY_CERTPATH_DISABLED_ALGS = @@ -53,13 +56,8 @@ public static final String PROPERTY_TLS_DISABLED_ALGS = "jdk.tls.disabledAlgorithms"; - private static final Map disabledAlgorithmsMap = - new HashMap<>(); - private static final Map keySizeConstraintsMap = - new HashMap<>(); - private final String[] disabledAlgorithms; - private final KeySizeConstraints keySizeConstraints; + private final Constraints algorithmConstraints; /** * Initialize algorithm constraints with the specified security property. @@ -74,11 +72,14 @@ public DisabledAlgorithmConstraints(String propertyName, AlgorithmDecomposer decomposer) { super(decomposer); - disabledAlgorithms = getAlgorithms(disabledAlgorithmsMap, propertyName); - keySizeConstraints = getKeySizeConstraints(disabledAlgorithms, - propertyName); + disabledAlgorithms = getAlgorithms(propertyName); + algorithmConstraints = new Constraints(disabledAlgorithms); } + /* + * This only checks if the algorithm has been completely disabled. If + * there are keysize or other limit, this method allow the algorithm. + */ @Override public final boolean permits(Set primitives, String algorithm, AlgorithmParameters parameters) { @@ -91,11 +92,19 @@ return checkAlgorithm(disabledAlgorithms, algorithm, decomposer); } + /* + * Checks if the key algorithm has been disabled or constraints have been + * placed on the key. + */ @Override public final boolean permits(Set primitives, Key key) { return checkConstraints(primitives, "", key, null); } + /* + * Checks if the key algorithm has been disabled or if constraints have + * been placed on the key. + */ @Override public final boolean permits(Set primitives, String algorithm, Key key, AlgorithmParameters parameters) { @@ -107,7 +116,39 @@ return checkConstraints(primitives, algorithm, key, parameters); } - // Check algorithm constraints + /* + * Check if a x509Certificate object is permitted. Check if all + * algorithms are allowed, certificate constraints, and the + * public key against key constraints. + * + * Uses new style permit() which throws exceptions. + */ + public final void permits(Set primitives, + CertConstraintParameters cp) throws CertPathValidatorException { + checkConstraints(primitives, cp); + } + + /* + * Check if Certificate object is within the constraints. + * Uses new style permit() which throws exceptions. + */ + public final void permits(Set primitives, + X509Certificate cert) throws CertPathValidatorException { + checkConstraints(primitives, new CertConstraintParameters(cert)); + } + + // Check if a string is contained inside the property + public boolean checkProperty(String param) { + param = param.toLowerCase(Locale.ENGLISH); + for (String block : disabledAlgorithms) { + if (block.toLowerCase(Locale.ENGLISH).indexOf(param) >= 0) { + return true; + } + } + return false; + } + + // Check algorithm constraints with key and algorithm private boolean checkConstraints(Set primitives, String algorithm, Key key, AlgorithmParameters parameters) { @@ -116,7 +157,7 @@ throw new IllegalArgumentException("The key cannot be null"); } - // check the target algorithm + // check the signature algorithm if (algorithm != null && algorithm.length() != 0) { if (!permits(primitives, algorithm, parameters)) { return false; @@ -129,97 +170,203 @@ } // check the key constraints - if (keySizeConstraints.disables(key)) { - return false; - } - - return true; + return algorithmConstraints.permits(key); } - private static KeySizeConstraints getKeySizeConstraints( - String[] disabledAlgorithms, String propertyName) { - synchronized (keySizeConstraintsMap) { - if(!keySizeConstraintsMap.containsKey(propertyName)) { - // map the key constraints - KeySizeConstraints keySizeConstraints = - new KeySizeConstraints(disabledAlgorithms); - keySizeConstraintsMap.put(propertyName, keySizeConstraints); - } + /* + * Check algorithm constraints with Certificate + * Uses new style permit() which throws exceptions. + */ + private void checkConstraints(Set primitives, + CertConstraintParameters cp) throws CertPathValidatorException { + + X509Certificate cert = cp.getCertificate(); + String algorithm = cert.getSigAlgName(); - return keySizeConstraintsMap.get(propertyName); + // Check signature algorithm is not disabled + if (!permits(primitives, algorithm, null)) { + throw new CertPathValidatorException( + "Algorithm constraints check failed on disabled "+ + "signature algorithm: " + algorithm, + null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); } + + // Check key algorithm is not disabled + if (!permits(primitives, cert.getPublicKey().getAlgorithm(), null)) { + throw new CertPathValidatorException( + "Algorithm constraints check failed on disabled "+ + "public key algorithm: " + algorithm, + null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); + } + + // Check the certificate and key constraints + algorithmConstraints.permits(cp); + } /** - * key constraints + * Key and Certificate Constraints + * + * The complete disabling of an algorithm is not handled by Constraints or + * Constraint classes. That is addressed with + * permit(Set, String, AlgorithmParameters) + * + * When passing a Key to permit(), the boolean return values follow the + * same as the interface class AlgorithmConstraints.permit(). This is to + * maintain compatibility: + * 'true' means the operation is allowed. + * 'false' means it failed the constraints and is disallowed. + * + * When passing CertConstraintParameters through permit(), an exception + * will be thrown on a failure to better identify why the operation was + * disallowed. */ - private static class KeySizeConstraints { - private static final Pattern pattern = Pattern.compile( - "(\\S+)\\s+keySize\\s*(<=|<|==|!=|>|>=)\\s*(\\d+)"); - private Map> constraintsMap = - Collections.synchronizedMap( - new HashMap>()); + private static class Constraints { + private Map> constraintsMap = new HashMap<>(); + private static final Pattern keySizePattern = Pattern.compile( + "keySize\\s*(<=|<|==|!=|>|>=)\\s*(\\d+)"); - public KeySizeConstraints(String[] restrictions) { - for (String restriction : restrictions) { - if (restriction == null || restriction.isEmpty()) { + public Constraints(String[] constraintArray) { + for (String constraintEntry : constraintArray) { + if (constraintEntry == null || constraintEntry.isEmpty()) { continue; } - Matcher matcher = pattern.matcher(restriction); - if (matcher.matches()) { - String algorithm = matcher.group(1); + constraintEntry = constraintEntry.trim(); + if (debug != null) { + debug.println("Constraints: " + constraintEntry); + } - KeySizeConstraint.Operator operator = - KeySizeConstraint.Operator.of(matcher.group(2)); - int length = Integer.parseInt(matcher.group(3)); + // Check if constraint is a complete disabling of an + // algorithm or has conditions. + String algorithm; + String policy; + int space = constraintEntry.indexOf(' '); + if (space > 0) { + algorithm = AlgorithmDecomposer.hashName( + constraintEntry.substring(0, space). + toUpperCase(Locale.ENGLISH)); + policy = constraintEntry.substring(space + 1); + } else { + constraintsMap.computeIfAbsent( + constraintEntry.toUpperCase(Locale.ENGLISH), + k -> new HashSet<>()); + continue; + } - algorithm = algorithm.toLowerCase(Locale.ENGLISH); + // Convert constraint conditions into Constraint classes + Constraint c, lastConstraint = null; + // Allow only one jdkCA entry per constraint entry + boolean jdkCALimit = false; + + for (String entry : policy.split("&")) { + entry = entry.trim(); - synchronized (constraintsMap) { - if (!constraintsMap.containsKey(algorithm)) { - constraintsMap.put(algorithm, - new HashSet()); + Matcher matcher = keySizePattern.matcher(entry); + if (matcher.matches()) { + if (debug != null) { + debug.println("Constraints set to keySize: " + + entry); } + c = new KeySizeConstraint(algorithm, + KeySizeConstraint.Operator.of(matcher.group(1)), + Integer.parseInt(matcher.group(2))); - Set constraintSet = - constraintsMap.get(algorithm); - KeySizeConstraint constraint = - new KeySizeConstraint(operator, length); - constraintSet.add(constraint); + } else if (entry.equalsIgnoreCase("jdkCA")) { + if (debug != null) { + debug.println("Constraints set to jdkCA."); + } + if (jdkCALimit) { + throw new IllegalArgumentException("Only one " + + "jdkCA entry allowed in property. " + + "Constraint: " + constraintEntry); + } + c = new jdkCAConstraint(algorithm); + jdkCALimit = true; + } else { + throw new IllegalArgumentException("Error in security" + + " property. Constraint unknown: " + entry); } + + // Link multiple conditions for a single constraint + // into a linked list. + if (lastConstraint == null) { + if (!constraintsMap.containsKey(algorithm)) { + constraintsMap.putIfAbsent(algorithm, + new HashSet<>()); + } + constraintsMap.get(algorithm).add(c); + } else { + lastConstraint.nextConstraint = c; + } + lastConstraint = c; } } } - // Does this KeySizeConstraints disable the specified key? - public boolean disables(Key key) { - String algorithm = key.getAlgorithm().toLowerCase(Locale.ENGLISH); - synchronized (constraintsMap) { - if (constraintsMap.containsKey(algorithm)) { - Set constraintSet = - constraintsMap.get(algorithm); - for (KeySizeConstraint constraint : constraintSet) { - if (constraint.disables(key)) { - return true; - } + // Get applicable constraints based off the signature algorithm + private Set getConstraints(String algorithm) { + return constraintsMap.get(algorithm); + } + + // Check if KeySizeConstraints permit the specified key + public boolean permits(Key key) { + Set set = getConstraints(key.getAlgorithm()); + if (set == null) { + return true; + } + for (Constraint constraint : set) { + if (!constraint.permits(key)) { + if (debug != null) { + debug.println("keySizeConstraint: failed key " + + "constraint check " + KeyUtil.getKeySize(key)); } + return false; } } + return true; + } - return false; + // Check if constraints permit this cert. + public void permits(CertConstraintParameters cp) + throws CertPathValidatorException { + X509Certificate cert = cp.getCertificate(); + + if (debug != null) { + debug.println("Constraints.permits(): " + cert.getSigAlgName()); + } + + // Get all signature algorithms to check for constraints + Set algorithms = + AlgorithmDecomposer.decomposeOneHash(cert.getSigAlgName()); + if (algorithms == null || algorithms.isEmpty()) { + return; + } + + // Attempt to add the public key algorithm to the set + algorithms.add(cert.getPublicKey().getAlgorithm()); + + // Check all applicable constraints + for (String algorithm : algorithms) { + Set set = getConstraints(algorithm); + if (set == null) { + continue; + } + for (Constraint constraint : set) { + constraint.permits(cp); + } + } } } - /** - * Key size constraint. - * - * e.g. "keysize <= 1024" - */ - private static class KeySizeConstraint { + // Abstract class for algorithm constraint checking + private abstract static class Constraint { + String algorithm; + Constraint nextConstraint = null; + // operator - static enum Operator { + enum Operator { EQ, // "==" NE, // "!=" LT, // "<" @@ -243,16 +390,77 @@ return GE; } - throw new IllegalArgumentException( - s + " is not a legal Operator"); + throw new IllegalArgumentException("Error in security " + + "property. " + s + " is not a legal Operator"); } } + /** + * Check if an algorithm constraint permit this key to be used. + * @param key Public key + * @return true if constraints do not match + */ + public boolean permits(Key key) { + return true; + } + + /** + * Check if an algorithm constraint is permit this certificate to + * be used. + * @param cp CertificateParameter containing certificate and state info + * @return true if constraints do not match + */ + public abstract void permits(CertConstraintParameters cp) + throws CertPathValidatorException; + } + + /* + * This class contains constraints dealing with the certificate chain + * of the certificate. + */ + private static class jdkCAConstraint extends Constraint { + jdkCAConstraint(String algo) { + algorithm = algo; + } + + /* + * Check if each constraint fails and check if there is a linked + * constraint Any permitted constraint will exit the linked list + * to allow the operation. + */ + public void permits(CertConstraintParameters cp) + throws CertPathValidatorException { + if (debug != null) { + debug.println("jdkCAConstraints.permits(): " + algorithm); + } + + // Return false if the chain has a trust anchor in cacerts + if (cp.isTrustedMatch()) { + if (nextConstraint != null) { + nextConstraint.permits(cp); + return; + } + throw new CertPathValidatorException( + "Algorithm constraints check failed on certificate " + + "anchor limits", + null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); + } + } + } + + + /* + * This class contains constraints dealing with the key size + * support limits per algorithm. e.g. "keySize <= 1024" + */ + private static class KeySizeConstraint extends Constraint { + private int minSize; // the minimal available key size private int maxSize; // the maximal available key size private int prohibitedSize = -1; // unavailable key sizes - public KeySizeConstraint(Operator operator, int length) { + public KeySizeConstraint(String algo, Operator operator, int length) { + algorithm = algo; switch (operator) { case EQ: // an unavailable key size this.minSize = 0; @@ -286,21 +494,59 @@ } } - // Does this key constraint disable the specified key? - public boolean disables(Key key) { - int size = KeyUtil.getKeySize(key); + /* + * If we are passed a certificate, extract the public key and use it. + * + * Check if each constraint fails and check if there is a linked + * constraint Any permitted constraint will exit the linked list + * to allow the operation. + */ + public void permits(CertConstraintParameters cp) + throws CertPathValidatorException { + if (!permitsImpl(cp.getCertificate().getPublicKey())) { + if (nextConstraint != null) { + nextConstraint.permits(cp); + return; + } + throw new CertPathValidatorException( + "Algorithm constraints check failed on keysize limits", + null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); + } + } + + // Check if key constraint disable the specified key + // Uses old style permit() + public boolean permits(Key key) { + // If we recursively find a constraint that permits us to use + // this key, return true and skip any other constraint checks. + if (nextConstraint != null && nextConstraint.permits(key)) { + return true; + } + if (debug != null) { + debug.println("KeySizeConstraints.permits(): " + algorithm); + } + + return permitsImpl(key); + } + + private boolean permitsImpl(Key key) { + // Verify this constraint is for this public key algorithm + if (algorithm.compareToIgnoreCase(key.getAlgorithm()) != 0) { + return true; + } + + int size = KeyUtil.getKeySize(key); if (size == 0) { - return true; // we don't allow any key of size 0. + return false; // we don't allow any key of size 0. } else if (size > 0) { - return ((size < minSize) || (size > maxSize) || + return !((size < minSize) || (size > maxSize) || (prohibitedSize == size)); } // Otherwise, the key size is not accessible. Conservatively, // please don't disable such keys. - return false; + return true; } } - } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/util/KeyStoreDelegator.java --- a/jdk/src/java.base/share/classes/sun/security/util/KeyStoreDelegator.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/util/KeyStoreDelegator.java Tue May 03 12:25:20 2016 -0700 @@ -196,8 +196,9 @@ // A new keystore is always created in the primary keystore format if (stream == null) { try { - keystore = primaryKeyStore.newInstance(); - + @SuppressWarnings("deprecation") + KeyStoreSpi tmp = primaryKeyStore.newInstance(); + keystore = tmp; } catch (InstantiationException | IllegalAccessException e) { // can safely ignore } @@ -214,7 +215,9 @@ bufferedStream.mark(Integer.MAX_VALUE); try { - keystore = primaryKeyStore.newInstance(); + @SuppressWarnings("deprecation") + KeyStoreSpi tmp = primaryKeyStore.newInstance(); + keystore = tmp; type = primaryType; keystore.engineLoad(bufferedStream, password); @@ -232,7 +235,9 @@ throw e; } - keystore = secondaryKeyStore.newInstance(); + @SuppressWarnings("deprecation") + KeyStoreSpi tmp= secondaryKeyStore.newInstance(); + keystore = tmp; type = secondaryType; bufferedStream.reset(); keystore.engineLoad(bufferedStream, password); @@ -284,7 +289,9 @@ boolean result = false; try { - keystore = primaryKeyStore.newInstance(); + @SuppressWarnings("deprecation") + KeyStoreSpi tmp = primaryKeyStore.newInstance(); + keystore = tmp; type = primaryType; result = keystore.engineProbe(stream); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/util/LegacyAlgorithmConstraints.java --- a/jdk/src/java.base/share/classes/sun/security/util/LegacyAlgorithmConstraints.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/util/LegacyAlgorithmConstraints.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -28,8 +28,6 @@ import java.security.AlgorithmParameters; import java.security.CryptoPrimitive; import java.security.Key; -import java.util.HashMap; -import java.util.Map; import java.util.Set; import static sun.security.util.AbstractAlgorithmConstraints.getAlgorithms; @@ -42,15 +40,12 @@ public static final String PROPERTY_TLS_LEGACY_ALGS = "jdk.tls.legacyAlgorithms"; - private static final Map legacyAlgorithmsMap = - new HashMap<>(); - private final String[] legacyAlgorithms; public LegacyAlgorithmConstraints(String propertyName, AlgorithmDecomposer decomposer) { super(decomposer); - legacyAlgorithms = getAlgorithms(legacyAlgorithmsMap, propertyName); + legacyAlgorithms = getAlgorithms(propertyName); } @Override diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/x509/X509CertImpl.java --- a/jdk/src/java.base/share/classes/sun/security/x509/X509CertImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/x509/X509CertImpl.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -1924,17 +1924,18 @@ public String getFingerprint(String algorithm) { return fingerprints.computeIfAbsent(algorithm, - x -> getCertificateFingerPrint(x)); + x -> getFingerprint(x, this)); } /** * Gets the requested finger print of the certificate. The result * only contains 0-9 and A-F. No small case, no colon. */ - private String getCertificateFingerPrint(String mdAlg) { + public static String getFingerprint(String algorithm, + X509Certificate cert) { try { - byte[] encCertInfo = getEncoded(); - MessageDigest md = MessageDigest.getInstance(mdAlg); + byte[] encCertInfo = cert.getEncoded(); + MessageDigest md = MessageDigest.getInstance(algorithm); byte[] digest = md.digest(encCertInfo); StringBuilder sb = new StringBuilder(digest.length * 2); for (int i = 0; i < digest.length; i++) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/security/x509/X509Key.java --- a/jdk/src/java.base/share/classes/sun/security/x509/X509Key.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/x509/X509Key.java Tue May 03 12:25:20 2016 -0700 @@ -255,11 +255,10 @@ } } - Object inst = null; + @SuppressWarnings("deprecation") + Object inst = (keyClass != null) ? keyClass.newInstance() : null; X509Key result; - if (keyClass != null) - inst = keyClass.newInstance(); if (inst instanceof X509Key) { result = (X509Key) inst; result.algid = algid; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/util/calendar/CalendarSystem.java --- a/jdk/src/java.base/share/classes/sun/util/calendar/CalendarSystem.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/util/calendar/CalendarSystem.java Tue May 03 12:25:20 2016 -0700 @@ -157,8 +157,9 @@ cal = LocalGregorianCalendar.getLocalGregorianCalendar(calendarName); } else { try { - Class cl = Class.forName(className); - cal = (CalendarSystem) cl.newInstance(); + @SuppressWarnings("deprecation") + Object tmp = Class.forName(className).newInstance(); + cal = (CalendarSystem) tmp; } catch (Exception e) { throw new InternalError(e); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/util/calendar/LocalGregorianCalendar.java --- a/jdk/src/java.base/share/classes/sun/util/calendar/LocalGregorianCalendar.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/util/calendar/LocalGregorianCalendar.java Tue May 03 12:25:20 2016 -0700 @@ -144,7 +144,7 @@ // Append an era to the predefined eras if it's given by the property. String prop = GetPropertyAction - .getProperty("jdk.calendar.japanese.supplemental.era"); + .privilegedGetProperty("jdk.calendar.japanese.supplemental.era"); if (prop != null) { Era era = parseEraEntry(prop); if (era != null) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java --- a/jdk/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java Tue May 03 12:25:20 2016 -0700 @@ -246,7 +246,7 @@ static { String oldmapping = GetPropertyAction - .getProperty("sun.timezone.ids.oldmapping", "false") + .privilegedGetProperty("sun.timezone.ids.oldmapping", "false") .toLowerCase(Locale.ROOT); USE_OLDMAPPING = (oldmapping.equals("yes") || oldmapping.equals("true")); AccessController.doPrivileged(new PrivilegedAction() { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java --- a/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java Tue May 03 12:25:20 2016 -0700 @@ -116,7 +116,7 @@ adapterCache = new ConcurrentHashMap<>(); static { - String order = GetPropertyAction.getProperty("java.locale.providers"); + String order = GetPropertyAction.privilegedGetProperty("java.locale.providers"); List typeList = new ArrayList<>(); // Check user specified adapter preference @@ -171,8 +171,9 @@ if (cached == null) { try { // lazily load adapters here - adapter = (LocaleProviderAdapter)Class.forName(type.getAdapterClassName()) - .newInstance(); + @SuppressWarnings("deprecation") + Object tmp = Class.forName(type.getAdapterClassName()).newInstance(); + adapter = (LocaleProviderAdapter)tmp; cached = adapterInstances.putIfAbsent(type, adapter); if (cached != null) { adapter = cached; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/classes/sun/util/locale/provider/SPILocaleProviderAdapter.java --- a/jdk/src/java.base/share/classes/sun/util/locale/provider/SPILocaleProviderAdapter.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/SPILocaleProviderAdapter.java Tue May 03 12:25:20 2016 -0700 @@ -73,7 +73,7 @@ try { return AccessController.doPrivileged(new PrivilegedExceptionAction

() { @Override - @SuppressWarnings("unchecked") + @SuppressWarnings(value={"unchecked", "deprecation"}) public P run() { P delegate = null; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/share/conf/security/java.security --- a/jdk/src/java.base/share/conf/security/java.security Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/share/conf/security/java.security Tue May 03 12:25:20 2016 -0700 @@ -497,13 +497,13 @@ # " DisabledAlgorithm { , DisabledAlgorithm } " # # DisabledAlgorithm: -# AlgorithmName [Constraint] +# AlgorithmName [Constraint] { '&' Constraint } # # AlgorithmName: # (see below) # # Constraint: -# KeySizeConstraint +# KeySizeConstraint, CertConstraint # # KeySizeConstraint: # keySize Operator DecimalInteger @@ -520,6 +520,9 @@ # DecimalDigit: one of # 1 2 3 4 5 6 7 8 9 0 # +# CertConstraint +# jdkCA +# # The "AlgorithmName" is the standard algorithm name of the disabled # algorithm. See "Java Cryptography Architecture Standard Algorithm Name # Documentation" for information about Standard Algorithm Names. Matching @@ -542,6 +545,29 @@ # be disabled. Note that the "KeySizeConstraint" only makes sense to key # algorithms. # +# "CertConstraint" specifies additional constraints for +# certificates that contain algorithms that are restricted: +# +#   "jdkCA" prohibits the specified algorithm only if the algorithm is used +#     in a certificate chain that terminates at a marked trust anchor in the +#     lib/security/cacerts keystore.  All other chains are not affected. +#     If the jdkCA constraint is not set, then all chains using the +#     specified algorithm are restricted. jdkCA may only be used once in +# a DisabledAlgorithm expression. +#     Example:  To apply this constraint to SHA-1 certificates, include +#     the following:  "SHA1 jdkCA" +# +# When an algorithm must satisfy more than one constraint, it must be +# delimited by an ampersand '&'. For example, to restrict certificates in a +# chain that terminate at a distribution provided trust anchor and contain +# RSA keys that are less than or equal to 1024 bits, add the following +# constraint: "RSA keySize <= 1024 & jdkCA". +# +# All DisabledAlgorithms expressions are processed in the order defined in the +# property. This requires lower keysize constraints to be specified +# before larger keysize constraints of the same algorithm. For example: +# "RSA keySize < 1024 & jdkCA, RSA keySize < 2048". +# # Note: This property is currently used by Oracle's PKIX implementation. It # is not guaranteed to be examined and used by other implementations. # diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisFileSystem.java --- a/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisFileSystem.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisFileSystem.java Tue May 03 12:25:20 2016 -0700 @@ -42,7 +42,7 @@ super(provider, dir); // check os.version - String osversion = GetPropertyAction.getProperty("os.version"); + String osversion = GetPropertyAction.privilegedGetProperty("os.version"); String[] vers = Util.split(osversion, '.'); assert vers.length >= 2; int majorVersion = Integer.parseInt(vers[0]); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisFileSystemProvider.java --- a/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisFileSystemProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisFileSystemProvider.java Tue May 03 12:25:20 2016 -0700 @@ -85,7 +85,7 @@ @Override FileTypeDetector getFileTypeDetector() { Path userMimeTypes = Paths.get( - GetPropertyAction.getProperty("user.home"), ".mime.types"); + GetPropertyAction.privilegedGetProperty("user.home"), ".mime.types"); Path etcMimeTypes = Paths.get("/etc/mime.types"); return chain(new GioFileTypeDetector(), diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/unix/classes/java/io/UnixFileSystem.java --- a/jdk/src/java.base/unix/classes/java/io/UnixFileSystem.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/unix/classes/java/io/UnixFileSystem.java Tue May 03 12:25:20 2016 -0700 @@ -36,7 +36,7 @@ private final String javaHome; public UnixFileSystem() { - Properties props = GetPropertyAction.getProperties(); + Properties props = GetPropertyAction.privilegedGetProperties(); slash = props.getProperty("file.separator").charAt(0); colon = props.getProperty("path.separator").charAt(0); javaHome = props.getProperty("java.home"); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java --- a/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java Tue May 03 12:25:20 2016 -0700 @@ -125,7 +125,7 @@ } String helperPath() { - Properties props = GetPropertyAction.getProperties(); + Properties props = GetPropertyAction.privilegedGetProperties(); return helperPath(props.getProperty("java.home"), props.getProperty("os.arch")); } @@ -159,7 +159,7 @@ } static Platform get() { - String osName = GetPropertyAction.getProperty("os.name"); + String osName = GetPropertyAction.privilegedGetProperty("os.name"); if (osName.equals("Linux")) { return LINUX; } if (osName.contains("OS X")) { return BSD; } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/unix/classes/java/net/DefaultDatagramSocketImplFactory.java --- a/jdk/src/java.base/unix/classes/java/net/DefaultDatagramSocketImplFactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/unix/classes/java/net/DefaultDatagramSocketImplFactory.java Tue May 03 12:25:20 2016 -0700 @@ -40,7 +40,7 @@ static { String prefix = null; try { - prefix = GetPropertyAction.getProperty("impl.prefix", null); + prefix = GetPropertyAction.privilegedGetProperty("impl.prefix"); if (prefix != null) prefixImplClass = Class.forName("java.net."+prefix+"DatagramSocketImpl"); } catch (Exception e) { @@ -61,7 +61,9 @@ throws SocketException { if (prefixImplClass != null) { try { - return (DatagramSocketImpl)prefixImplClass.newInstance(); + @SuppressWarnings("deprecation") + DatagramSocketImpl result = (DatagramSocketImpl)prefixImplClass.newInstance(); + return result; } catch (Exception e) { throw new SocketException("can't instantiate DatagramSocketImpl"); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/unix/classes/sun/net/NetHooks.java --- a/jdk/src/java.base/unix/classes/sun/net/NetHooks.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/unix/classes/sun/net/NetHooks.java Tue May 03 12:25:20 2016 -0700 @@ -28,9 +28,6 @@ import java.net.InetAddress; import java.io.FileDescriptor; import java.io.IOException; -import java.security.AccessController; -import java.security.PrivilegedAction; -import sun.security.action.GetPropertyAction; /** * Defines static methods to be invoked prior to binding or connecting TCP sockets. diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/unix/classes/sun/net/sdp/SdpProvider.java --- a/jdk/src/java.base/unix/classes/sun/net/sdp/SdpProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/unix/classes/sun/net/sdp/SdpProvider.java Tue May 03 12:25:20 2016 -0700 @@ -55,8 +55,9 @@ private PrintStream log; public SdpProvider() { + Properties props = GetPropertyAction.privilegedGetProperties(); // if this property is not defined then there is nothing to do. - String file = GetPropertyAction.getProperty("com.sun.sdp.conf"); + String file = props.getProperty("com.sun.sdp.conf"); if (file == null) { this.enabled = false; this.rules = null; @@ -65,17 +66,15 @@ // load configuration file List list = null; - if (file != null) { - try { - list = loadRulesFromFile(file); - } catch (IOException e) { - fail("Error reading %s: %s", file, e.getMessage()); - } + try { + list = loadRulesFromFile(file); + } catch (IOException e) { + fail("Error reading %s: %s", file, e.getMessage()); } // check if debugging is enabled PrintStream out = null; - String logfile = GetPropertyAction.getProperty("com.sun.sdp.debug"); + String logfile = props.getProperty("com.sun.sdp.debug"); if (logfile != null) { out = System.out; if (logfile.length() > 0) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/unix/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java --- a/jdk/src/java.base/unix/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/unix/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java Tue May 03 12:25:20 2016 -0700 @@ -76,7 +76,7 @@ private String hostname; /* Domain to use if not specified by user */ private static String defaultDomain = - GetPropertyAction.getProperty("http.auth.ntlm.domain", ""); + GetPropertyAction.privilegedGetProperty("http.auth.ntlm.domain", ""); public static boolean supportsTransparentAuth () { return false; @@ -141,7 +141,7 @@ password = pw.getPassword(); init0(); try { - String version = GetPropertyAction.getProperty("ntlm.version"); + String version = GetPropertyAction.privilegedGetProperty("ntlm.version"); client = new Client(version, hostname, username, ntdomain, password); } catch (NTLMException ne) { try { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/unix/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java --- a/jdk/src/java.base/unix/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/unix/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java Tue May 03 12:25:20 2016 -0700 @@ -48,7 +48,9 @@ throw new AssertionError(x); } try { - return c.newInstance(); + @SuppressWarnings("deprecation") + AsynchronousChannelProvider result = c.newInstance(); + return result; } catch (IllegalAccessException | InstantiationException x) { throw new AssertionError(x); } @@ -59,7 +61,7 @@ * Returns the default AsynchronousChannelProvider. */ public static AsynchronousChannelProvider create() { - String osname = GetPropertyAction.getProperty("os.name"); + String osname = GetPropertyAction.privilegedGetProperty("os.name"); if (osname.equals("SunOS")) return createProvider("sun.nio.ch.SolarisAsynchronousChannelProvider"); if (osname.equals("Linux")) diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/unix/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java --- a/jdk/src/java.base/unix/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/unix/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java Tue May 03 12:25:20 2016 -0700 @@ -46,8 +46,8 @@ private static final boolean disableSynchronousRead; static { - String propValue = GetPropertyAction - .getProperty("sun.nio.ch.disableSynchronousRead", "false"); + String propValue = GetPropertyAction.privilegedGetProperty( + "sun.nio.ch.disableSynchronousRead", "false"); disableSynchronousRead = (propValue.length() == 0) ? true : Boolean.valueOf(propValue); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/unix/classes/sun/nio/fs/DefaultFileSystemProvider.java --- a/jdk/src/java.base/unix/classes/sun/nio/fs/DefaultFileSystemProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/unix/classes/sun/nio/fs/DefaultFileSystemProvider.java Tue May 03 12:25:20 2016 -0700 @@ -44,7 +44,9 @@ throw new AssertionError(x); } try { - return c.newInstance(); + @SuppressWarnings("deprecation") + FileSystemProvider result = c.newInstance(); + return result; } catch (IllegalAccessException | InstantiationException x) { throw new AssertionError(x); } @@ -54,7 +56,7 @@ * Returns the default FileSystemProvider. */ public static FileSystemProvider create() { - String osname = GetPropertyAction.getProperty("os.name"); + String osname = GetPropertyAction.privilegedGetProperty("os.name"); if (osname.equals("SunOS")) return createProvider("sun.nio.fs.SolarisFileSystemProvider"); if (osname.equals("Linux")) diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/unix/classes/sun/nio/fs/UnixFileSystem.java --- a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixFileSystem.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixFileSystem.java Tue May 03 12:25:20 2016 -0700 @@ -57,7 +57,7 @@ // process working directory then paths must be resolved against the // default directory. String propValue = GetPropertyAction - .getProperty("sun.nio.fs.chdirAllowed", "false"); + .privilegedGetProperty("sun.nio.fs.chdirAllowed", "false"); boolean chdirAllowed = (propValue.length() == 0) ? true : Boolean.valueOf(propValue); if (chdirAllowed) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/windows/classes/java/io/WinNTFileSystem.java --- a/jdk/src/java.base/windows/classes/java/io/WinNTFileSystem.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/windows/classes/java/io/WinNTFileSystem.java Tue May 03 12:25:20 2016 -0700 @@ -42,7 +42,7 @@ private final char semicolon; public WinNTFileSystem() { - Properties props = GetPropertyAction.getProperties(); + Properties props = GetPropertyAction.privilegedGetProperties(); slash = props.getProperty("file.separator").charAt(0); semicolon = props.getProperty("path.separator").charAt(0); altSlash = (this.slash == '\\') ? '/' : '\\'; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/windows/classes/java/net/DefaultDatagramSocketImplFactory.java --- a/jdk/src/java.base/windows/classes/java/net/DefaultDatagramSocketImplFactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/windows/classes/java/net/DefaultDatagramSocketImplFactory.java Tue May 03 12:25:20 2016 -0700 @@ -56,7 +56,7 @@ static { Class prefixImplClassLocal = null; - Properties props = GetPropertyAction.getProperties(); + Properties props = GetPropertyAction.privilegedGetProperties(); preferIPv4Stack = Boolean.parseBoolean( props.getProperty("java.net.preferIPv4Stack")); @@ -90,7 +90,9 @@ throws SocketException { if (prefixImplClass != null) { try { - return (DatagramSocketImpl) prefixImplClass.newInstance(); + @SuppressWarnings("deprecation") + Object result = prefixImplClass.newInstance(); + return (DatagramSocketImpl) result; } catch (Exception e) { throw new SocketException("can't instantiate DatagramSocketImpl"); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/windows/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java --- a/jdk/src/java.base/windows/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/windows/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java Tue May 03 12:25:20 2016 -0700 @@ -53,8 +53,8 @@ private static String defaultDomain; /* Domain to use if not specified by user */ static { - defaultDomain = GetPropertyAction.getProperty("http.auth.ntlm.domain", - "domain"); + defaultDomain = GetPropertyAction + .privilegedGetProperty("http.auth.ntlm.domain", "domain"); }; private void init0() { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/windows/classes/sun/nio/ch/FileDispatcherImpl.java --- a/jdk/src/java.base/windows/classes/sun/nio/ch/FileDispatcherImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/windows/classes/sun/nio/ch/FileDispatcherImpl.java Tue May 03 12:25:20 2016 -0700 @@ -120,7 +120,7 @@ static boolean isFastFileTransferRequested() { String fileTransferProp = GetPropertyAction - .getProperty("jdk.nio.enableFastFileTransfer"); + .privilegedGetProperty("jdk.nio.enableFastFileTransfer"); boolean enable; if ("".equals(fileTransferProp)) { enable = true; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java --- a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java Tue May 03 12:25:20 2016 -0700 @@ -114,8 +114,8 @@ // indicates if accurate metadata is required (interesting on NTFS only) private static final boolean ensureAccurateMetadata; static { - String propValue = GetPropertyAction - .getProperty("sun.nio.fs.ensureAccurateMetadata", "false"); + String propValue = GetPropertyAction.privilegedGetProperty( + "sun.nio.fs.ensureAccurateMetadata", "false"); ensureAccurateMetadata = (propValue.length() == 0) ? true : Boolean.valueOf(propValue); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.base/windows/classes/sun/nio/fs/WindowsWatchService.java --- a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsWatchService.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsWatchService.java Tue May 03 12:25:20 2016 -0700 @@ -113,6 +113,10 @@ // completion key (used to map I/O completion to WatchKey) private int completionKey; + // flag indicates that ReadDirectoryChangesW failed + // and overlapped I/O operation wasn't started + private boolean errorStartingOverlapped; + WindowsWatchKey(Path dir, AbstractWatchService watcher, FileKey fileKey) @@ -175,6 +179,14 @@ return completionKey; } + void setErrorStartingOverlapped(boolean value) { + errorStartingOverlapped = value; + } + + boolean isErrorStartingOverlapped() { + return errorStartingOverlapped; + } + // Invalidate the key, assumes that resources have been released void invalidate() { ((WindowsWatchService)watcher()).poller.releaseResources(this); @@ -182,6 +194,7 @@ buffer = null; countAddress = 0; overlappedAddress = 0; + errorStartingOverlapped = false; } @Override @@ -455,11 +468,13 @@ * resources. */ private void releaseResources(WindowsWatchKey key) { - try { - CancelIo(key.handle()); - GetOverlappedResult(key.handle(), key.overlappedAddress()); - } catch (WindowsException expected) { - // expected as I/O operation has been cancelled + if (!key.isErrorStartingOverlapped()) { + try { + CancelIo(key.handle()); + GetOverlappedResult(key.handle(), key.overlappedAddress()); + } catch (WindowsException expected) { + // expected as I/O operation has been cancelled + } } CloseHandle(key.handle()); closeAttachedEvent(key.overlappedAddress()); @@ -628,6 +643,7 @@ } catch (WindowsException x) { // no choice but to cancel key criticalError = true; + key.setErrorStartingOverlapped(true); } } if (criticalError) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.desktop/unix/native/libawt_xawt/awt/awt_InputMethod.c --- a/jdk/src/java.desktop/unix/native/libawt_xawt/awt/awt_InputMethod.c Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.desktop/unix/native/libawt_xawt/awt/awt_InputMethod.c Tue May 03 12:25:20 2016 -0700 @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "awt.h" diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/AsyncConnection.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncConnection.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015, 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 + */ +package java.net.http; + +import java.nio.ByteBuffer; +import java.util.function.Consumer; + +/** + * Implemented by classes that offer an asynchronous interface. + * + * PlainHttpConnection, AsyncSSLConnection AsyncSSLDelegate. + * + * setAsyncCallbacks() is called to set the callback for reading + * and error notification. Reads all happen on the selector thread, which + * must not block. + * + * Writing uses the same write() methods as used in blocking mode. + * Queues are employed on the writing side to buffer data while it is waiting + * to be sent. This strategy relies on HTTP/2 protocol flow control to stop + * outgoing queue from continually growing. Writes can be initiated by the + * calling thread, but if socket becomes full then the queue is emptied by + * the selector thread + * + */ +interface AsyncConnection { + + /** + * Enables asynchronous sending and receiving mode. The given async + * receiver will receive all incoming data. asyncInput() will be called + * to trigger reads. asyncOutput() will be called to drive writes. + * + * The errorReceiver callback must be called when any fatal exception + * occurs. Connection is assumed to be closed afterwards. + * + * @param asyncReceiver + * @param errorReceiver + */ + void setAsyncCallbacks( + Consumer asyncReceiver, + Consumer errorReceiver); + + /** + * Does whatever is required to start reading. Usually registers + * an event with the selector thread. + */ + void startReading(); +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/AsyncEvent.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncEvent.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncEvent.java Tue May 03 12:25:20 2016 -0700 @@ -25,24 +25,27 @@ package java.net.http; import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; /** * Event handling interface from HttpClientImpl's selector. * - *

If blockingChannel is true, then the channel will be put in blocking + * If BLOCKING is set, then the channel will be put in blocking * mode prior to handle() being called. If false, then it remains non-blocking. + * + * If REPEATING is set then the event is not cancelled after being posted. */ abstract class AsyncEvent { - /** - * Implement this if channel should be made blocking before calling handle() - */ - public interface Blocking { } + public static final int BLOCKING = 0x1; // non blocking if not set + public static final int REPEATING = 0x2; // one off event if not set - /** - * Implement this if channel should remain non-blocking before calling handle() - */ - public interface NonBlocking { } + protected final int flags; + + AsyncEvent(int flags) { + this.flags = flags; + } /** Returns the channel */ public abstract SelectableChannel channel(); @@ -55,4 +58,12 @@ /** Called when selector is shutting down. Abort all exchanges. */ public abstract void abort(); + + public boolean blocking() { + return (flags & BLOCKING) != 0; + } + + public boolean repeating() { + return (flags & REPEATING) != 0; + } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLConnection.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLConnection.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2015, 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 + */ +package java.net.http; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; + +/** + * Asynchronous version of SSLConnection. + */ +class AsyncSSLConnection extends HttpConnection implements AsyncConnection { + final AsyncSSLDelegate sslDelegate; + final PlainHttpConnection delegate; + + AsyncSSLConnection(InetSocketAddress addr, HttpClientImpl client, String[] ap) { + super(addr, client); + delegate = new PlainHttpConnection(addr, client); + sslDelegate = new AsyncSSLDelegate(delegate, client, ap); + } + + @Override + public void connect() throws IOException, InterruptedException { + delegate.connect(); + } + + @Override + public CompletableFuture connectAsync() { + return delegate.connectAsync(); + } + + @Override + boolean connected() { + return delegate.connected(); + } + + @Override + boolean isSecure() { + return true; + } + + @Override + boolean isProxied() { + return false; + } + + @Override + SocketChannel channel() { + return delegate.channel(); + } + + @Override + ConnectionPool.CacheKey cacheKey() { + return ConnectionPool.cacheKey(address, null); + } + + @Override + synchronized long write(ByteBuffer[] buffers, int start, int number) throws IOException { + ByteBuffer[] bufs = Utils.reduce(buffers, start, number); + long n = Utils.remaining(bufs); + sslDelegate.write(bufs); + return n; + } + + @Override + long write(ByteBuffer buffer) throws IOException { + long n = buffer.remaining(); + sslDelegate.write(buffer); + return n; + } + + @Override + public void close() { + Utils.close(sslDelegate, delegate.channel()); + } + + @Override + public void setAsyncCallbacks(Consumer asyncReceiver, Consumer errorReceiver) { + sslDelegate.setAsyncCallbacks(asyncReceiver, errorReceiver); + delegate.setAsyncCallbacks(sslDelegate::lowerRead, errorReceiver); + } + + // Blocking read functions not used here + + @Override + protected ByteBuffer readImpl(int length) throws IOException { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + protected int readImpl(ByteBuffer buffer) throws IOException { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + CompletableFuture whenReceivingResponse() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void startReading() { + delegate.startReading(); + sslDelegate.startReading(); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLDelegate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLDelegate.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,598 @@ +/* + * Copyright (c) 2015, 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 + */ +package java.net.http; + +import java.io.Closeable; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.concurrent.ExecutorService; +import java.util.function.Consumer; +import static javax.net.ssl.SSLEngineResult.Status.*; +import javax.net.ssl.*; +import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*; + +/** + * Asynchronous wrapper around SSLEngine. send and receive is fully non + * blocking. When handshaking is required, a thread is created to perform + * the handshake and application level sends do not take place during this time. + * + * Is implemented using queues and functions operating on the receiving end + * of each queue. + * + * Application writes to: + * || + * \/ + * appOutputQ + * || + * \/ + * appOutputQ read by "upperWrite" method which does SSLEngine.wrap + * and writes to + * || + * \/ + * channelOutputQ + * || + * \/ + * channelOutputQ is read by "lowerWrite" method which is invoked from + * OP_WRITE events on the socket (from selector thread) + * + * Reading side is as follows + * -------------------------- + * + * "upperRead" method reads off channelInputQ and calls SSLEngine.unwrap and + * when decrypted data is returned, it is passed to the user's Consumer + * /\ + * || + * channelInputQ + * /\ + * || + * "lowerRead" method puts buffers into channelInputQ. It is invoked from + * OP_READ events from the selector. + * + * Whenever handshaking is required, the doHandshaking() method is called + * which creates a thread to complete the handshake. It takes over the + * channelInputQ from upperRead, and puts outgoing packets on channelOutputQ. + * Selector events are delivered to lowerRead and lowerWrite as normal. + * + * Errors + * + * Any exception thrown by the engine or channel, causes all Queues to be closed + * the channel to be closed, and the error is reported to the user's + * Consumer + */ +public class AsyncSSLDelegate implements Closeable, AsyncConnection { + + // outgoing buffers put in this queue first and may remain here + // while SSL handshaking happening. + final Queue appOutputQ; + + // queue of wrapped ByteBuffers waiting to be sent on socket channel + //final Queue channelOutputQ; + + // Bytes read into this queue before being unwrapped. Backup on this + // Q should only happen when the engine is stalled due to delegated tasks + final Queue channelInputQ; + + // input occurs through the read() method which is expected to be called + // when the selector signals some data is waiting to be read. All incoming + // handshake data is handled in this method, which means some calls to + // read() may return zero bytes of user data. This is not a sign of spinning, + // just that the handshake mechanics are being executed. + + final SSLEngine engine; + final SSLParameters sslParameters; + //final SocketChannel chan; + final HttpConnection lowerOutput; + final HttpClientImpl client; + final ExecutorService executor; + final BufferHandler bufPool; + Consumer receiver; + Consumer errorHandler; + // Locks. + final Object reader = new Object(); + final Object writer = new Object(); + // synchronizing handshake state + final Object handshaker = new Object(); + // flag set when reader or writer is blocked waiting for handshake to finish + boolean writerBlocked; + boolean readerBlocked; + + // some thread is currently doing the handshake + boolean handshaking; + + // alpn[] may be null. upcall is callback which receives incoming decoded bytes off socket + + AsyncSSLDelegate(HttpConnection lowerOutput, HttpClientImpl client, String[] alpn) + { + SSLContext context = client.sslContext(); + executor = client.executorService(); + bufPool = client; + appOutputQ = new Queue<>(); + appOutputQ.registerPutCallback(this::upperWrite); + //channelOutputQ = new Queue<>(); + //channelOutputQ.registerPutCallback(this::lowerWrite); + engine = context.createSSLEngine(); + engine.setUseClientMode(true); + SSLParameters sslp = client.sslParameters().orElse(null); + if (sslp == null) { + sslp = context.getSupportedSSLParameters(); + //sslp = context.getDefaultSSLParameters(); + //printParams(sslp); + } + sslParameters = Utils.copySSLParameters(sslp); + if (alpn != null) { + sslParameters.setApplicationProtocols(alpn); + Log.logSSL("Setting application protocols: " + Arrays.toString(alpn)); + } else { + Log.logSSL("No application protocols proposed"); + } + engine.setSSLParameters(sslParameters); + engine.setEnabledCipherSuites(sslp.getCipherSuites()); + engine.setEnabledProtocols(sslp.getProtocols()); + this.lowerOutput = lowerOutput; + this.client = client; + this.channelInputQ = new Queue<>(); + this.channelInputQ.registerPutCallback(this::upperRead); + } + + /** + * Put buffers to appOutputQ, and call upperWrite() if q was empty. + * + * @param src + */ + public void write(ByteBuffer[] src) throws IOException { + appOutputQ.putAll(src); + } + + public void write(ByteBuffer buf) throws IOException { + ByteBuffer[] a = new ByteBuffer[1]; + a[0] = buf; + write(a); + } + + @Override + public void close() { + Utils.close(appOutputQ, channelInputQ, lowerOutput); + } + + /** + * Attempts to wrap buffers from appOutputQ and place them on the + * channelOutputQ for writing. If handshaking is happening, then the + * process stalls and last buffers taken off the appOutputQ are put back + * into it until handshaking completes. + * + * This same method is called to try and resume output after a blocking + * handshaking operation has completed. + */ + private void upperWrite() { + try { + EngineResult r = null; + ByteBuffer[] buffers = appOutputQ.pollAll(Utils.EMPTY_BB_ARRAY); + int bytes = Utils.remaining(buffers); + while (bytes > 0) { + synchronized (writer) { + r = wrapBuffers(buffers); + int bytesProduced = r.bytesProduced(); + int bytesConsumed = r.bytesConsumed(); + bytes -= bytesConsumed; + if (bytesProduced > 0) { + // pass destination buffer to channelOutputQ. + lowerOutput.write(r.destBuffer); + } + synchronized (handshaker) { + if (r.handshaking()) { + // handshaking is happening or is needed + // so we put the buffers back on Q to process again + // later. It's possible that some may have already + // been processed, which is ok. + appOutputQ.pushbackAll(buffers); + writerBlocked = true; + if (!handshaking()) { + // execute the handshake in another thread. + // This method will be called again to resume sending + // later + doHandshake(r); + } + return; + } + } + } + } + returnBuffers(buffers); + } catch (Throwable t) { + t.printStackTrace(); + close(); + } + } + + private void doHandshake(EngineResult r) { + handshaking = true; + channelInputQ.registerPutCallback(null); + executor.execute(() -> { + try { + doHandshakeImpl(r); + channelInputQ.registerPutCallback(this::upperRead); + } catch (Throwable t) { + t.printStackTrace(); + close(); + } + }); + } + + private void returnBuffers(ByteBuffer[] bufs) { + for (ByteBuffer buf : bufs) + client.returnBuffer(buf); + } + + /** + * Return true if some thread is currently doing the handshake + * + * @return + */ + boolean handshaking() { + synchronized(handshaker) { + return handshaking; + } + } + + /** + * Executes entire handshake in calling thread. + * Returns after handshake is completed or error occurs + * @param r + * @throws IOException + */ + private void doHandshakeImpl(EngineResult r) throws IOException { + while (true) { + SSLEngineResult.HandshakeStatus status = r.handshakeStatus(); + if (status == NEED_TASK) { + LinkedList tasks = obtainTasks(); + for (Runnable task : tasks) + task.run(); + r = handshakeWrapAndSend(); + } else if (status == NEED_WRAP) { + r = handshakeWrapAndSend(); + } else if (status == NEED_UNWRAP) { + r = handshakeReceiveAndUnWrap(); + } + if (!r.handshaking()) + break; + } + boolean dowrite = false; + boolean doread = false; + // Handshake is finished. Now resume reading and/or writing + synchronized(handshaker) { + handshaking = false; + if (writerBlocked) { + writerBlocked = false; + dowrite = true; + } + if (readerBlocked) { + readerBlocked = false; + doread = true; + } + } + if (dowrite) + upperWrite(); + if (doread) + upperRead(); + } + + // acknowledge a received CLOSE request from peer + void doClosure() throws IOException { + //while (!wrapAndSend(emptyArray)) + //; + } + + LinkedList obtainTasks() { + LinkedList l = new LinkedList<>(); + Runnable r; + while ((r = engine.getDelegatedTask()) != null) + l.add(r); + return l; + } + + @Override + public synchronized void setAsyncCallbacks(Consumer asyncReceiver, Consumer errorReceiver) { + this.receiver = asyncReceiver; + this.errorHandler = errorReceiver; + } + + @Override + public void startReading() { + // maybe this class does not need to implement AsyncConnection + } + + static class EngineResult { + ByteBuffer destBuffer; + ByteBuffer srcBuffer; + SSLEngineResult result; + Throwable t; + + boolean handshaking() { + SSLEngineResult.HandshakeStatus s = result.getHandshakeStatus(); + return s != FINISHED && s != NOT_HANDSHAKING; + } + + int bytesConsumed() { + return result.bytesConsumed(); + } + + int bytesProduced() { + return result.bytesProduced(); + } + + Throwable exception() { + return t; + } + + SSLEngineResult.HandshakeStatus handshakeStatus() { + return result.getHandshakeStatus(); + } + + SSLEngineResult.Status status() { + return result.getStatus(); + } + } + + EngineResult handshakeWrapAndSend() throws IOException { + EngineResult r = wrapBuffer(Utils.EMPTY_BYTEBUFFER); + if (r.bytesProduced() > 0) { + lowerOutput.write(r.destBuffer); + } + return r; + } + + // called during handshaking. It blocks until a complete packet + // is available, unwraps it and returns. + EngineResult handshakeReceiveAndUnWrap() throws IOException { + ByteBuffer buf = channelInputQ.take(); + while (true) { + // block waiting for input + EngineResult r = unwrapBuffer(buf); + SSLEngineResult.Status status = r.status(); + if (status == BUFFER_UNDERFLOW) { + // wait for another buffer to arrive + ByteBuffer buf1 = channelInputQ.take(); + buf = combine (buf, buf1); + continue; + } + // OK + // theoretically possible we could receive some user data + if (r.bytesProduced() > 0) { + receiver.accept(r.destBuffer); + } + if (!buf.hasRemaining()) + return r; + } + } + + EngineResult wrapBuffer(ByteBuffer src) throws SSLException { + ByteBuffer[] bufs = new ByteBuffer[1]; + bufs[0] = src; + return wrapBuffers(bufs); + } + + EngineResult wrapBuffers(ByteBuffer[] src) throws SSLException { + EngineResult r = new EngineResult(); + ByteBuffer dst = bufPool.getBuffer(); + while (true) { + r.result = engine.wrap(src, dst); + switch (r.result.getStatus()) { + case BUFFER_OVERFLOW: + dst = getPacketBuffer(); + break; + case CLOSED: + case OK: + dst.flip(); + r.destBuffer = dst; + return r; + case BUFFER_UNDERFLOW: + // underflow handled externally + bufPool.returnBuffer(dst); + return r; + default: + assert false; + } + } + } + + EngineResult unwrapBuffer(ByteBuffer srcbuf) throws IOException { + EngineResult r = new EngineResult(); + r.srcBuffer = srcbuf; + + ByteBuffer dst = bufPool.getBuffer(); + while (true) { + r.result = engine.unwrap(srcbuf, dst); + switch (r.result.getStatus()) { + case BUFFER_OVERFLOW: + // dest buffer not big enough. Reallocate + int oldcap = dst.capacity(); + dst = getApplicationBuffer(); + assert dst.capacity() > oldcap; + break; + case CLOSED: + doClosure(); + throw new IOException("Engine closed"); + case BUFFER_UNDERFLOW: + bufPool.returnBuffer(dst); + return r; + case OK: + dst.flip(); + r.destBuffer = dst; + return r; + } + } + } + + /** + * Asynchronous read input. Call this when selector fires. + * Unwrap done in upperRead because it also happens in + * doHandshake() when handshake taking place + */ + public void lowerRead(ByteBuffer buffer) { + try { + channelInputQ.put(buffer); + } catch (Throwable t) { + close(); + errorHandler.accept(t); + } + } + + public void upperRead() { + EngineResult r; + ByteBuffer srcbuf; + synchronized (reader) { + try { + srcbuf = channelInputQ.poll(); + if (srcbuf == null) { + return; + } + while (true) { + r = unwrapBuffer(srcbuf); + switch (r.result.getStatus()) { + case BUFFER_UNDERFLOW: + // Buffer too small. Need to combine with next buf + ByteBuffer nextBuf = channelInputQ.poll(); + if (nextBuf == null) { + // no data available. push buffer back until more data available + channelInputQ.pushback(srcbuf); + return; + } else { + srcbuf = combine(srcbuf, nextBuf); + } + break; + case OK: + // check for any handshaking work + synchronized (handshaker) { + if (r.handshaking()) { + // handshaking is happening or is needed + // so we put the buffer back on Q to process again + // later. + channelInputQ.pushback(srcbuf); + readerBlocked = true; + if (!handshaking()) { + // execute the handshake in another thread. + // This method will be called again to resume sending + // later + doHandshake(r); + } + return; + } + } + ByteBuffer dst = r.destBuffer; + if (dst.hasRemaining()) { + receiver.accept(dst); + } + } + if (srcbuf.hasRemaining()) { + continue; + } + srcbuf = channelInputQ.poll(); + if (srcbuf == null) { + return; + } + } + } catch (Throwable t) { + Utils.close(lowerOutput); + errorHandler.accept(t); + } + } + } + + /** + * Get a new buffer that is the right size for application buffers. + * + * @return + */ + ByteBuffer getApplicationBuffer() { + SSLSession session = engine.getSession(); + int appBufsize = session.getApplicationBufferSize(); + bufPool.setMinBufferSize(appBufsize); + return bufPool.getBuffer(appBufsize); + } + + ByteBuffer getPacketBuffer() { + SSLSession session = engine.getSession(); + int packetBufSize = session.getPacketBufferSize(); + bufPool.setMinBufferSize(packetBufSize); + return bufPool.getBuffer(packetBufSize); + } + + ByteBuffer combine(ByteBuffer buf1, ByteBuffer buf2) { + int avail1 = buf1.capacity() - buf1.remaining(); + if (buf2.remaining() < avail1) { + buf1.compact(); + buf1.put(buf2); + buf1.flip(); + return buf1; + } + int newsize = buf1.remaining() + buf2.remaining(); + ByteBuffer newbuf = bufPool.getBuffer(newsize); + newbuf.put(buf1); + newbuf.put(buf2); + newbuf.flip(); + return newbuf; + } + + SSLParameters getSSLParameters() { + return sslParameters; + } + + static void printParams(SSLParameters p) { + System.out.println("SSLParameters:"); + if (p == null) { + System.out.println("Null params"); + return; + } + for (String cipher : p.getCipherSuites()) { + System.out.printf("cipher: %s\n", cipher); + } + for (String approto : p.getApplicationProtocols()) { + System.out.printf("application protocol: %s\n", approto); + } + for (String protocol : p.getProtocols()) { + System.out.printf("protocol: %s\n", protocol); + } + if (p.getServerNames() != null) + for (SNIServerName sname : p.getServerNames()) { + System.out.printf("server name: %s\n", sname.toString()); + } + } + + String getSessionInfo() { + StringBuilder sb = new StringBuilder(); + String application = engine.getApplicationProtocol(); + SSLSession sess = engine.getSession(); + String cipher = sess.getCipherSuite(); + String protocol = sess.getProtocol(); + sb.append("Handshake complete alpn: ") + .append(application) + .append(", Cipher: ") + .append(cipher) + .append(", Protocol: ") + .append(protocol); + return sb.toString(); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/AuthenticationFilter.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/AuthenticationFilter.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/AuthenticationFilter.java Tue May 03 12:25:20 2016 -0700 @@ -45,7 +45,7 @@ static final int DEFAULT_RETRY_LIMIT = 3; static final int retry_limit = Utils.getIntegerNetProperty( - "sun.net.httpclient.auth.retrylimit", DEFAULT_RETRY_LIMIT); + "java.net.httpclient.auth.retrylimit", DEFAULT_RETRY_LIMIT); static final int UNAUTHORIZED = 401; static final int PROXY_UNAUTHORIZED = 407; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/BufferHandler.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/BufferHandler.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/BufferHandler.java Tue May 03 12:25:20 2016 -0700 @@ -27,11 +27,23 @@ import java.nio.ByteBuffer; /** - * Implemented by buffer pools. + * Implemented by buffer pools. A buffer pool has a current buffer size + * (number of bytes in each buffer) which may increase over time. */ interface BufferHandler { - ByteBuffer getBuffer(); + default ByteBuffer getBuffer() { + return getBuffer(-1); + } + + void setMinBufferSize(int size); + + /** + * size == -1 means return any sized buffer. Any other value means + * @param size + * @return + */ + ByteBuffer getBuffer(int size); void returnBuffer(ByteBuffer buffer); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferConsumer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferConsumer.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2015, 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 + */ + +package java.net.http; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.function.Supplier; + +/** + * Takes a List which is assumed to contain at least one HTTP/2 + * frame and allows it to be processed supplying bytes, ints, shorts, byte[] etc. + * from the list. As each ByteBuffer is consumed it is removed from the List<>. + * + * NOTE. shorts and bytes returned are UNSIGNED ints + * + * When finished processing the frame, the List may be empty or may contain + * partially read or unread ByteBuffers. A new ByteBufferConsumer can be + * created with the List<> + */ +class ByteBufferConsumer { + + ByteBuffer currentBuffer; + + final List buffers; + final ListIterator iterator; + final Supplier newBufferSupplier; + + ByteBufferConsumer(List buffers, + Supplier newBufferSupplier) { + this.buffers = buffers; + this.newBufferSupplier = newBufferSupplier; + this.iterator = buffers.listIterator(); + if (!iterator.hasNext()) { + throw new IllegalArgumentException("Empty buffer list"); + } + currentBuffer = iterator.next(); + } + + private void dump() { + int l = 0; + System.err.printf("ByteBufferConsumer:\n"); + for (ByteBuffer buf : buffers) { + System.err.printf("\t%s\n", buf.toString()); + l+= buf.remaining(); + } + System.err.printf("BBC contains %d bytes\n", l); + } + + private synchronized ByteBuffer getBuffer(boolean exception) throws IOException { + while (currentBuffer == null || !currentBuffer.hasRemaining()) { + if (currentBuffer != null) { + iterator.remove(); + } + if (!iterator.hasNext()) { + currentBuffer = null; + if (exception) { + throw new IOException ("Connection closed unexpectedly"); + } + return null; + } + currentBuffer = iterator.next(); + } + return currentBuffer; + } + + // call this to check if the data has all been consumed + + public boolean consumed() { + try { + return getBuffer(false) == null; + } catch (IOException e) { + /* CAN'T HAPPEN */ + throw new InternalError(); + } + } + + public int getByte() throws IOException { + // TODO: what to do if connection is closed. Throw NPE? + ByteBuffer buf = getBuffer(true); + return buf.get() & 0xff; + } + + public byte[] getBytes(int n) throws IOException { + return getBytes(n, null); + } + + public byte[] getBytes(int n, byte[] buf) throws IOException { + if (buf == null) { + buf = new byte[n]; + } else if (buf.length < n) { + throw new IllegalArgumentException("getBytes: buffer too small"); + } + int offset = 0; + while (n > 0) { + ByteBuffer b = getBuffer(true); + int length = Math.min(n, b.remaining()); + b.get(buf, offset, length); + offset += length; + n -= length; + } + return buf; + } + + public int getShort() throws IOException { + ByteBuffer buf = getBuffer(true); + int rem = buf.remaining(); + if (rem >= 2) { + return buf.getShort() & 0xffff; + } + // Slow path. Not common + int val = 0; + val = (val << 8) + getByte(); + val = (val << 8) + getByte(); + return val; + } + + public int getInt() throws IOException { + ByteBuffer buf = getBuffer(true); + int rem = buf.remaining(); + if (rem >= 4) { + return buf.getInt(); + } + // Slow path. Not common + int val = 0; + for (int nbytes = 0; nbytes < 4; nbytes++) { + val = (val << 8) + getByte(); + } + return val; + } + + private static final ByteBuffer[] EMPTY = new ByteBuffer[0]; + + /** + * Extracts whatever number of ByteBuffers from list to get required number + * of bytes. Any remaining buffers are 'tidied up' so reading can continue. + */ + public ByteBuffer[] getBuffers(int bytecount) throws IOException { + LinkedList l = new LinkedList<>(); + while (bytecount > 0) { + ByteBuffer buffer = getBuffer(true); + int remaining = buffer.remaining(); + if (remaining > bytecount) { + int difference = remaining - bytecount; + // split + ByteBuffer newb = newBufferSupplier.get(); + newb.clear(); + int limit = buffer.limit(); + buffer.limit(limit - difference); + newb.put(buffer); + newb.flip(); + buffer.limit(limit); + l.add(newb); + bytecount = 0; + } else { + l.add(buffer); + currentBuffer = null; + iterator.remove(); + bytecount -= remaining; + } + } + return l.toArray(EMPTY); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferGenerator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferGenerator.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2015, 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 + */ + +package java.net.http; + +import java.nio.ByteBuffer; +import java.util.ArrayList; + +/** + * Manages a ByteBuffer[] for writing frames into for output. The last + * ByteBuffer in the list is always unflipped (able to receive more bytes for + * sending) until getBufferArray() is called, which calls finish(). + * + * This allows multiple frames to be written to the same BBG. + * + * Buffers added with addByteBuffer() must be already flipped. + */ +class ByteBufferGenerator { + + ByteBuffer currentBuffer; + // source is assumed to always return the same sized buffer + final BufferHandler pool; + final ArrayList buflist; + final int bufsize; + boolean finished; + + ByteBufferGenerator(BufferHandler pool) { + this.buflist = new ArrayList<>(); + this.pool = pool; + this.currentBuffer = pool.getBuffer(); + this.bufsize = currentBuffer.capacity(); + } + + private static final ByteBuffer[] EMPTY = new ByteBuffer[0]; + + public ByteBuffer[] getBufferArray() { + finish(); + return buflist.toArray(EMPTY); + } + + public ArrayList getBufferList() { + finish(); + return buflist; + } + + private synchronized void finish() { + if (finished) { + return; + } + finished = true; + currentBuffer.flip(); + if (currentBuffer.hasRemaining()) { + buflist.add(currentBuffer); + } else { + pool.returnBuffer(currentBuffer); + } + } + + // only used for SettingsFrame: offset is number of bytes to + // ignore at start (we only want the payload of the settings frame) + public byte[] asByteArray(int offset) { + ByteBuffer[] bufs = getBufferArray(); + int size = 0; + for (ByteBuffer buf : bufs) { + size += buf.remaining(); + } + byte[] bytes = new byte[size-offset]; + int pos = 0; + for (ByteBuffer buf : bufs) { + int rem = buf.remaining(); + int ignore = Math.min(rem, offset); + buf.position(buf.position()+ignore); + rem -= ignore; + offset -= ignore; + buf.get(bytes, pos, rem); + pos += rem; + } + return bytes; + } + + ByteBuffer getBuffer(long n) { + if (currentBuffer.remaining() < n) { + getNewBuffer(); + if (n > currentBuffer.capacity()) { + throw new IllegalArgumentException("requested buffer too large"); + } + } + return currentBuffer; + } + + void getNewBuffer() { + currentBuffer.flip(); + if (currentBuffer.hasRemaining()) { + buflist.add(currentBuffer); + } else { + pool.returnBuffer(currentBuffer); + } + currentBuffer = pool.getBuffer(); + } + + void addByteBuffer(ByteBuffer buf) { + getNewBuffer(); + buflist.add(buf); + } + + void addPadding(int length) { + while (length > 0) { + int n = Math.min(length, bufsize); + ByteBuffer b = getBuffer(n); + // TODO: currently zeroed? + b.position(b.position() + n); + length -= n; + } + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/CharsetToolkit.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/CharsetToolkit.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,159 @@ +/* + * 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 + */ +package java.net.http; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +import static java.nio.charset.StandardCharsets.UTF_8; + +// The purpose of this class is to separate charset-related tasks from the main +// WebSocket logic, simplifying where possible. +// +// * Coders hide the differences between coding and flushing stages on the +// API level +// * Verifier abstracts the way the verification is performed +// (spoiler: it's a decoding into a throw-away buffer) +// +// Coding methods throw exceptions instead of returning coding result denoting +// errors, since any kind of handling and recovery is not expected. +final class CharsetToolkit { + + private CharsetToolkit() { } + + static final class Verifier { + + private final CharsetDecoder decoder = UTF_8.newDecoder(); + // A buffer used to check validity of UTF-8 byte stream by decoding it. + // The contents of this buffer are never used. + // The size is arbitrary, though it should probably be chosen from the + // performance perspective since it affects the total number of calls to + // decoder.decode() and amount of work in each of these calls + private final CharBuffer blackHole = CharBuffer.allocate(1024); + + void verify(ByteBuffer in, boolean endOfInput) + throws CharacterCodingException { + while (true) { + // Since decoder.flush() cannot produce an error, it's not + // helpful for verification. Therefore this step is skipped. + CoderResult r = decoder.decode(in, blackHole, endOfInput); + if (r.isOverflow()) { + blackHole.clear(); + } else if (r.isUnderflow()) { + break; + } else if (r.isError()) { + r.throwException(); + } else { + // Should not happen + throw new InternalError(); + } + } + } + + Verifier reset() { + decoder.reset(); + return this; + } + } + + static final class Encoder { + + private final CharsetEncoder encoder = UTF_8.newEncoder(); + private boolean coding = true; + + CoderResult encode(CharBuffer in, ByteBuffer out, boolean endOfInput) + throws CharacterCodingException { + + if (coding) { + CoderResult r = encoder.encode(in, out, endOfInput); + if (r.isOverflow()) { + return r; + } else if (r.isUnderflow()) { + if (endOfInput) { + coding = false; + } else { + return r; + } + } else if (r.isError()) { + r.throwException(); + } else { + // Should not happen + throw new InternalError(); + } + } + assert !coding; + return encoder.flush(out); + } + + Encoder reset() { + coding = true; + encoder.reset(); + return this; + } + } + + static CharBuffer decode(ByteBuffer in) throws CharacterCodingException { + return UTF_8.newDecoder().decode(in); + } + + static final class Decoder { + + private final CharsetDecoder decoder = UTF_8.newDecoder(); + private boolean coding = true; // Either coding or flushing + + CoderResult decode(ByteBuffer in, CharBuffer out, boolean endOfInput) + throws CharacterCodingException { + + if (coding) { + CoderResult r = decoder.decode(in, out, endOfInput); + if (r.isOverflow()) { + return r; + } else if (r.isUnderflow()) { + if (endOfInput) { + coding = false; + } else { + return r; + } + } else if (r.isError()) { + r.throwException(); + } else { + // Should not happen + throw new InternalError(); + } + } + assert !coding; + return decoder.flush(out); + } + + Decoder reset() { + coding = true; + decoder.reset(); + return this; + } + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/ConnectionPool.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/ConnectionPool.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ConnectionPool.java Tue May 03 12:25:20 2016 -0700 @@ -35,7 +35,7 @@ class ConnectionPool { static final long KEEP_ALIVE = Utils.getIntegerNetProperty( - "sun.net.httpclient.keepalive.timeout", 1200); // seconds + "java.net.httpclient.keepalive.timeout", 1200); // seconds // Pools of idle connections diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/ContinuationFrame.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ContinuationFrame.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, 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 + */ + +package java.net.http; + +import java.io.IOException; + +class ContinuationFrame extends HeaderFrame { + + public static final int TYPE = 0x9; + + ContinuationFrame() { + type = TYPE; + } + + @Override + void readIncomingImpl(ByteBufferConsumer bc) throws IOException { + headerBlocks = bc.getBuffers(length); + } + + @Override + void writeOutgoing(ByteBufferGenerator bg) { + super.writeOutgoing(bg); + for (int i=0; i> userheaders, cookies; - userheaders = r.getUserHeaders().directMap(); + userheaders = r.getUserHeaders().map(); cookies = cookieMan.get(r.uri(), userheaders); // add the returned cookies HttpHeadersImpl systemHeaders = r.getSystemHeaders(); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/DataFrame.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/DataFrame.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2015, 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 + */ + +package java.net.http; + +import java.io.IOException; +import java.nio.ByteBuffer; + +class DataFrame extends Http2Frame { + + public final static int TYPE = 0x0; + + DataFrame() { + type = TYPE; + } + + // Flags + public static final int END_STREAM = 0x1; + public static final int PADDED = 0x8; + + int padLength; + int dataLength; + ByteBuffer[] data; + + public void setData(ByteBuffer[] data) { + this.data = data; + setDataLength(); + } + + @Override + String flagAsString(int flag) { + switch (flag) { + case END_STREAM: + return "END_STREAM"; + case PADDED: + return "PADDED"; + } + return super.flagAsString(flag); + } + + public synchronized void setData(ByteBuffer data) { + ByteBuffer[] bb; + if (data == null) { + bb = new ByteBuffer[0]; + } else { + bb = new ByteBuffer[1]; + bb[0] = data; + } + setData(bb); + } + + public synchronized ByteBuffer[] getData() { + return data; + } + + private void setDataLength() { + int len = 0; + for (ByteBuffer buf : data) { + len += buf.remaining(); + } + dataLength = len; + } + + @Override + void readIncomingImpl(ByteBufferConsumer bc) throws IOException { + if ((flags & PADDED) != 0) { + padLength = bc.getByte(); + dataLength = length - (padLength + 1); + } else { + dataLength = length; + } + data = bc.getBuffers(dataLength); + } + + int getPadLength() { + return padLength; + } + + int getDataLength() { + return dataLength; + } + + @Override + void writeOutgoing(ByteBufferGenerator bg) { + super.writeOutgoing(bg); + if ((flags & PADDED) != 0) { + ByteBuffer buf = bg.getBuffer(1); + buf.put((byte)getPadLength()); + } + for (int i=0; i LAST_ERROR) { + return "Error: " + Integer.toString(code); + } else { + return errorStrings[code]; + } + } + + int errorCode; + + @Override + public String toString() { + return super.toString() + " Error: " + stringForCode(errorCode); + } + + public int getErrorCode() { + return this.errorCode; + } + + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/Exchange.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Exchange.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Exchange.java Tue May 03 12:25:20 2016 -0700 @@ -128,7 +128,7 @@ } } - HttpResponseImpl responseImpl0(HttpConnection connection) + private HttpResponseImpl responseImpl0(HttpConnection connection) throws IOException, InterruptedException { exchImpl = ExchangeImpl.get(this, connection); @@ -136,7 +136,7 @@ request.addSystemHeader("Expect", "100-Continue"); exchImpl.sendHeadersOnly(); HttpResponseImpl resp = exchImpl.getResponse(); - logResponse(resp); + Utils.logResponse(resp); if (resp.statusCode() != 100) { return resp; } @@ -145,7 +145,7 @@ } else { exchImpl.sendRequest(); HttpResponseImpl resp = exchImpl.getResponse(); - logResponse(resp); + Utils.logResponse(resp); return checkForUpgrade(resp, exchImpl); } } @@ -163,9 +163,7 @@ } SecurityException e = securityCheck(acc); if (e != null) { - CompletableFuture cf = new CompletableFuture<>(); - cf.completeExceptionally(e); - return cf; + return CompletableFuture.failedFuture(e); } if (permissions.size() > 0) { return AccessController.doPrivileged( @@ -182,9 +180,7 @@ try { exchImpl = ExchangeImpl.get(this, connection); } catch (IOException | InterruptedException e) { - CompletableFuture cf = new CompletableFuture<>(); - cf.completeExceptionally(e); - return cf; + return CompletableFuture.failedFuture(e); } if (request.expectContinue()) { request.addSystemHeader("Expect", "100-Continue"); @@ -200,23 +196,19 @@ return exchImpl.sendBodyAsync() .thenCompose(exchImpl::getResponseAsync) .thenApply((r) -> { - logResponse(r); + Utils.logResponse(r); return r; }); } else { Exchange.this.response = r1; - logResponse(r1); + Utils.logResponse(r1); return CompletableFuture.completedFuture(r1); } }); } else { return exchImpl - .sendHeadersAsync() - .thenCompose((Void v) -> { - // send body and get response at same time - return exchImpl.sendBodyAsync() - .thenCompose(exchImpl::getResponseAsync); - }) + .sendRequestAsync() + .thenCompose(exchImpl::getResponseAsync) .thenCompose((HttpResponseImpl r1) -> { int rcode = r1.statusCode(); CompletableFuture cf = @@ -225,13 +217,13 @@ return cf; } else { Exchange.this.response = r1; - logResponse(r1); + Utils.logResponse(r1); return CompletableFuture.completedFuture(r1); } }) .thenApply((HttpResponseImpl response) -> { this.response = response; - logResponse(response); + Utils.logResponse(response); return response; }); } @@ -254,9 +246,9 @@ client.client2(), this) .thenCompose((Http2Connection c) -> { + c.putConnection(); Stream s = c.getStream(1); exchImpl = s; - c.putConnection(); return s.getResponseAsync(null); }) ); @@ -294,21 +286,6 @@ } - private void logResponse(HttpResponseImpl r) { - if (!Log.requests()) - return; - StringBuilder sb = new StringBuilder(); - String method = r.request().method(); - URI uri = r.uri(); - String uristring = uri == null ? "" : uri.toString(); - sb.append('(') - .append(method) - .append(" ") - .append(uristring) - .append(") ") - .append(Integer.toString(r.statusCode())); - Log.logResponse(sb.toString()); - } CompletableFuture responseBodyAsync(HttpResponse.BodyProcessor processor) { return exchImpl.responseBodyAsync(processor); @@ -352,9 +329,9 @@ } String method = request.method(); - HttpHeadersImpl userHeaders = request.getUserHeaders(); + HttpHeaders userHeaders = request.getUserHeaders(); URI u = getURIForSecurityCheck(); - URLPermission p = Utils.getPermission(u, method, userHeaders.directMap()); + URLPermission p = Utils.getPermission(u, method, userHeaders.map()); try { assert acc != null; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/FilterFactory.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/FilterFactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/FilterFactory.java Tue May 03 12:25:20 2016 -0700 @@ -39,7 +39,9 @@ List l = new LinkedList<>(); for (Class clazz : filterClasses) { try { - l.add(clazz.newInstance()); + @SuppressWarnings("deprecation") + HeaderFilter headerFilter = clazz.newInstance(); + l.add(headerFilter); } catch (ReflectiveOperationException e) { throw new InternalError(e); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/FrameReader.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/FrameReader.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,70 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package java.net.http; + +import java.nio.ByteBuffer; +import java.util.LinkedList; +import java.util.List; + +/** + * Represents one frame. May be initialized with a leftover buffer from previous + * frame. Call {@code haveFrame()} to determine if buffers contains at least one + * frame. If false, the obtain another buffer and call {@code}input(ByteBuffer)}. + * There may be additional bytes at end of the frame list. + */ +class FrameReader { + + final List buffers; + + FrameReader() { + buffers = new LinkedList<>(); + } + + FrameReader(FrameReader that) { + this.buffers = that.buffers; + } + + FrameReader(ByteBuffer remainder) { + buffers = new LinkedList<>(); + if (remainder != null) { + buffers.add(remainder); + } + } + + public synchronized void input(ByteBuffer buffer) { + buffers.add(buffer); + } + + public synchronized boolean haveFrame() { + //buffers = Utils.superCompact(buffers, () -> ByteBuffer.allocate(Utils.BUFSIZE)); + int size = 0; + for (ByteBuffer buffer : buffers) { + size += buffer.remaining(); + } + if (size < 3) { + return false; // don't have length yet + } + // we at least have length field + int length = 0; + int j = 0; + ByteBuffer b = buffers.get(j); + b.mark(); + for (int i=0; i<3; i++) { + while (!b.hasRemaining()) { + b.reset(); + b = buffers.get(++j); + b.mark(); + } + length = (length << 8) + (b.get() & 0xff); + } + b.reset(); + return (size >= length + 9); // frame length + } + + synchronized List frame() { + return buffers; + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/GoAwayFrame.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/GoAwayFrame.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2015, 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 + */ + +package java.net.http; + +import java.io.IOException; +import java.nio.ByteBuffer; + +class GoAwayFrame extends ErrorFrame { + + GoAwayFrame() { + type = TYPE; + } + + int lastStream; + byte[] debugData = new byte[0]; + + public static final int TYPE = 0x7; + + // Flags + public static final int ACK = 0x1; + + public void setDebugData(byte[] debugData) { + this.debugData = debugData; + } + + @Override + public String toString() { + return super.toString() + " Debugdata: " + new String(debugData); + } + + @Override + String flagAsString(int flag) { + switch (flag) { + case ACK: + return "ACK"; + } + return super.flagAsString(flag); + } + + public void setLastStream(int lastStream) { + this.lastStream = lastStream; + } + + public int getLastStream() { + return this.lastStream; + } + + public byte[] getDebugData() { + return debugData; + } + + @Override + void readIncomingImpl(ByteBufferConsumer bc) throws IOException { + if (length < 8) { + throw new IOException("Invalid GoAway frame"); + } + lastStream = bc.getInt() & 0x7fffffff; + errorCode = bc.getInt(); + //debugData = bc.getBytes(8); + int datalen = length - 8; + if (datalen > 0) { + debugData = bc.getBytes(datalen); + Log.logError("GoAway debugData " + new String(debugData)); + } + } + + @Override + void writeOutgoing(ByteBufferGenerator bg) { + super.writeOutgoing(bg); + ByteBuffer buf = bg.getBuffer(length); + buf.putInt(lastStream); + buf.putInt(errorCode); + if (length > 8) { + buf.put(debugData); + } + } + + @Override + void computeLength() { + length = 8 + debugData.length; + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/HeaderFrame.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFrame.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015, 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 + */ + +package java.net.http; + +import java.nio.ByteBuffer; + +/** + * Either a HeadersFrame or a ContinuationFrame + */ +abstract class HeaderFrame extends Http2Frame { + + int offset; + int number; + int headerLength; + ByteBuffer[] headerBlocks; + + public static final int END_HEADERS = 0x4; + + @Override + String flagAsString(int flag) { + switch (flag) { + case END_HEADERS: + return "END_HEADERS"; + } + return super.flagAsString(flag); + } + + /** + * Sets the array of hpack encoded ByteBuffers + */ + public void setHeaderBlock(ByteBuffer bufs[], int offset, int number) { + this.headerBlocks = bufs; + this.offset = offset; + this.number = number; + int length = 0; + for (int i=offset; i request.getAccessControlContext()); + request::getAccessControlContext); operations.add(cf); return cf; } @@ -269,7 +249,7 @@ cf.completeExceptionally(e); connection.close(); } - }, () -> request.getAccessControlContext()); + }, request::getAccessControlContext); operations.add(cf); return cf; } @@ -302,7 +282,7 @@ cf.completeExceptionally(e); connection.close(); } - }, () -> request.getAccessControlContext()); + }, request::getAccessControlContext); operations.add(cf); return cf; } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/Http1Request.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Http1Request.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Request.java Tue May 03 12:25:20 2016 -0700 @@ -30,6 +30,7 @@ import java.util.Map; import java.util.Set; import java.net.InetSocketAddress; +import java.net.http.HttpConnection.Mode; import java.nio.charset.StandardCharsets; import java.util.function.LongConsumer; import static java.nio.charset.StandardCharsets.US_ASCII; @@ -48,7 +49,8 @@ // See line 206 and below for description final ByteBuffer[] buffers; final HttpRequest.BodyProcessor requestProc; - final HttpHeadersImpl userHeaders, systemHeaders; + final HttpHeaders userHeaders; + final HttpHeadersImpl systemHeaders; final LongConsumer flowController; boolean streaming; long contentLength; @@ -91,10 +93,10 @@ private void collectHeaders1(StringBuilder sb, HttpRequestImpl request, - HttpHeadersImpl headers) + HttpHeaders headers) throws IOException { - Map> h = headers.directMap(); + Map> h = headers.map(); Set>> entries = h.entrySet(); for (Map.Entry> entry : entries) { @@ -112,8 +114,6 @@ } } - private static final int BUFSIZE = 64 * 1024; // TODO: configurable? - private String getPathAndQuery(URI uri) { String path = uri.getPath(); String query = uri.getQuery(); @@ -134,6 +134,25 @@ return addr.getHostString() + ":" + addr.getPort(); } + private String hostString() { + URI uri = request.uri(); + int port = uri.getPort(); + String host = uri.getHost(); + + boolean defaultPort; + if (port == -1) + defaultPort = true; + else if (request.secure()) + defaultPort = port == 443; + else + defaultPort = port == 80; + + if (defaultPort) + return host; + else + return host + ":" + Integer.toString(port); + } + private String requestURI() { URI uri = request.uri(); String method = request.method(); @@ -161,6 +180,7 @@ void sendRequest() throws IOException { collectHeaders(); + chan.configureMode(Mode.BLOCKING); if (contentLength == 0) { chan.write(buffers, 0, 2); } else if (contentLength > 0) { @@ -196,7 +216,7 @@ buffers[0] = ByteBuffer.wrap(cmd.getBytes(StandardCharsets.US_ASCII)); URI uri = request.uri(); if (uri != null) { - systemHeaders.setHeader("Host", uri.getHost()); + systemHeaders.setHeader("Host", hostString()); } if (request == null) { // this is not a user request. No content diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/Http1Response.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Http1Response.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Response.java Tue May 03 12:25:20 2016 -0700 @@ -24,7 +24,6 @@ package java.net.http; import java.io.IOException; -import java.net.URI; import java.nio.ByteBuffer; import java.util.List; import java.util.Map; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/Http2ClientImpl.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Http2ClientImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http2ClientImpl.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -23,11 +23,133 @@ */ package java.net.http; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.URI; +import static java.net.http.SettingsFrame.INITIAL_WINDOW_SIZE; +import static java.net.http.SettingsFrame.ENABLE_PUSH; +import static java.net.http.SettingsFrame.HEADER_TABLE_SIZE; +import static java.net.http.SettingsFrame.MAX_CONCURRENT_STREAMS; +import static java.net.http.SettingsFrame.MAX_FRAME_SIZE; +import java.util.Base64; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Http2 specific aspects of HttpClientImpl + */ class Http2ClientImpl { - Http2ClientImpl(HttpClientImpl t) {} - String getSettingsString() {return "";} - void debugPrint() {} - Http2Connection getConnectionFor(HttpRequestImpl r) { - return null; + + final private HttpClientImpl client; + + Http2ClientImpl(HttpClientImpl client) { + this.client = client; + } + + /* Map key is "scheme:host:port" */ + final private Map connections = + Collections.synchronizedMap(new HashMap<>()); + + final private Set opening = Collections.synchronizedSet(new HashSet<>()); + + synchronized boolean haveConnectionFor(URI uri, InetSocketAddress proxy) { + return connections.containsKey(Http2Connection.keyFor(uri,proxy)); + } + + /** + * If a https request then blocks and waits until a connection is opened. + * Returns null if the request is 'http' as a different (upgrade) + * mechanism is used. + * + * Only one connection per destination is created. Blocks when opening + * connection, or when waiting for connection to be opened. + * First thread opens the connection and notifies the others when done. + * + * If the request is secure (https) then we open the connection here. + * If not, then the more complicated upgrade from 1.1 to 2 happens (not here) + * In latter case, when the Http2Connection is connected, putConnection() must + * be called to store it. + */ + Http2Connection getConnectionFor(HttpRequestImpl req) + throws IOException, InterruptedException { + URI uri = req.uri(); + InetSocketAddress proxy = req.proxy(); + String key = Http2Connection.keyFor(uri, proxy); + Http2Connection connection; + synchronized (opening) { + while ((connection = connections.get(key)) == null) { + if (!req.secure()) { + return null; + } + if (!opening.contains(key)) { + opening.add(key); + break; + } else { + opening.wait(); + } + } + } + if (connection != null) { + return connection; + } + // we are opening the connection here blocking until it is done. + connection = new Http2Connection(req); + synchronized (opening) { + connections.put(key, connection); + opening.remove(key); + opening.notifyAll(); + } + return connection; + } + + + /* + * TODO: If there isn't a connection to the same destination, then + * store it. If there is already a connection, then close it + */ + synchronized void putConnection(Http2Connection c) { + String key = c.key(); + connections.put(key, c); + } + + synchronized void deleteConnection(Http2Connection c) { + String key = c.key(); + connections.remove(key); + } + + HttpClientImpl client() { + return client; + } + + /** Returns the client settings as a base64 (url) encoded string */ + String getSettingsString() { + SettingsFrame sf = getClientSettings(); + ByteBufferGenerator bg = new ByteBufferGenerator(client); + sf.writeOutgoing(bg); + byte[] settings = bg.asByteArray(9); // without the header + Base64.Encoder encoder = Base64.getUrlEncoder() + .withoutPadding(); + return encoder.encodeToString(settings); + } + + private static final int K = 1024; + + SettingsFrame getClientSettings() { + SettingsFrame frame = new SettingsFrame(); + frame.setParameter(HEADER_TABLE_SIZE, Utils.getIntegerNetProperty( + "java.net.httpclient.hpack.maxheadertablesize", 16 * K)); + frame.setParameter(ENABLE_PUSH, Utils.getIntegerNetProperty( + "java.net.httpclient.enablepush", 1)); + frame.setParameter(MAX_CONCURRENT_STREAMS, Utils.getIntegerNetProperty( + "java.net.httpclient.maxstreams", 16)); + frame.setParameter(INITIAL_WINDOW_SIZE, Utils.getIntegerNetProperty( + "java.net.httpclient.windowsize", 32 * K)); + frame.setParameter(MAX_FRAME_SIZE, Utils.getIntegerNetProperty( + "java.net.httpclient.maxframesize", 16 * K)); + frame.computeLength(); + return frame; } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/Http2Connection.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Http2Connection.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http2Connection.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -24,42 +24,767 @@ package java.net.http; import java.io.IOException; -import java.net.Authenticator; -import java.net.CookieManager; -import java.net.ProxySelector; +import java.net.InetSocketAddress; import java.net.URI; -import static java.net.http.Utils.BUFSIZE; +import java.net.http.HttpConnection.Mode; import java.nio.ByteBuffer; -import java.nio.channels.SelectableChannel; -import java.nio.channels.SelectionKey; -import static java.nio.channels.SelectionKey.OP_CONNECT; -import static java.nio.channels.SelectionKey.OP_READ; -import static java.nio.channels.SelectionKey.OP_WRITE; -import java.nio.channels.Selector; +import java.nio.charset.StandardCharsets; +import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; -import java.util.Set; -import java.util.concurrent.*; -import java.security.NoSuchAlgorithmException; -import java.util.ListIterator; -import java.util.Optional; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLParameters; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import sun.net.httpclient.hpack.Encoder; +import sun.net.httpclient.hpack.Decoder; +import static java.net.http.SettingsFrame.*; +import static java.net.http.Utils.BUFSIZE; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Formatter; +import java.util.stream.Collectors; +import sun.net.httpclient.hpack.DecodingCallback; + +/** + * An Http2Connection. Encapsulates the socket(channel) and any SSLEngine used + * over it. Contains an HttpConnection which hides the SocketChannel SSL stuff. + * + * Http2Connections belong to a Http2ClientImpl, (one of) which belongs + * to a HttpClientImpl. + * + * Creation cases: + * 1) upgraded HTTP/1.1 plain tcp connection + * 2) prior knowledge directly created plain tcp connection + * 3) directly created HTTP/2 SSL connection which uses ALPN. + * + * Sending is done by writing directly to underlying HttpConnection object which + * is operating in async mode. No flow control applies on output at this level + * and all writes are just executed as puts to an output Q belonging to HttpConnection + * Flow control is implemented by HTTP/2 protocol itself. + * + * Hpack header compression + * and outgoing stream creation is also done here, because these operations + * must be synchronized at the socket level. Stream objects send frames simply + * by placing them on the connection's output Queue. sendFrame() is called + * from a higher level (Stream) thread. + * + * asyncReceive(ByteBuffer) is always called from the selector thread. It assembles + * incoming Http2Frames, and directs them to the appropriate Stream.incoming() + * or handles them directly itself. This thread performs hpack decompression + * and incoming stream creation (Server push). Incoming frames destined for a + * stream are provided by calling Stream.incoming(). + */ +class Http2Connection implements BufferHandler { + + final Queue outputQ; + volatile boolean closed; + + //------------------------------------- + final HttpConnection connection; + HttpClientImpl client; + final Http2ClientImpl client2; + Map streams; + int nextstreamid = 3; // stream 1 is registered separately + int nextPushStream = 2; + Encoder hpackOut; + Decoder hpackIn; + SettingsFrame clientSettings, serverSettings; + ByteBufferConsumer bbc; + final LinkedList freeList; + final String key; // for HttpClientImpl.connections map + FrameReader reader; + + // Connection level flow control windows + int sendWindow = INITIAL_WINDOW_SIZE; + + final static int DEFAULT_FRAME_SIZE = 16 * 1024; + private static ByteBuffer[] empty = Utils.EMPTY_BB_ARRAY; + + final ExecutorWrapper executor; + + /** + * This is established by the protocol spec and the peer will update it with + * WINDOW_UPDATEs, which affects the sendWindow. + */ + final static int INITIAL_WINDOW_SIZE = 64 * 1024 - 1; + + // TODO: need list of control frames from other threads + // that need to be sent + + /** + * Case 1) Create from upgraded HTTP/1.1 connection. + * Is ready to use. Will not be SSL. exchange is the Exchange + * that initiated the connection, whose response will be delivered + * on a Stream. + */ + Http2Connection(HttpConnection connection, Http2ClientImpl client2, + Exchange exchange) throws IOException, InterruptedException { + this.outputQ = new Queue<>(); + String msg = "Connection send window size " + Integer.toString(sendWindow); + Log.logTrace(msg); + + //this.initialExchange = exchange; + assert !(connection instanceof SSLConnection); + this.connection = connection; + this.client = client2.client(); + this.client2 = client2; + this.executor = client.executorWrapper(); + this.freeList = new LinkedList<>(); + this.key = keyFor(connection); + streams = Collections.synchronizedMap(new HashMap<>()); + initCommon(); + //sendConnectionPreface(); + Stream initialStream = createStream(exchange); + initialStream.registerStream(1); + initialStream.requestSent(); + sendConnectionPreface(); + connection.configureMode(Mode.ASYNC); + // start reading and writing + // start reading + AsyncConnection asyncConn = (AsyncConnection)connection; + asyncConn.setAsyncCallbacks(this::asyncReceive, this::shutdown); + asyncReceive(connection.getRemaining()); + asyncConn.startReading(); + } + + // async style but completes immediately + static CompletableFuture createAsync(HttpConnection connection, + Http2ClientImpl client2, Exchange exchange) { + CompletableFuture cf = new CompletableFuture<>(); + try { + Http2Connection c = new Http2Connection(connection, client2, exchange); + cf.complete(c); + } catch (IOException | InterruptedException e) { + cf.completeExceptionally(e); + } + return cf; + } + + /** + * Cases 2) 3) + * + * request is request to be sent. + */ + Http2Connection(HttpRequestImpl request) throws IOException, InterruptedException { + InetSocketAddress proxy = request.proxy(); + URI uri = request.uri(); + InetSocketAddress addr = Utils.getAddress(request); + String msg = "Connection send window size " + Integer.toString(sendWindow); + Log.logTrace(msg); + this.key = keyFor(uri, proxy); + this.connection = HttpConnection.getConnection(addr, request, this); + streams = Collections.synchronizedMap(new HashMap<>()); + this.client = request.client(); + this.client2 = client.client2(); + this.executor = client.executorWrapper(); + this.freeList = new LinkedList<>(); + this.outputQ = new Queue<>(); + nextstreamid = 1; + initCommon(); + connection.connect(); + connection.configureMode(Mode.ASYNC); + // start reading + AsyncConnection asyncConn = (AsyncConnection)connection; + asyncConn.setAsyncCallbacks(this::asyncReceive, this::shutdown); + sendConnectionPreface(); + asyncConn.startReading(); + } + + // NEW + synchronized void obtainSendWindow(int amount) throws InterruptedException { + while (amount > 0) { + int n = Math.min(amount, sendWindow); + sendWindow -= n; + amount -= n; + if (amount > 0) + wait(); + } + } + + synchronized void updateSendWindow(int amount) { + if (sendWindow == 0) { + sendWindow += amount; + notifyAll(); + } else + sendWindow += amount; + } + + synchronized int sendWindow() { + return sendWindow; + } + + static String keyFor(HttpConnection connection) { + boolean isProxy = connection.isProxied(); + boolean isSecure = connection.isSecure(); + InetSocketAddress addr = connection.address(); + + return keyString(isSecure, isProxy, addr.getHostString(), addr.getPort()); + } + + static String keyFor(URI uri, InetSocketAddress proxy) { + boolean isSecure = uri.getScheme().equalsIgnoreCase("https"); + boolean isProxy = proxy != null; + + String host; + int port; + + if (isProxy) { + host = proxy.getHostString(); + port = proxy.getPort(); + } else { + host = uri.getHost(); + port = uri.getPort(); + } + return keyString(isSecure, isProxy, host, port); + } + + // {C,S}:{H:P}:host:port + // C indicates clear text connection "http" + // S indicates secure "https" + // H indicates host (direct) connection + // P indicates proxy + // Eg: "S:H:foo.com:80" + static String keyString(boolean secure, boolean proxy, String host, int port) { + char c1 = secure ? 'S' : 'C'; + char c2 = proxy ? 'P' : 'H'; + + StringBuilder sb = new StringBuilder(128); + sb.append(c1).append(':').append(c2).append(':') + .append(host).append(':').append(port); + return sb.toString(); + } + + String key() { + return this.key; + } + + void putConnection() { + client2.putConnection(this); + } + + private static String toHexdump1(ByteBuffer bb) { + bb.mark(); + StringBuilder sb = new StringBuilder(512); + Formatter f = new Formatter(sb); + + while (bb.hasRemaining()) { + int i = Byte.toUnsignedInt(bb.get()); + f.format("%02x:", i); + } + sb.deleteCharAt(sb.length()-1); + bb.reset(); + return sb.toString(); + } + + private static String toHexdump(ByteBuffer bb) { + List words = new ArrayList<>(); + int i = 0; + bb.mark(); + while (bb.hasRemaining()) { + if (i % 2 == 0) { + words.add(""); + } + byte b = bb.get(); + String hex = Integer.toHexString(256 + Byte.toUnsignedInt(b)).substring(1); + words.set(i / 2, words.get(i / 2) + hex); + i++; + } + bb.reset(); + return words.stream().collect(Collectors.joining(" ")); + } + + private void decodeHeaders(HeaderFrame frame, DecodingCallback decoder) { + boolean endOfHeaders = frame.getFlag(HeaderFrame.END_HEADERS); + + ByteBuffer[] buffers = frame.getHeaderBlock(); + for (int i = 0; i < buffers.length; i++) { + hpackIn.decode(buffers[i], endOfHeaders, decoder); + } + } + + int getInitialSendWindowSize() { + return serverSettings.getParameter(SettingsFrame.INITIAL_WINDOW_SIZE); + } + + void close() { + GoAwayFrame f = new GoAwayFrame(); + f.setDebugData("Requested by user".getBytes()); + // TODO: set last stream. For now zero ok. + sendFrame(f); + } + + // BufferHandler methods + + @Override + public ByteBuffer getBuffer(int n) { + return client.getBuffer(n); + } + + @Override + public void returnBuffer(ByteBuffer buf) { + client.returnBuffer(buf); + } -class Http2Connection { - static CompletableFuture createAsync( - HttpConnection connection, Http2ClientImpl client2, Exchange exchange) { - return null; + @Override + public void setMinBufferSize(int n) { + client.setMinBufferSize(n); + } + + private final Object readlock = new Object(); + + void asyncReceive(ByteBuffer buffer) { + synchronized (readlock) { + try { + if (reader == null) { + reader = new FrameReader(buffer); + } else { + reader.input(buffer); + } + while (true) { + if (reader.haveFrame()) { + List buffers = reader.frame(); + + ByteBufferConsumer bbc = new ByteBufferConsumer(buffers, this::getBuffer); + processFrame(bbc); + if (bbc.consumed()) { + reader = new FrameReader(); + return; + } else { + reader = new FrameReader(reader); + } + } else + return; + } + } catch (Throwable e) { + String msg = Utils.stackTrace(e); + Log.logTrace(msg); + shutdown(e); + } + } + } + + void shutdown(Throwable t) { + System.err.println("Shutdown: " + t); + t.printStackTrace(); + closed = true; + client2.deleteConnection(this); + Collection c = streams.values(); + for (Stream s : c) { + s.cancelImpl(t); + } + connection.close(); + } + + /** + * Handles stream 0 (common) frames that apply to whole connection and passes + * other stream specific frames to that Stream object. + * + * Invokes Stream.incoming() which is expected to process frame without + * blocking. + */ + void processFrame(ByteBufferConsumer bbc) throws IOException, InterruptedException { + Http2Frame frame = Http2Frame.readIncoming(bbc); + Log.logFrames(frame, "IN"); + int streamid = frame.streamid(); + if (streamid == 0) { + handleCommonFrame(frame); + } else { + Stream stream = getStream(streamid); + if (stream == null) { + // should never receive a frame with unknown stream id + resetStream(streamid, ResetFrame.PROTOCOL_ERROR); + } + if (frame instanceof PushPromiseFrame) { + PushPromiseFrame pp = (PushPromiseFrame)frame; + handlePushPromise(stream, pp); + } else if (frame instanceof HeaderFrame) { + // decode headers (or continuation) + decodeHeaders((HeaderFrame) frame, stream.rspHeadersConsumer()); + stream.incoming(frame); + } else + stream.incoming(frame); + } + } + + private void handlePushPromise(Stream parent, PushPromiseFrame pp) + throws IOException, InterruptedException { + + HttpRequestImpl parentReq = parent.request; + int promisedStreamid = pp.getPromisedStream(); + if (promisedStreamid != nextPushStream) { + resetStream(promisedStreamid, ResetFrame.PROTOCOL_ERROR); + return; + } else { + nextPushStream += 2; + } + HeaderDecoder decoder = new HeaderDecoder(); + decodeHeaders(pp, decoder); + HttpHeadersImpl headers = decoder.headers(); + HttpRequestImpl pushReq = HttpRequestImpl.createPushRequest(parentReq, headers); + + Stream.PushedStream pushStream = createPushStream(parent, pushReq); + pushStream.registerStream(promisedStreamid); + parent.incoming_pushPromise(pushReq, pushStream); + } + + private void handleCommonFrame(Http2Frame frame) + throws IOException, InterruptedException { + + switch (frame.type()) { + case SettingsFrame.TYPE: + { SettingsFrame f = (SettingsFrame)frame; + handleSettings(f);} + break; + case PingFrame.TYPE: + { PingFrame f = (PingFrame)frame; + handlePing(f);} + break; + case GoAwayFrame.TYPE: + { GoAwayFrame f = (GoAwayFrame)frame; + handleGoAway(f);} + break; + case WindowUpdateFrame.TYPE: + { WindowUpdateFrame f = (WindowUpdateFrame)frame; + handleWindowUpdate(f);} + break; + default: + protocolError(ErrorFrame.PROTOCOL_ERROR); + } + } + + void resetStream(int streamid, int code) throws IOException, InterruptedException { + Log.logError( + "Resetting stream {0,number,integer} with error code {1,number,integer}", + streamid, code); + ResetFrame frame = new ResetFrame(); + frame.streamid(streamid); + frame.setErrorCode(code); + sendFrame(frame); + streams.remove(streamid); + } + + private void handleWindowUpdate(WindowUpdateFrame f) + throws IOException, InterruptedException { + updateSendWindow(f.getUpdate()); + } + + private void protocolError(int errorCode) + throws IOException, InterruptedException { + GoAwayFrame frame = new GoAwayFrame(); + frame.setErrorCode(errorCode); + sendFrame(frame); + String msg = "Error code: " + errorCode; + shutdown(new IOException("protocol error")); + } + + private void handleSettings(SettingsFrame frame) + throws IOException, InterruptedException { + if (frame.getFlag(SettingsFrame.ACK)) { + // ignore ack frames for now. + return; + } + serverSettings = frame; + SettingsFrame ack = getAckFrame(frame.streamid()); + sendFrame(ack); + } + + private void handlePing(PingFrame frame) + throws IOException, InterruptedException { + frame.setFlag(PingFrame.ACK); + sendFrame(frame); + } + + private void handleGoAway(GoAwayFrame frame) + throws IOException, InterruptedException { + //System.err.printf("GoAWAY: %s\n", ErrorFrame.stringForCode(frame.getErrorCode())); + shutdown(new IOException("GOAWAY received")); + } + + private void initCommon() { + clientSettings = client2.getClientSettings(); + + // serverSettings will be updated by server + serverSettings = SettingsFrame.getDefaultSettings(); + hpackOut = new Encoder(serverSettings.getParameter(HEADER_TABLE_SIZE)); + hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE)); + } + + /** + * Max frame size we are allowed to send + */ + public int getMaxSendFrameSize() { + int param = serverSettings.getParameter(MAX_FRAME_SIZE); + if (param == -1) { + param = DEFAULT_FRAME_SIZE; + } + return param; + } + + /** + * Max frame size we will receive + */ + public int getMaxReceiveFrameSize() { + return clientSettings.getParameter(MAX_FRAME_SIZE); + } + + // Not sure how useful this is. + public int getMaxHeadersSize() { + return serverSettings.getParameter(MAX_HEADER_LIST_SIZE); + } + + private static final String CLIENT_PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; + + private static final byte[] PREFACE_BYTES = + CLIENT_PREFACE.getBytes(StandardCharsets.ISO_8859_1); + + /** + * Sends Connection preface and Settings frame with current preferred + * values + */ + private void sendConnectionPreface() throws IOException { + ByteBufferGenerator bg = new ByteBufferGenerator(this); + bg.getBuffer(PREFACE_BYTES.length).put(PREFACE_BYTES); + ByteBuffer[] ba = bg.getBufferArray(); + connection.write(ba, 0, ba.length); + + bg = new ByteBufferGenerator(this); + SettingsFrame sf = client2.getClientSettings(); + Log.logFrames(sf, "OUT"); + sf.writeOutgoing(bg); + WindowUpdateFrame wup = new WindowUpdateFrame(); + wup.streamid(0); + // send a Window update for the receive buffer we are using + // minus the initial 64 K specified in protocol + wup.setUpdate(client2.client().getReceiveBufferSize() - (64 * 1024 - 1)); + wup.computeLength(); + wup.writeOutgoing(bg); + Log.logFrames(wup, "OUT"); + ba = bg.getBufferArray(); + connection.write(ba, 0, ba.length); + } + + /** + * Returns an existing Stream with given id, or null if doesn't exist + */ + Stream getStream(int streamid) { + return streams.get(streamid); + } + + /** + * Creates Stream with given id. + */ + Stream createStream(Exchange exchange) { + Stream stream = new Stream(client, this, exchange); + return stream; + } + + Stream.PushedStream createPushStream(Stream parent, HttpRequestImpl pushReq) { + Stream.PushGroup pg = parent.request.pushGroup(); + return new Stream.PushedStream(pg, client, this, parent, pushReq); + } + + void putStream(Stream stream, int streamid) { + streams.put(streamid, stream); + } + + void deleteStream(Stream stream) { + streams.remove(stream.streamid); + } + + static final int MAX_STREAM = Integer.MAX_VALUE - 2; + + // Number of header bytes in a Headers Frame + final static int HEADERS_HEADER_SIZE = 15; + + // Number of header bytes in a Continuation frame + final static int CONTIN_HEADER_SIZE = 9; + + /** + * Encode the headers into a List and then create HEADERS + * and CONTINUATION frames from the list and return the List. + * + * @param frame + * @return + */ + private LinkedList encodeHeaders(OutgoingHeaders frame) { + LinkedList buffers = new LinkedList<>(); + ByteBuffer buf = getBuffer(); + buffers.add(buf); + encodeHeadersImpl(frame.stream.getRequestPseudoHeaders(), buffers); + encodeHeadersImpl(frame.getUserHeaders(), buffers); + encodeHeadersImpl(frame.getSystemHeaders(), buffers); + + for (ByteBuffer b : buffers) { + b.flip(); } - Http2Connection(HttpConnection connection, Http2ClientImpl client2, - Exchange exchange) throws IOException, InterruptedException { + LinkedList frames = new LinkedList<>(); + int maxframesize = getMaxSendFrameSize(); + + HeadersFrame oframe = new HeadersFrame(); + oframe.setFlags(frame.getFlags()); + oframe.streamid(frame.streamid()); + + oframe.setHeaderBlock(getBufferArray(buffers, maxframesize)); + frames.add(oframe); + // Any buffers left? + boolean done = buffers.isEmpty(); + if (done) { + oframe.setFlag(HeaderFrame.END_HEADERS); + } else { + ContinuationFrame cf = null; + while (!done) { + cf = new ContinuationFrame(); + cf.streamid(frame.streamid()); + cf.setHeaderBlock(getBufferArray(buffers, maxframesize)); + frames.add(cf); + done = buffers.isEmpty(); + } + cf.setFlag(HeaderFrame.END_HEADERS); + } + return frames; + } + + // should always return at least one buffer + private static ByteBuffer[] getBufferArray(LinkedList list, int maxsize) { + assert maxsize >= BUFSIZE; + LinkedList newlist = new LinkedList<>(); + int size = list.size(); + int nbytes = 0; + for (int i=0; i buffers) { + ByteBuffer buffer; + if (!(buffer = buffers.getLast()).hasRemaining()) { + buffer = getBuffer(); + buffers.add(buffer); + } + for (Map.Entry> e : hdrs.map().entrySet()) { + String key = e.getKey(); + String lkey = key.toLowerCase(); + List values = e.getValue(); + for (String value : values) { + hpackOut.header(lkey, value); + boolean encoded = false; + do { + encoded = hpackOut.encode(buffer); + if (!encoded) { + buffer = getBuffer(); + buffers.add(buffer); + } + } while (!encoded); + } + } + } + + public void sendFrames(List frames) throws IOException, InterruptedException { + for (Http2Frame frame : frames) { + sendFrame(frame); + } } - Stream getStream(int i) {return null;} - Stream createStream(Exchange ex) {return null;} - void putConnection() {} + static Throwable getExceptionFrom(CompletableFuture cf) { + try { + cf.get(); + return null; + } catch (Throwable e) { + if (e.getCause() != null) + return e.getCause(); + else + return e; + } + } + + + void execute(Runnable r) { + executor.execute(r, null); + } + + private final Object sendlock = new Object(); + + /** + * + */ + void sendFrame(Http2Frame frame) { + synchronized (sendlock) { + try { + if (frame instanceof OutgoingHeaders) { + OutgoingHeaders oh = (OutgoingHeaders) frame; + Stream stream = oh.getStream(); + stream.registerStream(nextstreamid); + oh.streamid(nextstreamid); + nextstreamid += 2; + // set outgoing window here. This allows thread sending + // body to proceed. + stream.updateOutgoingWindow(getInitialSendWindowSize()); + LinkedList frames = encodeHeaders(oh); + for (Http2Frame f : frames) { + sendOneFrame(f); + } + } else { + sendOneFrame(frame); + } + + } catch (IOException e) { + if (!closed) { + Log.logError(e); + shutdown(e); + } + } + } + } + + /** + * Send a frame. + * + * @param frame + * @throws IOException + */ + private void sendOneFrame(Http2Frame frame) throws IOException { + ByteBufferGenerator bbg = new ByteBufferGenerator(this); + frame.computeLength(); + Log.logFrames(frame, "OUT"); + frame.writeOutgoing(bbg); + ByteBuffer[] currentBufs = bbg.getBufferArray(); + connection.write(currentBufs, 0, currentBufs.length); + } + + + private SettingsFrame getAckFrame(int streamid) { + SettingsFrame frame = new SettingsFrame(); + frame.setFlag(SettingsFrame.ACK); + frame.streamid(streamid); + return frame; + } + + static class HeaderDecoder implements DecodingCallback { + HttpHeadersImpl headers; + + HeaderDecoder() { + this.headers = new HttpHeadersImpl(); + } + + @Override + public void onDecoded(CharSequence name, CharSequence value) { + headers.addHeader(name.toString(), value.toString()); + } + + HttpHeadersImpl headers() { + return headers; + } + } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/Http2Frame.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http2Frame.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2015, 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 + */ +package java.net.http; + +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * When sending a frame, the length field must be set in sub-class + * by calling computeLength() + */ +abstract class Http2Frame { + + int length = -1; + int type; + int streamid; + int flags; + + // called when reading in only + void initCommon(int length, int type, int streamid, int flags) { + this.length = length; + this.type = type; + this.streamid = streamid; + this.flags = flags; + } + + public int length() { + return length; + } + + public int type() { + return type; + } + + public int streamid() { + return streamid; + } + + public void setFlag(int flag) { + flags |= flag; + } + + public void setFlags(int flags) { + this.flags = flags; + } + + public int getFlags() { + return flags; + } + + public boolean getFlag(int flag) { + return (flags & flag) != 0; + } + + public void clearFlag(int flag) { + flags &= 0xffffffff ^ flag; + } + + public void streamid(int streamid) { + this.streamid = streamid; + } + + abstract void readIncomingImpl(ByteBufferConsumer bc) throws IOException; + + /** + * assume given array contains at least one complete frame. + */ + static Http2Frame readIncoming(ByteBufferConsumer bc) throws IOException { + int x = bc.getInt(); + int length = x >> 8; + int type = x & 0xff; + int flags = bc.getByte(); + int streamid = bc.getInt(); + Http2Frame f = null; + switch (type) { + case DataFrame.TYPE: + f = new DataFrame(); + break; + case HeadersFrame.TYPE: + f = new HeadersFrame(); + break; + case ContinuationFrame.TYPE: + f = new ContinuationFrame(); + break; + case ResetFrame.TYPE: + f = new ResetFrame(); + break; + case PriorityFrame.TYPE: + f = new PriorityFrame(); + break; + case SettingsFrame.TYPE: + f = new SettingsFrame(); + break; + case GoAwayFrame.TYPE: + f = new GoAwayFrame(); + break; + case PingFrame.TYPE: + f = new PingFrame(); + break; + case PushPromiseFrame.TYPE: + f = new PushPromiseFrame(); + break; + case WindowUpdateFrame.TYPE: + f = new WindowUpdateFrame(); + break; + default: + String msg = Integer.toString(type); + throw new IOException("unknown frame type " + msg); + } + f.initCommon(length, type, streamid, flags); + f.readIncomingImpl(bc); + return f; + } + + public String typeAsString() { + return asString(this.type); + } + + public static String asString(int type) { + switch (type) { + case DataFrame.TYPE: + return "DATA"; + case HeadersFrame.TYPE: + return "HEADERS"; + case ContinuationFrame.TYPE: + return "CONTINUATION"; + case ResetFrame.TYPE: + return "RESET"; + case PriorityFrame.TYPE: + return "PRIORITY"; + case SettingsFrame.TYPE: + return "SETTINGS"; + case GoAwayFrame.TYPE: + return "GOAWAY"; + case PingFrame.TYPE: + return "PING"; + case PushPromiseFrame.TYPE: + return "PUSH_PROMISE"; + case WindowUpdateFrame.TYPE: + return "WINDOW_UPDATE"; + default: + return "UNKNOWN"; + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(typeAsString()) + .append(": length=") + .append(Integer.toString(length)) + .append(", streamid=") + .append(streamid) + .append(", flags="); + + int f = flags; + int i = 0; + if (f == 0) { + sb.append("0 "); + } else { + while (f != 0) { + if ((f & 1) == 1) { + sb.append(flagAsString(1 << i)) + .append(' '); + } + f = f >> 1; + i++; + } + } + return sb.toString(); + } + + // Override + String flagAsString(int f) { + return "unknown"; + } + + abstract void computeLength(); + + void writeOutgoing(ByteBufferGenerator bg) { + if (length == -1) { + throw new InternalError("Length not set on outgoing frame"); + } + ByteBuffer buf = bg.getBuffer(9); + int x = (length << 8) + type; + buf.putInt(x); + buf.put((byte)flags); + buf.putInt(streamid); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/HttpClientImpl.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientImpl.java Tue May 03 12:25:20 2016 -0700 @@ -23,28 +23,32 @@ */ package java.net.http; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; import java.io.IOException; import java.net.Authenticator; import java.net.CookieManager; import java.net.ProxySelector; import java.net.URI; -import static java.net.http.Utils.BUFSIZE; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; -import static java.nio.channels.SelectionKey.OP_CONNECT; -import static java.nio.channels.SelectionKey.OP_READ; -import static java.nio.channels.SelectionKey.OP_WRITE; import java.nio.channels.Selector; -import java.util.*; -import java.util.stream.Stream; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.ExecutorService; -import java.security.NoSuchAlgorithmException; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLParameters; +import java.util.stream.Stream; + +import static java.net.http.Utils.BUFSIZE; /** * Client implementation. Contains all configuration information and also @@ -53,6 +57,9 @@ */ class HttpClientImpl extends HttpClient implements BufferHandler { + private static final ThreadFactory defaultFactory = + (r -> new Thread(null, r, "HttpClient_worker", 0, true)); + private final CookieManager cookieManager; private final Redirect followRedirects; private final ProxySelector proxySelector; @@ -67,7 +74,6 @@ private final SelectorManager selmgr; private final FilterFactory filters; private final Http2ClientImpl client2; - private static final ThreadFactory defaultFactory = Executors.defaultThreadFactory(); private final LinkedList timeouts; public static HttpClientImpl create(HttpClientBuilderImpl builder) { @@ -115,7 +121,6 @@ throw new InternalError(e); } selmgr.setDaemon(true); - selmgr.setName("HttpSelector"); filters = new FilterFactory(); initFilters(); } @@ -135,7 +140,7 @@ * 4) - mark connection as blocking * 5) - call AsyncEvent.handle() * - * If exchange needs to block again, then call registerEvent() again + * If exchange needs to block again, then call registerEvent() again */ void registerEvent(AsyncEvent exchange) throws IOException { selmgr.register(exchange); @@ -145,35 +150,56 @@ return client2; } - LinkedList freelist = new LinkedList<>(); + /** + * We keep one size of buffer on free list. That size may increase + * depending on demand. If that happens we dispose of free buffers + * that are smaller than new size. + */ + private final LinkedList freelist = new LinkedList<>(); + int currentSize = BUFSIZE; @Override - public synchronized ByteBuffer getBuffer() { - if (freelist.isEmpty()) { - return ByteBuffer.allocate(BUFSIZE); + public synchronized ByteBuffer getBuffer(int size) { + + ByteBuffer buf; + if (size == -1) + size = currentSize; + + if (size > currentSize) + currentSize = size; + + while (!freelist.isEmpty()) { + buf = freelist.removeFirst(); + if (buf.capacity() < currentSize) + continue; + buf.clear(); + return buf; } - return freelist.removeFirst(); + return ByteBuffer.allocate(size); } @Override public synchronized void returnBuffer(ByteBuffer buffer) { - buffer.clear(); freelist.add(buffer); } + @Override + public synchronized void setMinBufferSize(int n) { + currentSize = Math.max(n, currentSize); + } // Main loop for this client's selector + private final class SelectorManager extends Thread { - class SelectorManager extends Thread { - final Selector selector; - boolean closed; - - final List readyList; - final List registrations; + private final Selector selector; + private volatile boolean closed; + private final List readyList; + private final List registrations; SelectorManager() throws IOException { - readyList = new LinkedList<>(); - registrations = new LinkedList<>(); + super(null, null, "SelectorManager", 0, false); + readyList = new ArrayList<>(); + registrations = new ArrayList<>(); selector = Selector.open(); } @@ -193,32 +219,13 @@ closed = true; try { selector.close(); - } catch (IOException e) {} - } - - private List copy(List list) { - LinkedList c = new LinkedList<>(); - for (AsyncEvent e : list) { - c.add(e); - } - return c; - } - - String opvals(int i) { - StringBuilder sb = new StringBuilder(); - if ((i & OP_READ) != 0) - sb.append("OP_READ "); - if ((i & OP_CONNECT) != 0) - sb.append("OP_CONNECT "); - if ((i & OP_WRITE) != 0) - sb.append("OP_WRITE "); - return sb.toString(); + } catch (IOException ignored) { } } @Override public void run() { try { - while (true) { + while (!Thread.currentThread().isInterrupted()) { synchronized (this) { for (AsyncEvent exchange : registrations) { SelectableChannel c = exchange.channel(); @@ -229,7 +236,7 @@ if (key == null) { sa = new SelectorAttachment(c, selector); } else { - sa = (SelectorAttachment)key.attachment(); + sa = (SelectorAttachment) key.attachment(); } sa.register(exchange); } catch (IOException e) { @@ -243,6 +250,7 @@ } long timeval = getTimeoutValue(); long now = System.currentTimeMillis(); + //debugPrint(selector); int n = selector.select(timeval); if (n == 0) { signalTimeouts(now); @@ -251,7 +259,7 @@ Set keys = selector.selectedKeys(); for (SelectionKey key : keys) { - SelectorAttachment sa = (SelectorAttachment)key.attachment(); + SelectorAttachment sa = (SelectorAttachment) key.attachment(); int eventsOccurred = key.readyOps(); sa.events(eventsOccurred).forEach(readyList::add); sa.resetInterestOps(eventsOccurred); @@ -260,10 +268,8 @@ selector.selectedKeys().clear(); for (AsyncEvent exchange : readyList) { - if (exchange instanceof AsyncEvent.Blocking) { + if (exchange.blocking()) { exchange.channel().configureBlocking(true); - } else { - assert exchange instanceof AsyncEvent.NonBlocking; } executor.synchronize(); handleEvent(exchange); // will be delegated to executor @@ -272,14 +278,26 @@ } } catch (Throwable e) { if (!closed) { - System.err.println("HttpClientImpl terminating on error"); // This terminates thread. So, better just print stack trace String err = Utils.stackTrace(e); Log.logError("HttpClientImpl: fatal error: " + err); } + } finally { + shutdown(); } } + void debugPrint(Selector selector) { + System.err.println("Selector: debugprint start"); + Set keys = selector.keys(); + for (SelectionKey key : keys) { + SelectableChannel c = key.channel(); + int ops = key.interestOps(); + System.err.printf("selector chan:%s ops:%d\n", c, ops); + } + System.err.println("Selector: debugprint end"); + } + void handleEvent(AsyncEvent e) { if (closed) { e.abort(); @@ -303,7 +321,7 @@ private final SelectableChannel chan; private final Selector selector; private final ArrayList pending; - private int interestops; + private int interestOps; SelectorAttachment(SelectableChannel chan, Selector selector) { this.pending = new ArrayList<>(); @@ -312,53 +330,53 @@ } void register(AsyncEvent e) throws ClosedChannelException { - int newops = e.interestOps(); - boolean reRegister = (interestops & newops) != newops; - interestops |= newops; + int newOps = e.interestOps(); + boolean reRegister = (interestOps & newOps) != newOps; + interestOps |= newOps; pending.add(e); if (reRegister) { // first time registration happens here also - chan.register(selector, interestops, this); + chan.register(selector, interestOps, this); } } - int interestOps() { - return interestops; - } - /** * Returns a Stream containing only events that are - * registered with the given {@code interestop}. + * registered with the given {@code interestOps}. */ - Stream events(int interestop) { + Stream events(int interestOps) { return pending.stream() - .filter(ev -> (ev.interestOps() & interestop) != 0); + .filter(ev -> (ev.interestOps() & interestOps) != 0); } /** - * Removes any events with the given {@code interestop}, and if no + * Removes any events with the given {@code interestOps}, and if no * events remaining, cancels the associated SelectionKey. */ - void resetInterestOps(int interestop) { - int newops = 0; + void resetInterestOps(int interestOps) { + int newOps = 0; Iterator itr = pending.iterator(); while (itr.hasNext()) { AsyncEvent event = itr.next(); int evops = event.interestOps(); - if ((evops & interestop) != 0) { + if (event.repeating()) { + newOps |= evops; + continue; + } + if ((evops & interestOps) != 0) { itr.remove(); } else { - newops |= evops; + newOps |= evops; } } - interestops = newops; + this.interestOps = newOps; SelectionKey key = chan.keyFor(selector); - if (newops == 0) { + if (newOps == 0) { key.cancel(); } else { - key.interestOps(newops); + key.interestOps(newOps); } } } @@ -366,7 +384,8 @@ /** * Creates a HttpRequest associated with this group. * - * @throws IllegalStateException if the group has been stopped + * @throws IllegalStateException + * if the group has been stopped */ @Override public HttpRequestBuilderImpl request() { @@ -376,7 +395,8 @@ /** * Creates a HttpRequest associated with this group. * - * @throws IllegalStateException if the group has been stopped + * @throws IllegalStateException + * if the group has been stopped */ @Override public HttpRequestBuilderImpl request(URI uri) { @@ -444,16 +464,12 @@ return version.equals(Version.HTTP_2); } - //void setHttp2NotSupported(String host) { - //http2NotSupported.put(host, false); - //} - - final void initFilters() { + private void initFilters() { addFilter(AuthenticationFilter.class); addFilter(RedirectFilter.class); } - final void addFilter(Class f) { + private void addFilter(Class f) { filters.addFilter(f); } @@ -479,14 +495,14 @@ iter.previous(); break; } else if (!iter.hasNext()) { - event.delta = event.timeval - listval ; + event.delta = event.timeval - listval; } } iter.add(event); selmgr.wakeupSelector(); } - synchronized void signalTimeouts(long then) { + private synchronized void signalTimeouts(long then) { if (timeouts.isEmpty()) { return; } @@ -532,12 +548,12 @@ // used for the connection window int getReceiveBufferSize() { return Utils.getIntegerNetProperty( - "sun.net.httpclient.connectionWindowSize", 256 * 1024 + "java.net.httpclient.connectionWindowSize", 256 * 1024 ); } // returns 0 meaning block forever, or a number of millis to block for - synchronized long getTimeoutValue() { + private synchronized long getTimeoutValue() { if (timeouts.isEmpty()) { return 0; } else { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/HttpConnection.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpConnection.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpConnection.java Tue May 03 12:25:20 2016 -0700 @@ -23,9 +23,8 @@ */ package java.net.http; -import java.io.FileOutputStream; +import java.io.Closeable; import java.io.IOException; -import java.io.PrintStream; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; @@ -42,7 +41,17 @@ * SSLConnection: TLS channel direct to server * SSLTunnelConnection: TLS channel via (CONNECT) proxy tunnel */ -abstract class HttpConnection implements BufferHandler { +abstract class HttpConnection implements BufferHandler, Closeable { + + protected final static ByteBuffer emptyBuf = Utils.EMPTY_BYTEBUFFER; + + enum Mode { + BLOCKING, + NON_BLOCKING, + ASYNC + } + + protected Mode mode; // address we are connected to. Could be a server or a proxy final InetSocketAddress address; @@ -52,6 +61,7 @@ HttpConnection(InetSocketAddress address, HttpClientImpl client) { this.address = address; this.client = client; + this.buffer = emptyBuf; } /** @@ -68,7 +78,21 @@ */ public static HttpConnection getConnection(InetSocketAddress addr, HttpRequestImpl request) { - return getConnectionImpl(addr, request); + return getConnectionImpl(addr, request, null); + } + + /** + * Called specifically to get an async connection for HTTP/2 over SSL. + * + * @param addr + * @param request + * @param http2 + * @return + */ + public static HttpConnection getConnection(InetSocketAddress addr, + HttpRequestImpl request, Http2Connection http2) { + + return getConnectionImpl(addr, request, http2); } public abstract void connect() throws IOException, InterruptedException; @@ -93,7 +117,7 @@ // at beginning of response. ByteBuffer getRemaining() { ByteBuffer b = buffer; - buffer = null; + buffer = emptyBuf; return b; } @@ -123,17 +147,18 @@ } private static HttpConnection getSSLConnection(InetSocketAddress addr, - InetSocketAddress proxy, - HttpRequestImpl request, - String[] alpn) { + InetSocketAddress proxy, HttpRequestImpl request, + String[] alpn, Http2Connection http2) { HttpClientImpl client = request.client(); if (proxy != null) { return new SSLTunnelConnection(addr, client, proxy, request.getAccessControlContext()); + } else if (http2 == null) { + return new SSLConnection(addr, client, alpn); } else { - return new SSLConnection(addr, client, alpn); + return new AsyncSSLConnection(addr, client, alpn); } } @@ -142,7 +167,8 @@ * none available. */ private static HttpConnection getConnectionImpl(InetSocketAddress addr, - HttpRequestImpl request) { + HttpRequestImpl request, Http2Connection http2) { + HttpConnection c; HttpClientImpl client = request.client(); InetSocketAddress proxy = request.proxy(); @@ -167,7 +193,7 @@ if (c != null) { return c; } else { - return getSSLConnection(addr, proxy, request, alpn); + return getSSLConnection(addr, proxy, request, alpn, http2); } } } @@ -223,64 +249,16 @@ return address; } - void configureBlocking(boolean mode) throws IOException { - channel().configureBlocking(mode); + synchronized void configureMode(Mode mode) throws IOException { + this.mode = mode; + if (mode == Mode.BLOCKING) + channel().configureBlocking(true); + else + channel().configureBlocking(false); } abstract ConnectionPool.CacheKey cacheKey(); - /* - static PrintStream ps; - - static { - try { - String propval = Utils.getNetProperty("java.net.httpclient.showData"); - if (propval != null && propval.equalsIgnoreCase("true")) { - ps = new PrintStream(new FileOutputStream("/tmp/httplog.txt"), false); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - synchronized final void debugPrint(String s, ByteBuffer b) { - ByteBuffer[] bufs = new ByteBuffer[1]; - bufs[0] = b; - debugPrint(s, bufs, 0, 1); - } - - synchronized final void debugPrint(String s, - ByteBuffer[] bufs, - int start, - int number) { - if (ps == null) { - return; - } - - ps.printf("\n%s:\n", s); - - for (int i=start; i 0x20 && c <= 0x7F) { - ps.printf("%c", (char)c); - } else { - ps.printf("0x%02x ", c); - } - } - } - ps.printf("\n---------------------\n"); - } - - */ - // overridden in SSL only SSLParameters sslParameters() { return null; @@ -296,7 +274,8 @@ /** * Closes this connection, by returning the socket to its connection pool. */ - abstract void close(); + @Override + public abstract void close(); /** * Returns a ByteBuffer with data, or null if EOF. @@ -356,12 +335,17 @@ } @Override - public final ByteBuffer getBuffer() { - return client.getBuffer(); + public final ByteBuffer getBuffer(int n) { + return client.getBuffer(n); } @Override public final void returnBuffer(ByteBuffer buffer) { client.returnBuffer(buffer); } + + @Override + public final void setMinBufferSize(int n) { + client.setMinBufferSize(n); + } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/HttpHeadersImpl.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpHeadersImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpHeadersImpl.java Tue May 03 12:25:20 2016 -0700 @@ -24,44 +24,22 @@ package java.net.http; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.TreeMap; /** * Implementation of HttpHeaders. */ -class HttpHeadersImpl implements HttpHeaders1 { +class HttpHeadersImpl implements HttpHeaders { - private final HashMap> headers; - private boolean isUnmodifiable = false; + private final TreeMap> headers; public HttpHeadersImpl() { - headers = new HashMap<>(); - } - - /** - * Replace all List in headers with unmodifiable Lists. Call - * this only after all headers are added. The headers HashMap - * is wrapped with an unmodifiable HashMap in map() - */ - @Override - public void makeUnmodifiable() { - if (isUnmodifiable) - return; - - Set keys = new HashSet<>(headers.keySet()); - for (String key : keys) { - List values = headers.remove(key); - if (values != null) { - headers.put(key, Collections.unmodifiableList(values)); - } - } - isUnmodifiable = true; + headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); } @Override @@ -88,7 +66,7 @@ public HttpHeadersImpl deepCopy() { HttpHeadersImpl h1 = new HttpHeadersImpl(); - HashMap> headers1 = h1.headers; + TreeMap> headers1 = h1.headers; Set keys = headers.keySet(); for (String key : keys) { List vals = headers.get(key); @@ -98,22 +76,13 @@ return h1; } - private List getOrCreate(String name) { - List l = headers.get(name); - if (l == null) { - l = new LinkedList<>(); - headers.put(name, l); - } - return l; - } - void addHeader(String name, String value) { - List l = getOrCreate(name); - l.add(value); + headers.computeIfAbsent(name, k -> new LinkedList<>()) + .add(value); } void setHeader(String name, String value) { - List l = getOrCreate(name); + List l = headers.computeIfAbsent(name, k -> new LinkedList<>()); l.clear(); l.add(value); } @@ -122,7 +91,7 @@ public Optional firstValueAsLong(String name) { List l = headers.get(name); if (l == null) { - return Optional.ofNullable(null); + return Optional.empty(); } else { String v = l.get(0); Long lv = Long.parseLong(v); @@ -133,4 +102,4 @@ void clear() { headers.clear(); } -} +} \ No newline at end of file diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestBuilderImpl.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestBuilderImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestBuilderImpl.java Tue May 03 12:25:20 2016 -0700 @@ -39,10 +39,11 @@ private HttpClient.Version version; private final HttpClientImpl client; private ProxySelector proxy; - private long timeval = 0; + private long timeval; public HttpRequestBuilderImpl(HttpClientImpl client, URI uri) { this.client = client; + checkURI(uri); this.uri = uri; this.version = client.version(); this.userHeaders = new HttpHeadersImpl(); @@ -58,10 +59,17 @@ @Override public HttpRequestBuilderImpl uri(URI uri) { Objects.requireNonNull(uri); + checkURI(uri); this.uri = uri; return this; } + private static void checkURI(URI uri) { + String scheme = uri.getScheme().toLowerCase(); + if (!scheme.equals("https") && !scheme.equals("http")) + throw new IllegalArgumentException("invalid URI scheme"); + } + @Override public HttpRequestBuilderImpl followRedirects(HttpClient.Redirect follow) { Objects.requireNonNull(follow); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestImpl.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestImpl.java Tue May 03 12:25:20 2016 -0700 @@ -30,16 +30,14 @@ import java.net.http.HttpClient.Version; import java.net.http.HttpResponse.MultiProcessor; import java.util.concurrent.CompletableFuture; -import java.net.SocketPermission; import java.security.AccessControlContext; import java.security.AccessController; -import java.util.Set; import static java.net.http.HttpRedirectImpl.getRedirects; import java.util.Locale; class HttpRequestImpl extends HttpRequest { - private final HttpHeadersImpl userHeaders; + private final ImmutableHeaders userHeaders; private final HttpHeadersImpl systemHeaders; private final URI uri; private InetSocketAddress authority; // only used when URI not specified @@ -56,6 +54,7 @@ private boolean receiving; private AccessControlContext acc; private final long timeval; + private Stream.PushGroup pushGroup; public HttpRequestImpl(HttpClientImpl client, String method, @@ -63,8 +62,8 @@ this.client = client; this.method = method == null? "GET" : method; this.userHeaders = builder.headers() == null ? - new HttpHeadersImpl() : builder.headers(); - dropDisallowedHeaders(); + new ImmutableHeaders() : + new ImmutableHeaders(builder.headers(), Utils.ALLOWED_HEADERS); this.followRedirects = getRedirects(builder.followRedirects() == null ? client.followRedirects() : builder.followRedirects()); this.systemHeaders = new HttpHeadersImpl(); @@ -90,15 +89,13 @@ HttpRequestImpl other) { this.client = client; this.method = method == null? "GET" : method; - this.userHeaders = other.userHeaders == null ? - new HttpHeadersImpl() : other.userHeaders; - dropDisallowedHeaders(); + this.userHeaders = other.userHeaders; this.followRedirects = getRedirects(other.followRedirects() == null ? client.followRedirects() : other.followRedirects()); this.systemHeaders = other.systemHeaders; this.uri = uri; this.expectContinue = other.expectContinue; - this.secure = other.secure; + this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https"); this.requestProcessor = other.requestProcessor; this.proxy = other.proxy; this.version = other.version; @@ -115,7 +112,7 @@ this.method = method; this.followRedirects = getRedirects(client.followRedirects()); this.systemHeaders = new HttpHeadersImpl(); - this.userHeaders = new HttpHeadersImpl(); + this.userHeaders = new ImmutableHeaders(); this.uri = null; this.proxy = null; this.requestProcessor = HttpRequest.noBody(); @@ -132,16 +129,52 @@ return client; } + /** + * Creates a HttpRequestImpl from the given set of Headers and the associated + * "parent" request. Fields not taken from the headers are taken from the + * parent. + */ + static HttpRequestImpl createPushRequest(HttpRequestImpl parent, + HttpHeadersImpl headers) throws IOException { + + return new HttpRequestImpl(parent, headers); + } + + // only used for push requests + private HttpRequestImpl(HttpRequestImpl parent, HttpHeadersImpl headers) throws IOException { + this.method = headers.firstValue(":method") + .orElseThrow(() -> new IOException("No method in Push Promise")); + String path = headers.firstValue(":path") + .orElseThrow(() -> new IOException("No path in Push Promise")); + String scheme = headers.firstValue(":scheme") + .orElseThrow(() -> new IOException("No scheme in Push Promise")); + String authority = headers.firstValue(":authority") + .orElseThrow(() -> new IOException("No authority in Push Promise")); + StringBuilder sb = new StringBuilder(); + sb.append(scheme).append("://").append(authority).append(path); + this.uri = URI.create(sb.toString()); + + this.client = parent.client; + this.userHeaders = new ImmutableHeaders(headers, Utils.ALLOWED_HEADERS); + this.followRedirects = parent.followRedirects; + this.systemHeaders = parent.systemHeaders; + this.expectContinue = parent.expectContinue; + this.secure = parent.secure; + this.requestProcessor = parent.requestProcessor; + this.proxy = parent.proxy; + this.version = parent.version; + this.acc = parent.acc; + this.exchange = parent.exchange; + this.timeval = parent.timeval; + } @Override public String toString() { - return (uri == null ? "" : uri.toString()) + "/" + method + "(" - + hashCode() + ")"; + return (uri == null ? "" : uri.toString()) + " " + method; } @Override public HttpHeaders headers() { - userHeaders.makeUnmodifiable(); return userHeaders; } @@ -154,21 +187,6 @@ systemHeaders.setHeader("HTTP2-Settings", h2client.getSettingsString()); } - private static final Set DISALLOWED_HEADERS_SET = Set.of( - "authorization", "connection", "cookie", "content-length", - "date", "expect", "from", "host", "origin", "proxy-authorization", - "referer", "user-agent", "upgrade", "via", "warning"); - - - // we silently drop headers that are disallowed - private void dropDisallowedHeaders() { - Set hdrnames = userHeaders.directMap().keySet(); - - hdrnames.removeIf((s) -> - DISALLOWED_HEADERS_SET.contains(s.toLowerCase()) - ); - } - private synchronized void receiving() { if (receiving) { throw new IllegalStateException("already receiving response"); @@ -176,6 +194,10 @@ receiving = true; } + synchronized Stream.PushGroup pushGroup() { + return pushGroup; + } + /* * Response filters may result in a new HttpRequestImpl being created * (but still associated with the same API HttpRequest) and the process @@ -200,10 +222,25 @@ .thenApply((r) -> (HttpResponse)r); } - public CompletableFuture - sendAsyncMulti(HttpResponse.MultiProcessor rspproc) { - // To change body of generated methods, choose Tools | Templates. - throw new UnsupportedOperationException("Not supported yet."); + + @SuppressWarnings("unchecked") + @Override + public synchronized CompletableFuture + multiResponseAsync(MultiProcessor rspproc) { + if (System.getSecurityManager() != null) { + acc = AccessController.getContext(); + } + this.pushGroup = new Stream.PushGroup<>(rspproc, this); + CompletableFuture cf = pushGroup.mainResponse(); + responseAsync() + .whenComplete((HttpResponse r, Throwable t) -> { + if (r != null) + cf.complete(r); + else + cf.completeExceptionally(t); + pushGroup.pushError(t); + }); + return (CompletableFuture)pushGroup.groupResult(); } @Override @@ -255,7 +292,7 @@ @Override public URI uri() { return uri; } - HttpHeadersImpl getUserHeaders() { return userHeaders; } + HttpHeaders getUserHeaders() { return userHeaders; } HttpHeadersImpl getSystemHeaders() { return systemHeaders; } @@ -275,11 +312,4 @@ } long timeval() { return timeval; } - - @Override - public CompletableFuture - multiResponseAsync(MultiProcessor rspproc) { - //To change body of generated methods, choose Tools | Templates. - throw new UnsupportedOperationException("Not supported yet."); - } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/HttpResponse.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponse.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponse.java Tue May 03 12:25:20 2016 -0700 @@ -200,8 +200,12 @@ if (n == -1) { throw new IOException("Bad Content-Disposition type"); } - String disposition = dispoHeader.substring(n + 9, - dispoHeader.lastIndexOf(';')); + int lastsemi = dispoHeader.lastIndexOf(';'); + String disposition; + if (lastsemi < n) + disposition = dispoHeader.substring(n + 9); + else + disposition = dispoHeader.substring(n + 9, lastsemi); file = Paths.get(directory.toString(), disposition); fc = FileChannel.open(file, openOptions); return null; @@ -727,11 +731,14 @@ } private CompletableFuture getBody(HttpRequest req, - CompletableFuture cf) { + CompletableFuture cf) { URI u = req.uri(); String path = u.getPath(); + if (path.startsWith("/")) + path = path.substring(1); + final String fpath = path; return cf.thenCompose((HttpResponse resp) -> { - return resp.bodyAsync(HttpResponse.asFile(destination.resolve(path))); + return resp.bodyAsync(HttpResponse.asFile(destination.resolve(fpath))); }); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/HttpResponseImpl.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponseImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponseImpl.java Tue May 03 12:25:20 2016 -0700 @@ -26,6 +26,7 @@ package java.net.http; import java.io.IOException; +import java.io.UncheckedIOException; import java.net.URI; import java.nio.ByteBuffer; import java.security.AccessControlContext; @@ -42,17 +43,18 @@ int responseCode; Exchange exchange; HttpRequestImpl request; - HttpHeaders1 headers; - HttpHeaders1 trailers; + HttpHeaders headers; + HttpHeaders trailers; SSLParameters sslParameters; URI uri; HttpClient.Version version; AccessControlContext acc; RawChannel rawchan; HttpConnection connection; + final Stream stream; - public HttpResponseImpl(int responseCode, Exchange exch, HttpHeaders1 headers, - HttpHeaders1 trailers, SSLParameters sslParameters, + public HttpResponseImpl(int responseCode, Exchange exch, HttpHeaders headers, + HttpHeaders trailers, SSLParameters sslParameters, HttpClient.Version version, HttpConnection connection) { this.responseCode = responseCode; this.exchange = exch; @@ -63,6 +65,23 @@ this.uri = request.uri(); this.version = version; this.connection = connection; + this.stream = null; + } + + // A response to a PUSH_PROMISE + public HttpResponseImpl(int responseCode, HttpRequestImpl pushRequest, + ImmutableHeaders headers, + Stream stream, SSLParameters sslParameters) { + this.responseCode = responseCode; + this.exchange = null; + this.request = pushRequest; + this.headers = headers; + this.trailers = null; + this.sslParameters = sslParameters; + this.uri = request.uri(); // TODO: take from headers + this.version = HttpClient.Version.HTTP_2; + this.connection = null; + this.stream = stream; } @Override @@ -77,26 +96,35 @@ @Override public HttpHeaders headers() { - headers.makeUnmodifiable(); return headers; } @Override public HttpHeaders trailers() { - trailers.makeUnmodifiable(); return trailers; } @Override public T body(java.net.http.HttpResponse.BodyProcessor processor) { - return exchange.responseBody(processor); + try { + if (exchange != null) { + return exchange.responseBody(processor); + } else { + return stream.responseBody(processor); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } } @Override public CompletableFuture bodyAsync(java.net.http.HttpResponse.BodyProcessor processor) { acc = AccessController.getContext(); - return exchange.responseBodyAsync(processor); + if (exchange != null) + return exchange.responseBodyAsync(processor); + else + return stream.responseBodyAsync(processor); } @Override diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/ImmutableHeaders.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ImmutableHeaders.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015, 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 + */ + +package java.net.http; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; +import java.util.function.Predicate; + +/** + * Immutable HttpHeaders constructed from mutable HttpHeadersImpl. + */ + +class ImmutableHeaders implements HttpHeaders { + + private final Map> map; + + @SuppressWarnings("unchecked") + ImmutableHeaders() { + map = (Map>)Collections.EMPTY_MAP; + } + // TODO: fix lower case issue. Must be lc for http/2 compares ignoreCase for http/1 + ImmutableHeaders(HttpHeadersImpl h, Predicate keyAllowed) { + Map> src = h.directMap(); + Map> m = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + + src.forEach((key, value) -> { + if (keyAllowed.test(key)) + m.put(key, Collections.unmodifiableList(value)); + }); + map = Collections.unmodifiableMap(m); + } + + @Override + public Optional firstValue(String name) { + List l = map.get(name); + String v = l == null ? null : l.get(0); + return Optional.ofNullable(v); + } + + @Override + public Optional firstValueAsLong(String name) { + return firstValue(name).map((v -> Long.parseLong(v))); + } + + @Override + public List allValues(String name) { + return map.get(name); + } + + @Override + public Map> map() { + return map; + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/Log.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Log.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Log.java Tue May 03 12:25:20 2016 -0700 @@ -26,7 +26,9 @@ import java.util.Locale; /** - * -Djava.net.HttpClient.log=errors,requests,headers,frames[:type:type2:..],content + * -Djava.net.HttpClient.log= + * errors,requests,headers, + * frames[:type:type2:..],content,ssl,trace * * Any of errors, requests, headers or content are optional. * @@ -47,6 +49,7 @@ public static final int CONTENT = 0x8; public static final int FRAMES = 0x10; public static final int SSL = 0x20; + public static final int TRACE = 0x40; static int logging; // Frame types: "control", "data", "window", "all" @@ -81,8 +84,11 @@ case "ssl": logging |= SSL; break; + case "trace": + logging |= TRACE; + break; case "all": - logging |= CONTENT|HEADERS|REQUESTS|FRAMES|ERRORS; + logging |= CONTENT|HEADERS|REQUESTS|FRAMES|ERRORS|TRACE; break; } if (val.startsWith("frames")) { @@ -130,6 +136,10 @@ return (logging & HEADERS) != 0; } + static boolean trace() { + return (logging & TRACE) != 0; + } + static boolean ssl() { return (logging & SSL) != 0; } @@ -138,9 +148,9 @@ return (logging & FRAMES) != 0; } - static void logError(String s) { + static void logError(String s, Object... s1) { if (errors()) - logger.log(Level.INFO, "ERROR: " + s); + logger.log(Level.INFO, "ERROR: " + s, s1); } static void logError(Throwable t) { @@ -150,24 +160,50 @@ } } - static void logSSL(String s) { + static void logSSL(String s, Object... s1) { if (ssl()) - logger.log(Level.INFO, "SSL: " + s); + logger.log(Level.INFO, "SSL: " + s, s1); + } + + static void logTrace(String s, Object... s1) { + if (trace()) { + String format = "TRACE: " + s; + logger.log(Level.INFO, format, s1); + } + } + + static void logRequest(String s, Object... s1) { + if (requests()) + logger.log(Level.INFO, "REQUEST: " + s, s1); + } + + static void logResponse(String s, Object... s1) { + if (requests()) + logger.log(Level.INFO, "RESPONSE: " + s, s1); } - static void logRequest(String s) { - if (requests()) - logger.log(Level.INFO, "REQUEST: " + s); + static void logHeaders(String s, Object... s1) { + if (headers()) + logger.log(Level.INFO, "HEADERS: " + s, s1); + } +// START HTTP2 + static boolean loggingFrame(Class clazz) { + if (frametypes == ALL) { + return true; + } + if (clazz == DataFrame.class) { + return (frametypes & DATA) != 0; + } else if (clazz == WindowUpdateFrame.class) { + return (frametypes & WINDOW_UPDATES) != 0; + } else { + return (frametypes & CONTROL) != 0; + } } - static void logResponse(String s) { - if (requests()) - logger.log(Level.INFO, "RESPONSE: " + s); - } - - static void logHeaders(String s) { - if (headers()) - logger.log(Level.INFO, "HEADERS: " + s); + static void logFrames(Http2Frame f, String direction) { + if (frames() && loggingFrame(f.getClass())) { + logger.log(Level.INFO, "FRAME: " + direction + ": " + f.toString()); + } } // not instantiable diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/MultiExchange.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/MultiExchange.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/MultiExchange.java Tue May 03 12:25:20 2016 -0700 @@ -54,7 +54,7 @@ final static int DEFAULT_MAX_ATTEMPTS = 5; final static int max_attempts = Utils.getIntegerNetProperty( - "sun.net.httpclient.redirects.retrylimit", DEFAULT_MAX_ATTEMPTS + "java.net.httpclient.redirects.retrylimit", DEFAULT_MAX_ATTEMPTS ); private final List filters; @@ -187,8 +187,7 @@ public CompletableFuture responseAsync(Void v) { CompletableFuture cf; if (++attempts > max_attempts) { - cf = new CompletableFuture<>(); - cf.completeExceptionally(new IOException("Too many retries")); + cf = CompletableFuture.failedFuture(new IOException("Too many retries")); } else { if (currentreq.timeval() != 0) { // set timer @@ -241,7 +240,6 @@ * completed exceptionally. */ private CompletableFuture getExceptionalCF(Throwable t) { - CompletableFuture error = new CompletableFuture<>(); if ((t instanceof CompletionException) || (t instanceof ExecutionException)) { if (t.getCause() != null) { t = t.getCause(); @@ -250,8 +248,7 @@ if (cancelled && t instanceof IOException) { t = new HttpTimeoutException("request timed out"); } - error.completeExceptionally(t); - return error; + return CompletableFuture.failedFuture(t); } T responseBody(HttpResponse.BodyProcessor processor) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/OutgoingHeaders.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/OutgoingHeaders.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015, 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 + */ + +package java.net.http; + +import java.io.IOException; + +/** + * Contains all parameters for outgoing headers. Is converted to + * HeadersFrame and ContinuationFrames by Http2Connection. + */ +class OutgoingHeaders extends Http2Frame { + + int streamDependency; + int weight; + boolean exclusive; + Stream stream; + + public static final int PRIORITY = 0x20; + + HttpHeaders user, system; + + OutgoingHeaders(HttpHeaders hdrs1, HttpHeaders hdrs2, Stream stream) { + this.user = hdrs2; + this.system = hdrs1; + this.stream = stream; + } + + public void setPriority(int streamDependency, boolean exclusive, int weight) { + this.streamDependency = streamDependency; + this.exclusive = exclusive; + this.weight = weight; + this.flags |= PRIORITY; + } + + public int getStreamDependency() { + return streamDependency; + } + + public int getWeight() { + return weight; + } + + public boolean getExclusive() { + return exclusive; + } + + public Stream getStream() { + return stream; + } + + public HttpHeaders getUserHeaders() { + return user; + } + + public HttpHeaders getSystemHeaders() { + return system; + } + + @Override + void readIncomingImpl(ByteBufferConsumer bc) throws IOException { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + void computeLength() { + //To change body of generated methods, choose Tools | Templates. + throw new UnsupportedOperationException("Not supported yet."); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/Pair.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Pair.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Pair.java Tue May 03 12:25:20 2016 -0700 @@ -43,4 +43,9 @@ static Pair pair(T first, U second) { return new Pair<>(first, second); } + + @Override + public String toString() { + return "(" + first + ", " + second + ")"; + } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/PingFrame.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PingFrame.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015, 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 + */ + +package java.net.http; + +import java.io.IOException; +import java.nio.ByteBuffer; + +class PingFrame extends Http2Frame { + + PingFrame() { + type = TYPE; + } + + byte[] data; + + public final static int TYPE = 0x6; + + // Flags + public static final int ACK = 0x1; + + @Override + String flagAsString(int flag) { + switch (flag) { + case ACK: + return "ACK"; + } + return super.flagAsString(flag); + } + + public void setData(byte[] data) { + if (data.length != 8) { + throw new IllegalArgumentException("Ping data not 8 bytes"); + } + this.data = data; + } + + public byte[] getData() { + return data; + } + + @Override + void readIncomingImpl(ByteBufferConsumer bc) throws IOException { + if (length != 8) { + throw new IOException("Invalid Ping frame"); + } + data = bc.getBytes(8); + } + + @Override + void writeOutgoing(ByteBufferGenerator bg) { + if (data == null) { + data = new byte[] {0, 0, 0, 0, 0 ,0, 0, 0}; + } + super.writeOutgoing(bg); + ByteBuffer buf = bg.getBuffer(8); + buf.put(data); + } + + @Override + void computeLength() { + length = 8; + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/PlainHttpConnection.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/PlainHttpConnection.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PlainHttpConnection.java Tue May 03 12:25:20 2016 -0700 @@ -31,20 +31,43 @@ import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; /** - * Plain raw TCP connection direct to destination + * Plain raw TCP connection direct to destination. 2 modes + * 1) Blocking used by http/1. In this case the connect is actually non + * blocking but the request is sent blocking. The first byte of a response + * is received non-blocking and the remainder of the response is received + * blocking + * 2) Non-blocking. In this case (for http/2) the connection is actually opened + * blocking but all reads and writes are done non-blocking under the + * control of a Http2Connection object. */ -class PlainHttpConnection extends HttpConnection { +class PlainHttpConnection extends HttpConnection implements AsyncConnection { protected SocketChannel chan; private volatile boolean connected; private boolean closed; + Consumer asyncReceiver; + Consumer errorReceiver; + Queue asyncOutputQ; + final Object reading = new Object(); + final Object writing = new Object(); - class ConnectEvent extends AsyncEvent implements AsyncEvent.Blocking { + @Override + public void startReading() { + try { + client.registerEvent(new ReadEvent()); + } catch (IOException e) { + shutdown(); + } + } + + class ConnectEvent extends AsyncEvent { CompletableFuture cf; ConnectEvent(CompletableFuture cf) { + super(AsyncEvent.BLOCKING); this.cf = cf; } @@ -112,14 +135,62 @@ @Override long write(ByteBuffer[] buffers, int start, int number) throws IOException { - //debugPrint("Send", buffers, start, number); - return chan.write(buffers, start, number); + if (mode != Mode.ASYNC) + return chan.write(buffers, start, number); + // async + synchronized(writing) { + int qlen = asyncOutputQ.size(); + ByteBuffer[] bufs = Utils.reduce(buffers, start, number); + long n = Utils.remaining(bufs); + asyncOutputQ.putAll(bufs); + if (qlen == 0) + asyncOutput(); + return n; + } + } + + ByteBuffer asyncBuffer = null; + + void asyncOutput() { + synchronized (writing) { + try { + while (true) { + if (asyncBuffer == null) { + asyncBuffer = asyncOutputQ.poll(); + if (asyncBuffer == null) { + return; + } + } + if (!asyncBuffer.hasRemaining()) { + asyncBuffer = null; + continue; + } + int n = chan.write(asyncBuffer); + //System.err.printf("Written %d bytes to chan\n", n); + if (n == 0) { + client.registerEvent(new WriteEvent()); + return; + } + } + } catch (IOException e) { + shutdown(); + } + } } @Override long write(ByteBuffer buffer) throws IOException { - //debugPrint("Send", buffer); - return chan.write(buffer); + if (mode != Mode.ASYNC) + return chan.write(buffer); + // async + synchronized(writing) { + int qlen = asyncOutputQ.size(); + long n = buffer.remaining(); + asyncOutputQ.put(buffer); + if (qlen == 0) + asyncOutput(); + return n; + } } @Override @@ -131,7 +202,7 @@ * Close this connection */ @Override - synchronized void close() { + public synchronized void close() { if (closed) return; closed = true; @@ -155,14 +226,49 @@ return buf; } + void shutdown() { + close(); + errorReceiver.accept(new IOException("Connection aborted")); + } + + void asyncRead() { + synchronized (reading) { + try { + while (true) { + ByteBuffer buf = getBuffer(); + int n = chan.read(buf); + //System.err.printf("Read %d bytes from chan\n", n); + if (n == -1) { + throw new IOException(); + } + if (n == 0) { + returnBuffer(buf); + return; + } + buf.flip(); + asyncReceiver.accept(buf); + } + } catch (IOException e) { + shutdown(); + } + } + } + @Override protected int readImpl(ByteBuffer buf) throws IOException { int mark = buf.position(); - int n = chan.read(buf); + int n; + // FIXME: this hack works in conjunction with the corresponding change + // in java.net.http.RawChannel.registerEvent + if ((n = buffer.remaining()) != 0) { + buf.put(buffer); + } else { + n = chan.read(buf); + } if (n == -1) { return -1; } - Utils.flipToMark(buffer, mark); + Utils.flipToMark(buf, mark); String s = "Receive (" + n + " bytes) "; //debugPrint(s, buf); return n; @@ -178,10 +284,67 @@ return connected; } - class ReceiveResponseEvent extends AsyncEvent implements AsyncEvent.Blocking { + // used for all output in HTTP/2 + class WriteEvent extends AsyncEvent { + WriteEvent() { + super(0); + } + + @Override + public SelectableChannel channel() { + return chan; + } + + @Override + public int interestOps() { + return SelectionKey.OP_WRITE; + } + + @Override + public void handle() { + asyncOutput(); + } + + @Override + public void abort() { + shutdown(); + } + } + + // used for all input in HTTP/2 + class ReadEvent extends AsyncEvent { + ReadEvent() { + super(AsyncEvent.REPEATING); // && !BLOCKING + } + + @Override + public SelectableChannel channel() { + return chan; + } + + @Override + public int interestOps() { + return SelectionKey.OP_READ; + } + + @Override + public void handle() { + asyncRead(); + } + + @Override + public void abort() { + shutdown(); + } + + } + + // used in blocking channels only + class ReceiveResponseEvent extends AsyncEvent { CompletableFuture cf; ReceiveResponseEvent(CompletableFuture cf) { + super(AsyncEvent.BLOCKING); this.cf = cf; } @Override @@ -216,6 +379,15 @@ } @Override + public synchronized void setAsyncCallbacks(Consumer asyncReceiver, + Consumer errorReceiver) { + this.asyncReceiver = asyncReceiver; + this.errorReceiver = errorReceiver; + asyncOutputQ = new Queue<>(); + asyncOutputQ.registerPutCallback(this::asyncOutput); + } + + @Override CompletableFuture whenReceivingResponse() { CompletableFuture cf = new CompletableFuture<>(); try { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/PlainTunnelingConnection.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/PlainTunnelingConnection.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PlainTunnelingConnection.java Tue May 03 12:25:20 2016 -0700 @@ -111,7 +111,7 @@ } @Override - void close() { + public void close() { delegate.close(); connected = false; } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/PriorityFrame.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PriorityFrame.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015, 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 + */ + +package java.net.http; + +import java.io.IOException; +import java.nio.ByteBuffer; + +class PriorityFrame extends Http2Frame { + + int streamDependency; + int weight; + boolean exclusive; + + public final static int TYPE = 0x2; + + PriorityFrame() { + type = TYPE; + } + + public PriorityFrame(int streamDependency, boolean exclusive, int weight) { + this.streamDependency = streamDependency; + this.exclusive = exclusive; + this.weight = weight; + this.type = TYPE; + } + + int streamDependency() { + return streamDependency; + } + + int weight() { + return weight; + } + + boolean exclusive() { + return exclusive; + } + + @Override + void readIncomingImpl(ByteBufferConsumer bc) throws IOException { + int x = bc.getInt(); + exclusive = (x & 0x80000000) != 0; + streamDependency = x & 0x7fffffff; + weight = bc.getByte(); + } + + @Override + void writeOutgoing(ByteBufferGenerator bg) { + super.writeOutgoing(bg); + ByteBuffer buf = bg.getBuffer(5); + int x = exclusive ? (1 << 31) + streamDependency : streamDependency; + buf.putInt(x); + buf.put((byte)weight); + } + + @Override + void computeLength() { + length = 5; + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/PushPromiseFrame.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PushPromiseFrame.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2015, 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 + */ + +package java.net.http; + +import java.io.IOException; +import java.nio.ByteBuffer; + +class PushPromiseFrame extends HeaderFrame { + + int padLength; + int promisedStream; + + PushPromiseFrame() { + type = TYPE; + } + + public static final int TYPE = 0x5; + + // Flags + public static final int END_HEADERS = 0x4; + public static final int PADDED = 0x8; + + @Override + public String toString() { + return super.toString() + " promisedStreamid: " + promisedStream + + " headerLength: " + headerLength; + } + + @Override + String flagAsString(int flag) { + switch (flag) { + case PADDED: + return "PADDED"; + case END_HEADERS: + return "END_HEADERS"; + } + return super.flagAsString(flag); + } + + public void setPadLength(int padLength) { + this.padLength = padLength; + flags |= PADDED; + } + + public void setPromisedStream(int stream) { + this.promisedStream = stream; + } + + public int getPromisedStream() { + return promisedStream; + } + + /** + */ + @Override + void readIncomingImpl(ByteBufferConsumer bc) throws IOException { + if ((flags & PADDED) != 0) { + padLength = bc.getByte(); + headerLength = length - (padLength + 5); + } else + headerLength = length - 4; + + promisedStream = bc.getInt() & 0x7fffffff; + headerBlocks = bc.getBuffers(headerLength); + } + + @Override + void computeLength() { + int len = 0; + if ((flags & PADDED) != 0) { + len += (1 + padLength); + } + len += (4 + headerLength); + this.length = len; + } + + @Override + void writeOutgoing(ByteBufferGenerator bg) { + super.writeOutgoing(bg); + ByteBuffer buf = bg.getBuffer(length); + if ((flags & PADDED) != 0) { + buf.put((byte)padLength); + } + buf.putInt(promisedStream); + for (int i=0; i implements Closeable { + + private final LinkedList q = new LinkedList<>(); + private volatile boolean closed = false; + private Runnable callback; + private boolean forceCallback; + private int waiters; // true if someone waiting + + synchronized void putAll(T[] objs) throws IOException { + if (closed) { + throw new IOException("stream closed"); + } + boolean wasEmpty = q.isEmpty(); + + for (T obj : objs) { + q.add(obj); + } + + if (waiters > 0) + notifyAll(); + + if (wasEmpty || forceCallback) { + forceCallback = false; + if (callback != null) { + callback.run(); + } + } + } + + synchronized int size() { + return q.size(); + } + + synchronized void put(T obj) throws IOException { + if (closed) { + throw new IOException("stream closed"); + } + + q.add(obj); + if (waiters > 0) + notifyAll(); + + if (q.size() == 1 || forceCallback) { + forceCallback = false; + if (callback != null) { + callback.run(); + } + } + } + + /** + * callback is invoked any time put is called where + * the Queue was empty. + */ + synchronized void registerPutCallback(Runnable callback) { + this.callback = callback; + if (callback != null && q.size() > 0) + callback.run(); + } + + @Override + public synchronized void close() { + closed = true; + notifyAll(); + } + + synchronized T take() throws IOException { + if (closed) { + throw new IOException("stream closed"); + } + try { + while (q.size() == 0) { + waiters++; + wait(); + waiters--; + } + return q.removeFirst(); + } catch (InterruptedException ex) { + throw new IOException(ex); + } + } + + public synchronized T poll() throws IOException { + if (closed) + throw new IOException("stream closed"); + + if (q.isEmpty()) + return null; + T res = q.removeFirst(); + return res; + } + + public synchronized T[] pollAll(T[] type) throws IOException { + T[] ret = q.toArray(type); + q.clear(); + return ret; + } + + public synchronized void pushback(T v) { + forceCallback = true; + q.addFirst(v); + } + + public synchronized void pushbackAll(T[] v) { + forceCallback = true; + for (int i=v.length-1; i>=0; i--) { + q.addFirst(v[i]); + } + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java Tue May 03 12:25:20 2016 -0700 @@ -28,18 +28,18 @@ import java.nio.channels.ByteChannel; import java.nio.channels.GatheringByteChannel; import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; -/** - * Used to implement WebSocket. Each RawChannel corresponds to - * a TCP connection (SocketChannel) but is connected to a Selector - * and an ExecutorService for invoking the send and receive callbacks - * Also includes SSL processing. - */ -class RawChannel implements ByteChannel, GatheringByteChannel { +// +// Used to implement WebSocket. Each RawChannel corresponds to a TCP connection +// (SocketChannel) but is connected to a Selector and an ExecutorService for +// invoking the send and receive callbacks. Also includes SSL processing. +// +final class RawChannel implements ByteChannel, GatheringByteChannel { private final HttpClientImpl client; private final HttpConnection connection; - private boolean closed; + private volatile boolean closed; private interface RawEvent { @@ -50,8 +50,6 @@ void handle(); } - interface BlockingEvent extends RawEvent { } - interface NonBlockingEvent extends RawEvent { } RawChannel(HttpClientImpl client, HttpConnection connection) { @@ -64,39 +62,40 @@ private final RawEvent re; RawAsyncEvent(RawEvent re) { + super(AsyncEvent.BLOCKING); // BLOCKING & !REPEATING this.re = re; } + RawAsyncEvent(RawEvent re, int flags) { + super(flags); + this.re = re; + } + + @Override public SelectableChannel channel() { return connection.channel(); } // must return the selector interest op flags OR'd + @Override public int interestOps() { return re.interestOps(); } // called when event occurs + @Override public void handle() { re.handle(); } - public void abort() {} + @Override + public void abort() { } } - private class BlockingRawAsyncEvent extends RawAsyncEvent - implements AsyncEvent.Blocking { - - BlockingRawAsyncEvent(RawEvent re) { - super(re); - } - } - - private class NonBlockingRawAsyncEvent extends RawAsyncEvent - implements AsyncEvent.NonBlocking { + private class NonBlockingRawAsyncEvent extends RawAsyncEvent { NonBlockingRawAsyncEvent(RawEvent re) { - super(re); + super(re, 0); // !BLOCKING & !REPEATING } } @@ -105,17 +104,24 @@ * (i.e. register new event for each callback) */ public void registerEvent(RawEvent event) throws IOException { - if (event instanceof BlockingEvent) { - client.registerEvent(new BlockingRawAsyncEvent(event)); - } else if (event instanceof NonBlockingEvent) { + if (!(event instanceof NonBlockingEvent)) { + throw new InternalError(); + } + if ((event.interestOps() & SelectionKey.OP_READ) != 0 + && connection.buffer.hasRemaining()) { + // FIXME: a hack to deal with leftovers from previous reads into an + // internal buffer (works in conjunction with change in + // java.net.http.PlainHttpConnection.readImpl(java.nio.ByteBuffer) + connection.channel().configureBlocking(false); + event.handle(); + } else { client.registerEvent(new NonBlockingRawAsyncEvent(event)); - } else { - throw new InternalError(); } } @Override public int read(ByteBuffer dst) throws IOException { + assert !connection.channel().isBlocking(); return connection.read(dst); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/RedirectFilter.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/RedirectFilter.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/RedirectFilter.java Tue May 03 12:25:20 2016 -0700 @@ -33,17 +33,19 @@ HttpRequestImpl requestImpl; HttpRequest request; HttpClientImpl client; + HttpClient.Redirect policy; String method; final static int DEFAULT_MAX_REDIRECTS = 5; URI uri; final static int max_redirects = Utils.getIntegerNetProperty( - "sun.net.httpclient.redirects.retrylimit", DEFAULT_MAX_REDIRECTS + "java.net.httpclient.redirects.retrylimit", DEFAULT_MAX_REDIRECTS ); @Override public void request(HttpRequestImpl r) throws IOException { this.request = r; + this.policy = request.followRedirects(); this.client = r.getClient(); this.method = r.method(); this.requestImpl = r; @@ -61,7 +63,7 @@ */ private HttpRequestImpl handleResponse(HttpResponseImpl r) { int rcode = r.statusCode(); - if (rcode == 200) { + if (rcode == 200 || policy == HttpClient.Redirect.NEVER) { return null; } if (rcode >= 300 && rcode <= 399) { @@ -79,6 +81,7 @@ private URI getRedirectedURI(HttpHeaders headers) { URI redirectedURI; + String ss = headers.firstValue("Location").orElse("Not present"); redirectedURI = headers.firstValue("Location") .map((s) -> URI.create(s)) .orElseThrow(() -> new UncheckedIOException( diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/ResetFrame.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ResetFrame.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, 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 + */ + +package java.net.http; + +import java.io.IOException; +import java.nio.ByteBuffer; + +class ResetFrame extends ErrorFrame { + + public final static int TYPE = 0x3; + + // See ErrorFrame for error values + + ResetFrame() { + type = TYPE; + } + + public ResetFrame(int errorCode) { + this.errorCode = errorCode; + this.type = TYPE; + } + + @Override + void readIncomingImpl(ByteBufferConsumer bc) throws IOException { + errorCode = bc.getInt(); + } + + @Override + void writeOutgoing(ByteBufferGenerator bg) { + super.writeOutgoing(bg); + ByteBuffer buf = bg.getBuffer(4); + buf.putInt(errorCode); + } + + @Override + void computeLength() { + length = 4; + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/ResponseHeaders.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/ResponseHeaders.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ResponseHeaders.java Tue May 03 12:25:20 2016 -0700 @@ -48,7 +48,7 @@ * * This class is not thread-safe */ -class ResponseHeaders implements HttpHeaders1 { +class ResponseHeaders implements HttpHeaders { static final int DATA_SIZE = 16 * 1024; // initial space for headers static final int NUM_HEADERS = 50; // initial expected max number of headers @@ -368,10 +368,6 @@ return Collections.unmodifiableList(l); } - @Override - public void makeUnmodifiable() { - } - // Delegates map to HashMap but converts keys to lower case static class HeaderMap implements Map> { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/SSLConnection.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/SSLConnection.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/SSLConnection.java Tue May 03 12:25:20 2016 -0700 @@ -121,13 +121,8 @@ } @Override - void close() { - try { - //System.err.println ("Closing: " + this); - delegate.channel().close(); // TODO: proper close - } catch (IOException ex) { - Log.logError(ex.toString()); - } + public void close() { + Utils.close(delegate.channel()); } @Override diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/SSLDelegate.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/SSLDelegate.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/SSLDelegate.java Tue May 03 12:25:20 2016 -0700 @@ -29,13 +29,9 @@ import java.util.Arrays; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLEngineResult.Status; -import javax.net.ssl.SSLParameters; -import javax.net.ssl.SSLSession; +import javax.net.ssl.*; import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*; /** @@ -60,16 +56,18 @@ engine.setUseClientMode(true); SSLParameters sslp = client.sslParameters().orElse(null); if (sslp == null) { - sslp = context.getDefaultSSLParameters(); + sslp = context.getSupportedSSLParameters(); } sslParameters = Utils.copySSLParameters(sslp); if (alpn != null) { sslParameters.setApplicationProtocols(alpn); Log.logSSL("Setting application protocols: " + Arrays.toString(alpn)); } else { - Log.logSSL("Warning no application protocols proposed!"); + Log.logSSL("No application protocols proposed"); } engine.setSSLParameters(sslParameters); + engine.setEnabledCipherSuites(sslp.getCipherSuites()); + engine.setEnabledProtocols(sslp.getProtocols()); wrapper = new EngineWrapper(chan, engine); this.chan = chan; this.client = client; @@ -268,7 +266,7 @@ do { if (needData) { do { - x = chan.read (unwrap_src); + x = chan.read (unwrap_src); } while (x == 0); if (x == -1) { throw new IOException ("connection closed for reading"); @@ -440,6 +438,27 @@ } } + static void printParams(SSLParameters p) { + System.out.println("SSLParameters:"); + if (p == null) { + System.out.println("Null params"); + return; + } + for (String cipher : p.getCipherSuites()) { + System.out.printf("cipher: %s\n", cipher); + } + for (String approto : p.getApplicationProtocols()) { + System.out.printf("application protocol: %s\n", approto); + } + for (String protocol : p.getProtocols()) { + System.out.printf("protocol: %s\n", protocol); + } + if (p.getServerNames() != null) + for (SNIServerName sname : p.getServerNames()) { + System.out.printf("server name: %s\n", sname.toString()); + } + } + String getSessionInfo() { StringBuilder sb = new StringBuilder(); String application = engine.getApplicationProtocol(); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/SSLTunnelConnection.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/SSLTunnelConnection.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/SSLTunnelConnection.java Tue May 03 12:25:20 2016 -0700 @@ -130,12 +130,8 @@ } @Override - void close() { - try { - //System.err.println ("Closing: " + this); - delegate.channel().close(); // TODO: proper close - } catch (IOException ex) { - } + public void close() { + Utils.close(delegate.channel()); } @Override diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/SettingsFrame.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/SettingsFrame.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2015, 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 + */ + +package java.net.http; + +import java.io.IOException; +import java.nio.ByteBuffer; + +class SettingsFrame extends Http2Frame { + + int[] parameters; + + public static final int TYPE = 0x4; + + // Flags + public static final int ACK = 0x1; + + @Override + String flagAsString(int flag) { + switch (flag) { + case ACK: + return "ACK"; + } + return super.flagAsString(flag); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()) + .append(" Settings: "); + + for (int i = 0; i < MAX_PARAM; i++) { + if (parameters[i] != -1) { + sb.append(name(i)) + .append("=") + .append(Integer.toString(parameters[i])) + .append(' '); + } + } + return sb.toString(); + } + + // Parameters + public static final int HEADER_TABLE_SIZE = 0x1; + public static final int ENABLE_PUSH = 0x2; + public static final int MAX_CONCURRENT_STREAMS = 0x3; + public static final int INITIAL_WINDOW_SIZE = 0x4; + public static final int MAX_FRAME_SIZE = 0x5; + public static final int MAX_HEADER_LIST_SIZE = 0x6; + + private String name(int i) { + switch (i+1) { + case HEADER_TABLE_SIZE: + return "HEADER_TABLE_SIZE"; + case ENABLE_PUSH: + return "ENABLE_PUSH"; + case MAX_CONCURRENT_STREAMS: + return "MAX_CONCURRENT_STREAMS"; + case INITIAL_WINDOW_SIZE: + return "INITIAL_WINDOW_SIZE"; + case MAX_FRAME_SIZE: + return "MAX_FRAME_SIZE"; + case MAX_HEADER_LIST_SIZE: + return "MAX_HEADER_LIST_SIZE"; + } + return "unknown parameter"; + } + public static final int MAX_PARAM = 0x6; + + public SettingsFrame() { + type = TYPE; + parameters = new int [MAX_PARAM]; + for (int i=0; i < parameters.length; i++) { + parameters[i] = -1; + } + } + + public int getParameter(int paramID) { + if (paramID > MAX_PARAM) { + throw new IllegalArgumentException("illegal parameter"); + } + return parameters[paramID-1]; + } + + public SettingsFrame setParameter(int paramID, int value) { + if (paramID > MAX_PARAM) { + throw new IllegalArgumentException("illegal parameter"); + } + parameters[paramID-1] = value; + return this; + } + + @Override + void readIncomingImpl(ByteBufferConsumer bc) throws IOException { + if (length % 6 != 0) { + throw new IOException("Protocol error: invalid settings frame"); + } + int n = length / 6; + for (int i=0; i 0 || id <= MAX_PARAM) { + // a known parameter. Ignore otherwise + parameters[id-1] = val; + } + } + } + + @Override + void computeLength() { + length = 0; + for (int i : parameters) { + if (i != -1) { + length += 6; + } + } + } + + @Override + void writeOutgoing(ByteBufferGenerator bg) { + super.writeOutgoing(bg); + ByteBuffer buf = bg.getBuffer(length); + for (int i = 0; i < MAX_PARAM; i++) { + if (parameters[i] != -1) { + buf.putShort((short)(i+1)); + buf.putInt(parameters[i]); + } + } + } + + private static final int K = 1024; + + public static SettingsFrame getDefaultSettings() { + SettingsFrame f = new SettingsFrame(); + // TODO: check these values + f.setParameter(ENABLE_PUSH, 1); + f.setParameter(HEADER_TABLE_SIZE, 4 * K); + f.setParameter(MAX_CONCURRENT_STREAMS, 35); + f.setParameter(INITIAL_WINDOW_SIZE, 16 * K); + f.setParameter(MAX_FRAME_SIZE, 16 * K); + return f; + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/Stream.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Stream.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Stream.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -24,78 +24,819 @@ package java.net.http; +import sun.net.httpclient.hpack.DecodingCallback; + import java.io.IOException; -import java.io.UncheckedIOException; import java.net.URI; import java.nio.ByteBuffer; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; +import java.util.function.BiFunction; import java.util.function.LongConsumer; /** - * Http/2 Stream + * Http/2 Stream handling. + * + * REQUESTS + * + * sendHeadersOnly() -- assembles HEADERS frame and puts on connection outbound Q + * + * sendRequest() -- sendHeadersOnly() + sendBody() + * + * sendBody() -- in calling thread: obeys all flow control (so may block) + * obtains data from request body processor and places on connection + * outbound Q. + * + * sendBodyAsync() -- calls sendBody() in an executor thread. + * + * sendHeadersAsync() -- calls sendHeadersOnly() which does not block + * + * sendRequestAsync() -- calls sendRequest() in an executor thread + * + * RESPONSES + * + * Multiple responses can be received per request. Responses are queued up on + * a LinkedList of CF and the the first one on the list is completed + * with the next response + * + * getResponseAsync() -- queries list of response CFs and returns first one + * if one exists. Otherwise, creates one and adds it to list + * and returns it. Completion is achieved through the + * incoming() upcall from connection reader thread. + * + * getResponse() -- calls getResponseAsync() and waits for CF to complete + * + * responseBody() -- in calling thread: blocks for incoming DATA frames on + * stream inputQ. Obeys remote and local flow control so may block. + * Calls user response body processor with data buffers. + * + * responseBodyAsync() -- calls responseBody() in an executor thread. + * + * incoming() -- entry point called from connection reader thread. Frames are + * either handled immediately without blocking or for data frames + * placed on the stream's inputQ which is consumed by the stream's + * reader thread. + * + * PushedStream sub class + * ====================== + * Sending side methods are not used because the request comes from a PUSH_PROMISE + * frame sent by the server. When a PUSH_PROMISE is received the PushedStream + * is created. PushedStream does not use responseCF list as there can be only + * one response. The CF is created when the object created and when the response + * HEADERS frame is received the object is completed. */ class Stream extends ExchangeImpl { - void debugPrint() { - } + final Queue inputQ; + + volatile int streamid; + + long responseContentLen = -1; + long responseBytesProcessed = 0; + long requestContentLen; + + Http2Connection connection; + HttpClientImpl client; + final HttpRequestImpl request; + final DecodingCallback rspHeadersConsumer; + HttpHeadersImpl responseHeaders; + final HttpHeadersImpl requestHeaders; + final HttpHeadersImpl requestPseudoHeaders; + HttpResponse.BodyProcessor responseProcessor; + final HttpRequest.BodyProcessor requestProcessor; + HttpResponse response; + + // state flags + boolean requestSent, responseReceived; + + final FlowController userRequestFlowController = + new FlowController(); + final FlowController remoteRequestFlowController = + new FlowController(); + final FlowController responseFlowController = + new FlowController(); + + final ExecutorWrapper executor; @Override @SuppressWarnings("unchecked") CompletableFuture responseBodyAsync(HttpResponse.BodyProcessor processor) { - return null; + this.responseProcessor = processor; + CompletableFuture cf; + try { + T body = processor.onResponseBodyStart( + responseContentLen, responseHeaders, + responseFlowController); // TODO: filter headers + if (body != null) { + cf = CompletableFuture.completedFuture(body); + receiveDataAsync(processor); + } else + cf = receiveDataAsync(processor); + } catch (IOException e) { + cf = CompletableFuture.failedFuture(e); + } + PushGroup pg = request.pushGroup(); + if (pg != null) { + // if an error occurs make sure it is recorded in the PushGroup + cf = cf.whenComplete((t,e) -> pg.pushError(e)); + } + return cf; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("streamid: ") + .append(streamid); + return sb.toString(); + } + + // pushes entire response body into response processor + // blocking when required by local or remote flow control + void receiveData() throws IOException { + Http2Frame frame; + DataFrame df = null; + try { + do { + frame = inputQ.take(); + if (!(frame instanceof DataFrame)) { + assert false; + continue; + } + df = (DataFrame) frame; + int len = df.getDataLength(); + ByteBuffer[] buffers = df.getData(); + for (ByteBuffer b : buffers) { + responseFlowController.take(); + responseProcessor.onResponseBodyChunk(b); + } + sendWindowUpdate(len); + } while (!df.getFlag(DataFrame.END_STREAM)); + } catch (InterruptedException e) { + throw new IOException(e); + } + } + + private CompletableFuture receiveDataAsync(HttpResponse.BodyProcessor processor) { + CompletableFuture cf = new CompletableFuture<>(); + executor.execute(() -> { + try { + receiveData(); + T body = processor.onResponseComplete(); + cf.complete(body); + responseReceived(); + } catch (Throwable t) { + cf.completeExceptionally(t); + } + }, null); + return cf; + } + + private void sendWindowUpdate(int increment) + throws IOException, InterruptedException { + if (increment == 0) + return; + LinkedList list = new LinkedList<>(); + WindowUpdateFrame frame = new WindowUpdateFrame(); + frame.streamid(streamid); + frame.setUpdate(increment); + list.add(frame); + frame = new WindowUpdateFrame(); + frame.streamid(0); + frame.setUpdate(increment); + list.add(frame); + connection.sendFrames(list); + } + + @Override + CompletableFuture sendBodyAsync() { + final CompletableFuture cf = new CompletableFuture<>(); + executor.execute(() -> { + try { + sendBodyImpl(); + cf.complete(null); + } catch (IOException | InterruptedException e) { + cf.completeExceptionally(e); + } + }, null); + return cf; + } + + @SuppressWarnings("unchecked") + Stream(HttpClientImpl client, Http2Connection connection, Exchange e) { + super(e); + this.client = client; + this.connection = connection; + this.request = e.request(); + this.requestProcessor = request.requestProcessor(); + responseHeaders = new HttpHeadersImpl(); + requestHeaders = new HttpHeadersImpl(); + rspHeadersConsumer = (name, value) -> { + responseHeaders.addHeader(name.toString(), value.toString()); + }; + this.executor = client.executorWrapper(); + //this.response_cf = new CompletableFuture(); + this.requestPseudoHeaders = new HttpHeadersImpl(); + // NEW + this.inputQ = new Queue<>(); + } + + @SuppressWarnings("unchecked") + Stream(HttpClientImpl client, Http2Connection connection, HttpRequestImpl req) { + super(null); + this.client = client; + this.connection = connection; + this.request = req; + this.requestProcessor = null; + responseHeaders = new HttpHeadersImpl(); + requestHeaders = new HttpHeadersImpl(); + rspHeadersConsumer = (name, value) -> { + responseHeaders.addHeader(name.toString(), value.toString()); + }; + this.executor = client.executorWrapper(); + //this.response_cf = new CompletableFuture(); + this.requestPseudoHeaders = new HttpHeadersImpl(); + // NEW + this.inputQ = new Queue<>(); } - Stream(HttpClientImpl client, Http2Connection connection, Exchange e) { - super(e); + /** + * Entry point from Http2Connection reader thread. + * + * Data frames will be removed by response body thread. + * + * @param frame + * @throws IOException + */ + void incoming(Http2Frame frame) throws IOException, InterruptedException { + if ((frame instanceof HeaderFrame) && ((HeaderFrame)frame).endHeaders()) { + // Complete headers accumulated. handle response. + // It's okay if there are multiple HeaderFrames. + handleResponse(); + } else if (frame instanceof DataFrame) { + inputQ.put(frame); + } else { + otherFrame(frame); + } + } + + void otherFrame(Http2Frame frame) throws IOException { + switch (frame.type()) { + case WindowUpdateFrame.TYPE: + incoming_windowUpdate((WindowUpdateFrame) frame); + break; + case ResetFrame.TYPE: + incoming_reset((ResetFrame) frame); + break; + case PriorityFrame.TYPE: + incoming_priority((PriorityFrame) frame); + break; + default: + String msg = "Unexpected frame: " + frame.toString(); + throw new IOException(msg); + } + } + + // The Hpack decoder decodes into one of these consumers of name,value pairs + + DecodingCallback rspHeadersConsumer() { + return rspHeadersConsumer; + } + + // create and return the HttpResponseImpl + protected void handleResponse() throws IOException { + HttpConnection c = connection.connection; // TODO: improve + long statusCode = responseHeaders + .firstValueAsLong(":status") + .orElseThrow(() -> new IOException("no statuscode in response")); + + this.response = new HttpResponseImpl((int)statusCode, exchange, responseHeaders, null, + c.sslParameters(), HttpClient.Version.HTTP_2, c); + this.responseContentLen = responseHeaders + .firstValueAsLong("content-length") + .orElse(-1L); + // different implementations for normal streams and pushed streams + completeResponse(response); + } + + void incoming_reset(ResetFrame frame) { + // TODO: implement reset + int error = frame.getErrorCode(); + IOException e = new IOException(ErrorFrame.stringForCode(error)); + completeResponseExceptionally(e); + throw new UnsupportedOperationException("Not implemented"); + } + + void incoming_priority(PriorityFrame frame) { + // TODO: implement priority + throw new UnsupportedOperationException("Not implemented"); + } + + void incoming_windowUpdate(WindowUpdateFrame frame) { + int amount = frame.getUpdate(); + if (amount > 0) + remoteRequestFlowController.accept(amount); + } + + void incoming_pushPromise(HttpRequestImpl pushReq, PushedStream pushStream) throws IOException { + if (Log.requests()) { + Log.logRequest("PUSH_PROMISE: " + pushReq.toString()); + } + PushGroup pushGroup = request.pushGroup(); + if (pushGroup == null) { + cancelImpl(new IllegalStateException("unexpected push promise")); + } + // get the handler and call it. + BiFunction,Boolean> ph = + pushGroup.pushHandler(); + + CompletableFuture pushCF = pushStream + .getResponseAsync(null) + .thenApply(r -> (HttpResponse)r); + boolean accept = ph.apply(pushReq, pushCF); + if (!accept) { + IOException ex = new IOException("Stream cancelled by user"); + cancelImpl(ex); + pushCF.completeExceptionally(ex); + } else { + pushStream.requestSent(); + pushGroup.addPush(); + } + } + + private OutgoingHeaders headerFrame(long contentLength) { + HttpHeadersImpl h = request.getSystemHeaders(); + if (contentLength > 0) { + h.setHeader("content-length", Long.toString(contentLength)); + } + setPseudoHeaderFields(); + OutgoingHeaders f = new OutgoingHeaders(h, request.getUserHeaders(), this); + if (contentLength == 0) { + f.setFlag(HeadersFrame.END_STREAM); + } + return f; + } + + private void setPseudoHeaderFields() { + HttpHeadersImpl hdrs = requestPseudoHeaders; + String method = request.method(); + hdrs.setHeader(":method", method); + URI uri = request.uri(); + hdrs.setHeader(":scheme", uri.getScheme()); + // TODO: userinfo deprecated. Needs to be removed + hdrs.setHeader(":authority", uri.getAuthority()); + // TODO: ensure header names beginning with : not in user headers + String query = uri.getQuery(); + String path = uri.getPath(); + if (path == null) { + if (method.equalsIgnoreCase("OPTIONS")) { + path = "*"; + } else { + path = "/"; + } + } + if (query != null) { + path += "?" + query; + } + hdrs.setHeader(":path", path); + } + + HttpHeadersImpl getRequestPseudoHeaders() { + return requestPseudoHeaders; } @Override HttpResponseImpl getResponse() throws IOException { - return null; + try { + return getResponseAsync(null).join(); + } catch (Throwable e) { + Throwable t = e.getCause(); + if (t instanceof IOException) { + throw (IOException)t; + } + throw e; + } } @Override void sendRequest() throws IOException, InterruptedException { + sendHeadersOnly(); + sendBody(); + } + + /** + * A simple general purpose blocking flow controller + */ + class FlowController implements LongConsumer { + int permits; + + FlowController() { + this.permits = 0; + } + + @Override + public synchronized void accept(long n) { + if (n < 1) { + throw new InternalError("FlowController.accept called with " + n); + } + if (permits == 0) { + permits += n; + notifyAll(); + } else { + permits += n; + } + } + + public synchronized void take() throws InterruptedException { + take(1); + } + + public synchronized void take(int amount) throws InterruptedException { + assert permits >= 0; + while (permits < amount) { + int n = Math.min(amount, permits); + permits -= n; + amount -= n; + if (amount > 0) + wait(); + } + } } @Override void sendHeadersOnly() throws IOException, InterruptedException { + if (Log.requests() && request != null) { + Log.logRequest(request.toString()); + } + requestContentLen = requestProcessor.onRequestStart(request, userRequestFlowController); + OutgoingHeaders f = headerFrame(requestContentLen); + connection.sendFrame(f); } @Override void sendBody() throws IOException, InterruptedException { + sendBodyImpl(); } + void registerStream(int id) { + this.streamid = id; + connection.putStream(this, streamid); + } + + DataFrame getDataFrame() throws IOException, InterruptedException { + userRequestFlowController.take(); + int maxpayloadLen = connection.getMaxSendFrameSize() - 9; + ByteBuffer buffer = connection.getBuffer(); + buffer.limit(maxpayloadLen); + boolean complete = requestProcessor.onRequestBodyChunk(buffer); + buffer.flip(); + int amount = buffer.remaining(); + // wait for flow control if necessary. Following method will block + // until after headers frame is sent, so correct streamid is set. + remoteRequestFlowController.take(amount); + connection.obtainSendWindow(amount); + + DataFrame df = new DataFrame(); + df.streamid(streamid); + if (complete) { + df.setFlag(DataFrame.END_STREAM); + } + df.setData(buffer); + df.computeLength(); + return df; + } + + @Override CompletableFuture sendHeadersAsync() { - return null; + try { + sendHeadersOnly(); + return CompletableFuture.completedFuture(null); + } catch (IOException | InterruptedException ex) { + return CompletableFuture.failedFuture(ex); + } } + /** + * A List of responses relating to this stream. Normally there is only + * one response, but intermediate responses like 100 are allowed + * and must be passed up to higher level before continuing. Deals with races + * such as if responses are returned before the CFs get created by + * getResponseAsync() + */ + + final List> response_cfs = new LinkedList<>(); + @Override CompletableFuture getResponseAsync(Void v) { - return null; + CompletableFuture cf; + synchronized (response_cfs) { + if (!response_cfs.isEmpty()) { + cf = response_cfs.remove(0); + } else { + cf = new CompletableFuture<>(); + response_cfs.add(cf); + } + } + PushGroup pg = request.pushGroup(); + if (pg != null) { + // if an error occurs make sure it is recorded in the PushGroup + cf = cf.whenComplete((t,e) -> pg.pushError(e)); + } + return cf; + } + + /** + * Completes the first uncompleted CF on list, and removes it. If there is no + * uncompleted CF then creates one (completes it) and adds to list + */ + void completeResponse(HttpResponse r) { + HttpResponseImpl resp = (HttpResponseImpl)r; + synchronized (response_cfs) { + for (CompletableFuture cf : response_cfs) { + if (!cf.isDone()) { + cf.complete(resp); + response_cfs.remove(cf); + //responseHeaders = new HttpHeadersImpl(); // for any following header blocks + return; + } else + System.err.println("Stream: " + this + " ALREADY DONE"); + } + response_cfs.add(CompletableFuture.completedFuture(resp)); + //responseHeaders = new HttpHeadersImpl(); // for any following header blocks + } } - @Override - CompletableFuture sendBodyAsync() { - return null; + // methods to update state and remove stream when finished + + synchronized void requestSent() { + requestSent = true; + if (responseReceived) + connection.deleteStream(this); + } + + synchronized void responseReceived() { + responseReceived = true; + if (requestSent) + connection.deleteStream(this); + PushGroup pg = request.pushGroup(); + if (pg != null) + pg.noMorePushes(); + } + + /** + * same as above but for errors + * + * @param t + */ + void completeResponseExceptionally(Throwable t) { + synchronized (response_cfs) { + for (CompletableFuture cf : response_cfs) { + if (!cf.isDone()) { + cf.completeExceptionally(t); + response_cfs.remove(cf); + return; + } + } + response_cfs.add(CompletableFuture.failedFuture(t)); + } + } + + void sendBodyImpl() throws IOException, InterruptedException { + if (requestContentLen == 0) { + // no body + return; + } + DataFrame df; + do { + df = getDataFrame(); + // TODO: check accumulated content length (if not checked below) + connection.sendFrame(df); + } while (!df.getFlag(DataFrame.END_STREAM)); + requestSent(); } @Override void cancel() { + cancelImpl(new Exception("Cancelled")); } + void cancelImpl(Throwable e) { + Log.logTrace("cancelling stream: {0}\n", e.toString()); + inputQ.close(); + try { + connection.resetStream(streamid, ResetFrame.CANCEL); + } catch (IOException | InterruptedException ex) { + Log.logError(ex); + } + } + @Override CompletableFuture sendRequestAsync() { - return null; + CompletableFuture cf = new CompletableFuture<>(); + executor.execute(() -> { + try { + sendRequest(); + cf.complete(null); + } catch (IOException |InterruptedException e) { + cf.completeExceptionally(e); + } + }, null); + return cf; } @Override T responseBody(HttpResponse.BodyProcessor processor) throws IOException { - return null; + this.responseProcessor = processor; + T body = processor.onResponseBodyStart( + responseContentLen, responseHeaders, + responseFlowController); // TODO: filter headers + if (body == null) { + receiveData(); + return processor.onResponseComplete(); + } else + receiveDataAsync(processor); + responseReceived(); + return body; + } + + // called from Http2Connection reader thread + synchronized void updateOutgoingWindow(int update) { + remoteRequestFlowController.accept(update); + } + + void close(String msg) { + cancel(); + } + + static class PushedStream extends Stream { + final PushGroup pushGroup; + final private Stream parent; // used by server push streams + // push streams need the response CF allocated up front as it is + // given directly to user via the multi handler callback function. + final CompletableFuture pushCF; + final HttpRequestImpl pushReq; + + PushedStream(PushGroup pushGroup, HttpClientImpl client, + Http2Connection connection, Stream parent, + HttpRequestImpl pushReq) { + super(client, connection, pushReq); + this.pushGroup = pushGroup; + this.pushReq = pushReq; + this.pushCF = new CompletableFuture<>(); + this.parent = parent; + } + + // Following methods call the super class but in case of + // error record it in the PushGroup. The error method is called + // with a null value when no error occurred (is a no-op) + @Override + CompletableFuture sendBodyAsync() { + return super.sendBodyAsync() + .whenComplete((v, t) -> pushGroup.pushError(t)); + } + + @Override + CompletableFuture sendHeadersAsync() { + return super.sendHeadersAsync() + .whenComplete((v, t) -> pushGroup.pushError(t)); + } + + @Override + CompletableFuture sendRequestAsync() { + return super.sendRequestAsync() + .whenComplete((v, t) -> pushGroup.pushError(t)); + } + + @Override + CompletableFuture getResponseAsync(Void vo) { + return pushCF.whenComplete((v, t) -> pushGroup.pushError(t)); + } + + @Override + CompletableFuture responseBodyAsync(HttpResponse.BodyProcessor processor) { + return super.responseBodyAsync(processor) + .whenComplete((v, t) -> pushGroup.pushError(t)); + } + + @Override + void completeResponse(HttpResponse r) { + HttpResponseImpl resp = (HttpResponseImpl)r; + Utils.logResponse(resp); + pushCF.complete(resp); + } + + @Override + void completeResponseExceptionally(Throwable t) { + pushCF.completeExceptionally(t); + } + + @Override + synchronized void responseReceived() { + super.responseReceived(); + pushGroup.pushCompleted(); + } + + // create and return the PushResponseImpl + @Override + protected void handleResponse() { + HttpConnection c = connection.connection; // TODO: improve + long statusCode = responseHeaders + .firstValueAsLong(":status") + .orElse(-1L); + + if (statusCode == -1L) + completeResponseExceptionally(new IOException("No status code")); + ImmutableHeaders h = new ImmutableHeaders(responseHeaders, Utils.ALL_HEADERS); + this.response = new HttpResponseImpl((int)statusCode, pushReq, h, this, + c.sslParameters()); + this.responseContentLen = responseHeaders + .firstValueAsLong("content-length") + .orElse(-1L); + // different implementations for normal streams and pushed streams + completeResponse(response); + } + } + + /** + * One PushGroup object is associated with the parent Stream of + * the pushed Streams. This keeps track of all common state associated + * with the pushes. + */ + static class PushGroup { + // the overall completion object, completed when all pushes are done. + final CompletableFuture resultCF; + Throwable error; // any exception that occured during pushes + + // CF for main response + final CompletableFuture mainResponse; + + // user's processor object + final HttpResponse.MultiProcessor multiProcessor; + + // per push handler function provided by processor + final private BiFunction, + Boolean> pushHandler; + int numberOfPushes; + int remainingPushes; + boolean noMorePushes = false; + + PushGroup(HttpResponse.MultiProcessor multiProcessor, HttpRequestImpl req) { + this.resultCF = new CompletableFuture<>(); + this.mainResponse = new CompletableFuture<>(); + this.multiProcessor = multiProcessor; + this.pushHandler = multiProcessor.onStart(req, mainResponse); + } + + CompletableFuture groupResult() { + return resultCF; + } + + CompletableFuture mainResponse() { + return mainResponse; + } + + private BiFunction, Boolean> pushHandler() + { + return pushHandler; + } + + synchronized void addPush() { + numberOfPushes++; + remainingPushes++; + } + + synchronized int numberOfPushes() { + return numberOfPushes; + } + // This is called when the main body response completes because it means + // no more PUSH_PROMISEs are possible + synchronized void noMorePushes() { + noMorePushes = true; + checkIfCompleted(); + } + + synchronized void pushCompleted() { + remainingPushes--; + checkIfCompleted(); + } + + synchronized void checkIfCompleted() { + if (remainingPushes == 0 && error == null && noMorePushes) { + T overallResult = multiProcessor.onComplete(); + resultCF.complete(overallResult); + } + } + + synchronized void pushError(Throwable t) { + if (t == null) + return; + this.error = t; + resultCF.completeExceptionally(t); + } } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.httpclient/share/classes/java/net/http/Utils.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Utils.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Utils.java Tue May 03 12:25:20 2016 -0700 @@ -21,28 +21,37 @@ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any */ - package java.net.http; +import sun.net.NetProperties; + +import javax.net.ssl.SSLParameters; import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.IOException; import java.io.PrintStream; import java.io.UnsupportedEncodingException; +import java.net.InetSocketAddress; import java.net.NetPermission; import java.net.URI; import java.net.URLPermission; +import java.nio.Buffer; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; -import javax.net.ssl.SSLParameters; -import sun.net.NetProperties; +import java.util.concurrent.CompletableFuture; +import java.util.function.LongBinaryOperator; +import java.util.function.Predicate; /** * Miscellaneous utilities */ -class Utils { +final class Utils { /** * Allocated buffer size. Must never be higher than 16K. But can be lower @@ -51,7 +60,59 @@ */ public static final int BUFSIZE = 16 * 1024; - /** Validates a RFC7230 token */ + private static final Set DISALLOWED_HEADERS_SET = Set.of( + "authorization", "connection", "cookie", "content-length", + "date", "expect", "from", "host", "origin", "proxy-authorization", + "referer", "user-agent", "upgrade", "via", "warning"); + + static final Predicate + ALLOWED_HEADERS = header -> !Utils.DISALLOWED_HEADERS_SET.contains(header); + + static final Predicate + ALL_HEADERS = header -> true; + + static InetSocketAddress getAddress(HttpRequestImpl req) { + URI uri = req.uri(); + if (uri == null) { + return req.authority(); + } + int port = uri.getPort(); + if (port == -1) { + if (uri.getScheme().equalsIgnoreCase("https")) { + port = 443; + } else { + port = 80; + } + } + String host = uri.getHost(); + if (req.proxy() == null) { + return new InetSocketAddress(host, port); + } else { + return InetSocketAddress.createUnresolved(host, port); + } + } + + /** + * Puts position to limit and limit to capacity so we can resume reading + * into this buffer, but if required > 0 then limit may be reduced so that + * no more than required bytes are read next time. + */ + static void resumeChannelRead(ByteBuffer buf, int required) { + int limit = buf.limit(); + buf.position(limit); + int capacity = buf.capacity() - limit; + if (required > 0 && required < capacity) { + buf.limit(limit + required); + } else { + buf.limit(buf.capacity()); + } + } + + private Utils() { } + + /** + * Validates a RFC7230 token + */ static void validateToken(String token, String errormsg) { int length = token.length(); for (int i = 0; i < length; i++) { @@ -69,7 +130,7 @@ } /** - * Return sthe security permission required for the given details. + * Returns the security permission required for the given details. * If method is CONNECT, then uri must be of form "scheme://host:port" */ static URLPermission getPermission(URI uri, @@ -117,13 +178,13 @@ } static int getIntegerNetProperty(String name, int defaultValue) { - return AccessController.doPrivileged((PrivilegedAction)() -> - NetProperties.getInteger(name, defaultValue) ); + return AccessController.doPrivileged((PrivilegedAction) () -> + NetProperties.getInteger(name, defaultValue)); } static String getNetProperty(String name) { - return AccessController.doPrivileged((PrivilegedAction)() -> - NetProperties.get(name) ); + return AccessController.doPrivileged((PrivilegedAction) () -> + NetProperties.get(name)); } static SSLParameters copySSLParameters(SSLParameters p) { @@ -134,7 +195,9 @@ p1.setEndpointIdentificationAlgorithm(p.getEndpointIdentificationAlgorithm()); p1.setMaximumPacketSize(p.getMaximumPacketSize()); p1.setNeedClientAuth(p.getNeedClientAuth()); - p1.setProtocols(p.getProtocols().clone()); + String[] protocols = p.getProtocols(); + if (protocols != null) + p1.setProtocols(protocols.clone()); p1.setSNIMatchers(p.getSNIMatchers()); p1.setServerNames(p.getServerNames()); p1.setUseCipherSuitesOrder(p.getUseCipherSuitesOrder()); @@ -142,33 +205,14 @@ return p1; } - - /** Resumes reading into the given buffer. */ - static void unflip(ByteBuffer buf) { - buf.position(buf.limit()); - buf.limit(buf.capacity()); - } - /** * Set limit to position, and position to mark. - * - * - * @param buffer - * @param mark */ static void flipToMark(ByteBuffer buffer, int mark) { buffer.limit(buffer.position()); buffer.position(mark); } - /** Compact and leave ready for reading. */ - static void compact(List buffers) { - for (ByteBuffer b : buffers) { - b.compact(); - b.flip(); - } - } - static String stackTrace(Throwable t) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); String s = null; @@ -182,8 +226,10 @@ return s; } - /** Copies as much of src to dst as possible. */ - static void copy (ByteBuffer src, ByteBuffer dst) { + /** + * Copies as much of src to dst as possible. + */ + static void copy(ByteBuffer src, ByteBuffer dst) { int srcLen = src.remaining(); int dstLen = dst.remaining(); if (srcLen > dstLen) { @@ -204,18 +250,101 @@ return dst; } - static String combine(String[] s) { + // + // Helps to trim long names (packages, nested/inner types) in logs/toString + // + static String toStringSimple(Object o) { + return o.getClass().getSimpleName() + "@" + + Integer.toHexString(System.identityHashCode(o)); + } + + // + // 1. It adds a number of remaining bytes; + // 2. Standard Buffer-type toString for CharBuffer (since it adheres to the + // contract of java.lang.CharSequence.toString() which is both not too + // useful and not too private) + // + static String toString(Buffer b) { + return toStringSimple(b) + + "[pos=" + b.position() + + " lim=" + b.limit() + + " cap=" + b.capacity() + + " rem=" + b.remaining() + "]"; + } + + static String toString(CharSequence s) { + return s == null + ? "null" + : toStringSimple(s) + "[len=" + s.length() + "]"; + } + + static String dump(Object... objects) { + return Arrays.toString(objects); + } + + static final System.Logger logger = System.getLogger("java.net.http.WebSocket"); + + static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.allocate(0); + + static String webSocketSpecViolation(String section, String detail) { + return "RFC 6455 " + section + " " + detail; + } + + static void logResponse(HttpResponseImpl r) { + if (!Log.requests()) { + return; + } StringBuilder sb = new StringBuilder(); - sb.append('['); - boolean first = true; - for (String s1 : s) { - if (!first) { - sb.append(", "); - first = false; + String method = r.request().method(); + URI uri = r.uri(); + String uristring = uri == null ? "" : uri.toString(); + sb.append('(').append(method).append(" ").append(uristring).append(") ").append(Integer.toString(r.statusCode())); + Log.logResponse(sb.toString()); + } + + static int remaining(ByteBuffer[] bufs) { + int remain = 0; + for (ByteBuffer buf : bufs) + remain += buf.remaining(); + return remain; + } + + // assumes buffer was written into starting at position zero + static void unflip(ByteBuffer buf) { + buf.position(buf.limit()); + buf.limit(buf.capacity()); + } + + static void close(Closeable... chans) { + for (Closeable chan : chans) { + System.err.println("Closing " + chan); + try { + chan.close(); + } catch (IOException e) { } - sb.append(s1); } - sb.append(']'); - return sb.toString(); + } + + static ByteBuffer[] reduce(ByteBuffer[] bufs, int start, int number) { + if (start == 0 && number == bufs.length) + return bufs; + ByteBuffer[] nbufs = new ByteBuffer[number]; + int j = 0; + for (int i=start; iHigh level HTTP API - * This provides a high-level client interface to HTTP (versions 1.1 and 2). - * Synchronous and asynchronous (via - * {@link java.util.concurrent.CompletableFuture}) modes are provided. The main - * classes defined are: + *

High level HTTP and WebSocket API

+ * This provides a high-level client interfaces to HTTP (versions 1.1 and 2) + * and WebSocket. Synchronous and asynchronous (via {@link + * java.util.concurrent.CompletableFuture}) modes are provided for HTTP. + * WebSocket works in asynchronous mode only. The main types defined are: *
    *
  • {@link java.net.http.HttpClient}
  • *
  • {@link java.net.http.HttpRequest}
  • diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.logging/share/classes/java/util/logging/LogManager.java --- a/jdk/src/java.logging/share/classes/java/util/logging/LogManager.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.logging/share/classes/java/util/logging/LogManager.java Tue May 03 12:25:20 2016 -0700 @@ -231,13 +231,15 @@ cname = System.getProperty("java.util.logging.manager"); if (cname != null) { try { - Class clz = ClassLoader.getSystemClassLoader() - .loadClass(cname); - mgr = (LogManager) clz.newInstance(); + @SuppressWarnings("deprecation") + Object tmp = ClassLoader.getSystemClassLoader() + .loadClass(cname).newInstance(); + mgr = (LogManager) tmp; } catch (ClassNotFoundException ex) { - Class clz = Thread.currentThread() - .getContextClassLoader().loadClass(cname); - mgr = (LogManager) clz.newInstance(); + @SuppressWarnings("deprecation") + Object tmp = Thread.currentThread() + .getContextClassLoader().loadClass(cname).newInstance(); + mgr = (LogManager) tmp; } } } catch (Exception ex) { @@ -991,8 +993,9 @@ List handlers = new ArrayList<>(names.length); for (String type : names) { try { - Class clz = ClassLoader.getSystemClassLoader().loadClass(type); - Handler hdl = (Handler) clz.newInstance(); + @SuppressWarnings("deprecation") + Object o = ClassLoader.getSystemClassLoader().loadClass(type).newInstance(); + Handler hdl = (Handler) o; // Check if there is a property defining the // this handler's level. String levs = getProperty(type + ".level"); @@ -1330,11 +1333,13 @@ // calling readConfiguration(InputStream) with a suitable stream. try { Class clz = ClassLoader.getSystemClassLoader().loadClass(cname); - clz.newInstance(); + @SuppressWarnings("deprecation") + Object witness = clz.newInstance(); return; } catch (ClassNotFoundException ex) { Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname); - clz.newInstance(); + @SuppressWarnings("deprecation") + Object witness = clz.newInstance(); return; } } catch (Exception ex) { @@ -1561,7 +1566,8 @@ for (String word : names) { try { Class clz = ClassLoader.getSystemClassLoader().loadClass(word); - clz.newInstance(); + @SuppressWarnings("deprecation") + Object witness = clz.newInstance(); } catch (Exception ex) { System.err.println("Can't load config class \"" + word + "\""); System.err.println("" + ex); @@ -2307,8 +2313,9 @@ String val = getProperty(name); try { if (val != null) { - Class clz = ClassLoader.getSystemClassLoader().loadClass(val); - return (Filter) clz.newInstance(); + @SuppressWarnings("deprecation") + Object o = ClassLoader.getSystemClassLoader().loadClass(val).newInstance(); + return (Filter) o; } } catch (Exception ex) { // We got one of a variety of exceptions in creating the @@ -2328,8 +2335,9 @@ String val = getProperty(name); try { if (val != null) { - Class clz = ClassLoader.getSystemClassLoader().loadClass(val); - return (Formatter) clz.newInstance(); + @SuppressWarnings("deprecation") + Object o = ClassLoader.getSystemClassLoader().loadClass(val).newInstance(); + return (Formatter) o; } } catch (Exception ex) { // We got one of a variety of exceptions in creating the diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.logging/share/classes/java/util/logging/MemoryHandler.java --- a/jdk/src/java.logging/share/classes/java/util/logging/MemoryHandler.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.logging/share/classes/java/util/logging/MemoryHandler.java Tue May 03 12:25:20 2016 -0700 @@ -117,7 +117,9 @@ Class clz; try { clz = ClassLoader.getSystemClassLoader().loadClass(targetName); - target = (Handler) clz.newInstance(); + @SuppressWarnings("deprecation") + Object o = clz.newInstance(); + target = (Handler) o; } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { throw new RuntimeException("MemoryHandler can't load handler target \"" + targetName + "\"" , e); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.management/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java --- a/jdk/src/java.management/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.management/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java Tue May 03 12:25:20 2016 -0700 @@ -655,7 +655,9 @@ final Object[] openArray = (Object[]) openValue; final Collection valueCollection; try { - valueCollection = cast(collectionClass.newInstance()); + @SuppressWarnings("deprecation") + Collection tmp = collectionClass.newInstance(); + valueCollection = cast(tmp); } catch (Exception e) { throw invalidObjectException("Cannot create collection", e); } @@ -1114,7 +1116,9 @@ try { final Class targetClass = getTargetClass(); ReflectUtil.checkPackageAccess(targetClass); - o = targetClass.newInstance(); + @SuppressWarnings("deprecation") + Object tmp = targetClass.newInstance(); + o = tmp; for (int i = 0; i < itemNames.length; i++) { if (cd.containsKey(itemNames[i])) { Object openItem = cd.get(itemNames[i]); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.management/share/classes/javax/management/MBeanServerFactory.java --- a/jdk/src/java.management/share/classes/javax/management/MBeanServerFactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.management/share/classes/javax/management/MBeanServerFactory.java Tue May 03 12:25:20 2016 -0700 @@ -458,6 +458,7 @@ **/ private static MBeanServerBuilder newBuilder(Class builderClass) { try { + @SuppressWarnings("deprecation") final Object abuilder = builderClass.newInstance(); return (MBeanServerBuilder)abuilder; } catch (RuntimeException x) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.management/share/classes/javax/management/remote/JMXConnectorFactory.java --- a/jdk/src/java.management/share/classes/javax/management/remote/JMXConnectorFactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.management/share/classes/javax/management/remote/JMXConnectorFactory.java Tue May 03 12:25:20 2016 -0700 @@ -531,7 +531,9 @@ // We have just proved that this cast is correct Class providerClassT = Util.cast(providerClass); try { - return providerClassT.newInstance(); + @SuppressWarnings("deprecation") + T result = providerClassT.newInstance(); + return result; } catch (Exception e) { final String msg = "Exception when instantiating provider [" + className + diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.naming/share/classes/com/sun/naming/internal/FactoryEnumeration.java --- a/jdk/src/java.naming/share/classes/com/sun/naming/internal/FactoryEnumeration.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.naming/share/classes/com/sun/naming/internal/FactoryEnumeration.java Tue May 03 12:25:20 2016 -0700 @@ -86,7 +86,9 @@ answer = cls; } // Instantiate Class to get factory - answer = ((Class) answer).newInstance(); + @SuppressWarnings("deprecation") + Object tmp = ((Class) answer).newInstance(); + answer = tmp; ref = new NamedWeakReference<>(answer, className); factories.set(posn-1, ref); // replace Class object or null return answer; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.naming/share/classes/com/sun/naming/internal/ResourceManager.java --- a/jdk/src/java.naming/share/classes/com/sun/naming/internal/ResourceManager.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.naming/share/classes/com/sun/naming/internal/ResourceManager.java Tue May 03 12:25:20 2016 -0700 @@ -399,7 +399,9 @@ className = parser.nextToken() + classSuffix; try { // System.out.println("loading " + className); - factory = helper.loadClass(className, loader).newInstance(); + @SuppressWarnings("deprecation") // Class.newInstance + Object tmp = helper.loadClass(className, loader).newInstance(); + factory = tmp; } catch (InstantiationException e) { NamingException ne = new NamingException("Cannot instantiate " + className); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.naming/share/classes/javax/naming/ldap/StartTlsRequest.java --- a/jdk/src/java.naming/share/classes/javax/naming/ldap/StartTlsRequest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.naming/share/classes/javax/naming/ldap/StartTlsRequest.java Tue May 03 12:25:20 2016 -0700 @@ -192,18 +192,12 @@ } try { VersionHelper helper = VersionHelper.getVersionHelper(); - Class clas = helper.loadClass( - "com.sun.jndi.ldap.ext.StartTlsResponseImpl"); - - resp = (StartTlsResponse) clas.newInstance(); + @SuppressWarnings("deprecation") + Object o = helper.loadClass( + "com.sun.jndi.ldap.ext.StartTlsResponseImpl").newInstance(); + resp = (StartTlsResponse) o; - } catch (IllegalAccessException e) { - throw wrapException(e); - - } catch (InstantiationException e) { - throw wrapException(e); - - } catch (ClassNotFoundException e) { + } catch (IllegalAccessException | InstantiationException | ClassNotFoundException e) { throw wrapException(e); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.naming/share/classes/javax/naming/spi/NamingManager.java --- a/jdk/src/java.naming/share/classes/javax/naming/spi/NamingManager.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.naming/share/classes/javax/naming/spi/NamingManager.java Tue May 03 12:25:20 2016 -0700 @@ -159,7 +159,9 @@ } } - return (clas != null) ? (ObjectFactory) clas.newInstance() : null; + @SuppressWarnings("deprecation") // Class.newInstance + ObjectFactory result = (clas != null) ? (ObjectFactory) clas.newInstance() : null; + return result; } @@ -710,8 +712,9 @@ if (factory == null) { try { - factory = (InitialContextFactory) - helper.loadClass(className).newInstance(); + @SuppressWarnings("deprecation") + Object o = helper.loadClass(className).newInstance(); + factory = (InitialContextFactory) o; } catch (Exception e) { NoInitialContextException ne = new NoInitialContextException( diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.prefs/share/classes/java/util/prefs/Preferences.java --- a/jdk/src/java.prefs/share/classes/java/util/prefs/Preferences.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.prefs/share/classes/java/util/prefs/Preferences.java Tue May 03 12:25:20 2016 -0700 @@ -238,10 +238,11 @@ // dependent on the invoking thread. // Checking AllPermission also seems wrong. try { - return (PreferencesFactory) - Class.forName(factoryName, false, - ClassLoader.getSystemClassLoader()) + @SuppressWarnings("deprecation") + Object result =Class.forName(factoryName, false, + ClassLoader.getSystemClassLoader()) .newInstance(); + return (PreferencesFactory)result; } catch (Exception ex) { try { // workaround for javaws, plugin, @@ -250,11 +251,12 @@ if (sm != null) { sm.checkPermission(new java.security.AllPermission()); } - return (PreferencesFactory) - Class.forName(factoryName, false, - Thread.currentThread() - .getContextClassLoader()) + @SuppressWarnings("deprecation") + Object result = Class.forName(factoryName, false, + Thread.currentThread() + .getContextClassLoader()) .newInstance(); + return (PreferencesFactory) result; } catch (Exception e) { throw new InternalError( "Can't instantiate Preferences factory " @@ -299,9 +301,10 @@ platformFactory = "java.util.prefs.FileSystemPreferencesFactory"; } try { - return (PreferencesFactory) - Class.forName(platformFactory, false, - Preferences.class.getClassLoader()).newInstance(); + @SuppressWarnings("deprecation") + Object result = Class.forName(platformFactory, false, + Preferences.class.getClassLoader()).newInstance(); + return (PreferencesFactory) result; } catch (Exception e) { throw new InternalError( "Can't instantiate platform default Preferences factory " diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.rmi/share/classes/java/rmi/activation/ActivationID.java --- a/jdk/src/java.rmi/share/classes/java/rmi/activation/ActivationID.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.rmi/share/classes/java/rmi/activation/ActivationID.java Tue May 03 12:25:20 2016 -0700 @@ -272,6 +272,7 @@ Class refClass = Class.forName(RemoteRef.packagePrefix + "." + in.readUTF()) .asSubclass(RemoteRef.class); + @SuppressWarnings("deprecation") RemoteRef ref = refClass.newInstance(); ref.readExternal(in); activator = (Activator) diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.rmi/share/classes/java/rmi/server/RMIClassLoader.java --- a/jdk/src/java.rmi/share/classes/java/rmi/server/RMIClassLoader.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.rmi/share/classes/java/rmi/server/RMIClassLoader.java Tue May 03 12:25:20 2016 -0700 @@ -681,7 +681,9 @@ Class.forName(providerClassName, false, ClassLoader.getSystemClassLoader()) .asSubclass(RMIClassLoaderSpi.class); - return providerClass.newInstance(); + @SuppressWarnings("deprecation") + RMIClassLoaderSpi result = providerClass.newInstance(); + return result; } catch (ClassNotFoundException e) { throw new NoClassDefFoundError(e.getMessage()); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.rmi/share/classes/java/rmi/server/RemoteObject.java --- a/jdk/src/java.rmi/share/classes/java/rmi/server/RemoteObject.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.rmi/share/classes/java/rmi/server/RemoteObject.java Tue May 03 12:25:20 2016 -0700 @@ -439,18 +439,16 @@ RemoteRef.packagePrefix + "." + refClassName; Class refClass = Class.forName(internalRefClassName); try { - ref = (RemoteRef) refClass.newInstance(); + @SuppressWarnings("deprecation") + Object tmp = refClass.newInstance(); + ref = (RemoteRef) tmp; /* * If this step fails, assume we found an internal * class that is not meant to be a serializable ref * type. */ - } catch (InstantiationException e) { - throw new ClassNotFoundException(internalRefClassName, e); - } catch (IllegalAccessException e) { - throw new ClassNotFoundException(internalRefClassName, e); - } catch (ClassCastException e) { + } catch (InstantiationException | IllegalAccessException | ClassCastException e) { throw new ClassNotFoundException(internalRefClassName, e); } ref.readExternal(in); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.rmi/share/classes/sun/rmi/server/Activation.java --- a/jdk/src/java.rmi/share/classes/sun/rmi/server/Activation.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.rmi/share/classes/sun/rmi/server/Activation.java Tue May 03 12:25:20 2016 -0700 @@ -2066,7 +2066,9 @@ try { Class execPolicyClass = getRMIClass(execPolicyClassName); - execPolicy = execPolicyClass.newInstance(); + @SuppressWarnings("deprecation") + Object tmp = execPolicyClass.newInstance(); + execPolicy = tmp; execPolicyMethod = execPolicyClass.getMethod("checkExecCommand", ActivationGroupDesc.class, diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.smartcardio/share/classes/javax/smartcardio/TerminalFactory.java --- a/jdk/src/java.smartcardio/share/classes/javax/smartcardio/TerminalFactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.smartcardio/share/classes/javax/smartcardio/TerminalFactory.java Tue May 03 12:25:20 2016 -0700 @@ -111,8 +111,9 @@ type = "PC/SC"; Provider sun = Security.getProvider("SunPCSC"); if (sun == null) { - Class clazz = Class.forName("sun.security.smartcardio.SunPCSC"); - sun = (Provider)clazz.newInstance(); + @SuppressWarnings("deprecation") + Object o = Class.forName("sun.security.smartcardio.SunPCSC").newInstance(); + sun = (Provider)o; } factory = TerminalFactory.getInstance(type, null, sun); } catch (Exception e) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.sql.rowset/share/classes/com/sun/rowset/CachedRowSetImpl.java --- a/jdk/src/java.sql.rowset/share/classes/com/sun/rowset/CachedRowSetImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.sql.rowset/share/classes/com/sun/rowset/CachedRowSetImpl.java Tue May 03 12:25:20 2016 -0700 @@ -2962,7 +2962,9 @@ SQLData obj = null; try { ReflectUtil.checkPackageAccess(c); - obj = (SQLData) c.newInstance(); + @SuppressWarnings("deprecation") + Object tmp = c.newInstance(); + obj = (SQLData) tmp; } catch(Exception ex) { throw new SQLException("Unable to Instantiate: ", ex); } @@ -5710,7 +5712,9 @@ SQLData obj = null; try { ReflectUtil.checkPackageAccess(c); - obj = (SQLData) c.newInstance(); + @SuppressWarnings("deprecation") + Object tmp = c.newInstance(); + obj = (SQLData) tmp; } catch(Exception ex) { throw new SQLException("Unable to Instantiate: ", ex); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.sql.rowset/share/classes/com/sun/rowset/internal/CachedRowSetWriter.java --- a/jdk/src/java.sql.rowset/share/classes/com/sun/rowset/internal/CachedRowSetWriter.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.sql.rowset/share/classes/com/sun/rowset/internal/CachedRowSetWriter.java Tue May 03 12:25:20 2016 -0700 @@ -574,7 +574,9 @@ SQLData obj = null; try { ReflectUtil.checkPackageAccess(c); - obj = (SQLData)c.newInstance(); + @SuppressWarnings("deprecation") + Object tmp = c.newInstance(); + obj = (SQLData)tmp; } catch (Exception ex) { throw new SQLException("Unable to Instantiate: ", ex); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.sql.rowset/share/classes/javax/sql/rowset/RowSetProvider.java --- a/jdk/src/java.sql.rowset/share/classes/javax/sql/rowset/RowSetProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.sql.rowset/share/classes/javax/sql/rowset/RowSetProvider.java Tue May 03 12:25:20 2016 -0700 @@ -136,8 +136,9 @@ } // getFactoryClass takes care of adding the read edge if // necessary - Class c = getFactoryClass(factoryClassName, null, false); - factory = (RowSetFactory) c.newInstance(); + @SuppressWarnings("deprecation") + Object o = getFactoryClass(factoryClassName, null, false).newInstance(); + factory = (RowSetFactory) o; } } catch (Exception e) { throw new SQLException( "RowSetFactory: " + factoryClassName + @@ -202,6 +203,7 @@ // getFactoryClass takes care of adding the read edge if // necessary Class providerClass = getFactoryClass(factoryClassName, cl, false); + @SuppressWarnings("deprecation") RowSetFactory instance = (RowSetFactory) providerClass.newInstance(); if (debug) { trace("Created new instance of " + providerClass + diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.sql.rowset/share/classes/javax/sql/rowset/serial/SQLInputImpl.java --- a/jdk/src/java.sql.rowset/share/classes/javax/sql/rowset/serial/SQLInputImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.sql.rowset/share/classes/javax/sql/rowset/serial/SQLInputImpl.java Tue May 03 12:25:20 2016 -0700 @@ -478,7 +478,9 @@ SQLData obj = null; try { ReflectUtil.checkPackageAccess(c); - obj = (SQLData)c.newInstance(); + @SuppressWarnings("deprecation") + Object tmp = c.newInstance(); + obj = (SQLData)tmp; } catch (Exception ex) { throw new SQLException("Unable to Instantiate: ", ex); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.sql.rowset/share/classes/javax/sql/rowset/spi/SyncFactory.java --- a/jdk/src/java.sql.rowset/share/classes/javax/sql/rowset/spi/SyncFactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.sql.rowset/share/classes/javax/sql/rowset/spi/SyncFactory.java Tue May 03 12:25:20 2016 -0700 @@ -582,14 +582,12 @@ * there. **/ c = Class.forName(providerID, true, cl); - return (SyncProvider) c.newInstance(); + @SuppressWarnings("deprecation") + Object result = c.newInstance(); + return (SyncProvider)result; - } catch (IllegalAccessException e) { + } catch (IllegalAccessException | InstantiationException | ClassNotFoundException e) { throw new SyncFactoryException("IllegalAccessException: " + e.getMessage()); - } catch (InstantiationException e) { - throw new SyncFactoryException("InstantiationException: " + e.getMessage()); - } catch (ClassNotFoundException e) { - throw new SyncFactoryException("ClassNotFoundException: " + e.getMessage()); } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/algorithms/SignatureAlgorithm.java --- a/jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/algorithms/SignatureAlgorithm.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/algorithms/SignatureAlgorithm.java Tue May 03 12:25:20 2016 -0700 @@ -152,7 +152,9 @@ log.log(java.util.logging.Level.FINE, "Create URI \"" + algorithmURI + "\" class \"" + implementingClass + "\""); } - return implementingClass.newInstance(); + @SuppressWarnings("deprecation") + SignatureAlgorithmSpi result = implementingClass.newInstance(); + return result; } catch (IllegalAccessException ex) { Object exArgs[] = { algorithmURI, ex.getMessage() }; throw new XMLSignatureException("algorithms.NoSuchAlgorithm", exArgs, ex); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/Canonicalizer.java --- a/jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/Canonicalizer.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/Canonicalizer.java Tue May 03 12:25:20 2016 -0700 @@ -115,7 +115,9 @@ Class implementingClass = canonicalizerHash.get(algorithmURI); - canonicalizerSpi = implementingClass.newInstance(); + @SuppressWarnings("deprecation") + CanonicalizerSpi tmp = implementingClass.newInstance(); + canonicalizerSpi = tmp; canonicalizerSpi.reset = true; } catch (Exception e) { Object exArgs[] = { algorithmURI }; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolver.java --- a/jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolver.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolver.java Tue May 03 12:25:20 2016 -0700 @@ -182,6 +182,7 @@ public static void register(String className, boolean globalResolver) throws ClassNotFoundException, IllegalAccessException, InstantiationException { JavaUtils.checkRegisterPermission(); + @SuppressWarnings("deprecation") KeyResolverSpi keyResolverSpi = (KeyResolverSpi) Class.forName(className).newInstance(); keyResolverSpi.setGlobalResolver(globalResolver); @@ -207,7 +208,9 @@ KeyResolverSpi keyResolverSpi = null; Exception ex = null; try { - keyResolverSpi = (KeyResolverSpi) Class.forName(className).newInstance(); + @SuppressWarnings("deprecation") + Object tmp = Class.forName(className).newInstance(); + keyResolverSpi = (KeyResolverSpi) tmp; } catch (ClassNotFoundException e) { ex = e; } catch (IllegalAccessException e) { @@ -272,6 +275,7 @@ JavaUtils.checkRegisterPermission(); List keyResolverList = new ArrayList(classNames.size()); for (String className : classNames) { + @SuppressWarnings("deprecation") KeyResolverSpi keyResolverSpi = (KeyResolverSpi) Class.forName(className).newInstance(); keyResolverSpi.setGlobalResolver(false); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolverSpi.java --- a/jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolverSpi.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolverSpi.java Tue May 03 12:25:20 2016 -0700 @@ -110,7 +110,9 @@ KeyResolverSpi tmp = this; if (globalResolver) { try { - tmp = getClass().newInstance(); + @SuppressWarnings("deprecation") + KeyResolverSpi krs = getClass().newInstance(); + tmp = krs; } catch (InstantiationException e) { throw new KeyResolverException("", e); } catch (IllegalAccessException e) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/Transform.java --- a/jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/Transform.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/Transform.java Tue May 03 12:25:20 2016 -0700 @@ -160,7 +160,9 @@ throw new InvalidTransformException("signature.Transform.UnknownTransform", exArgs); } try { - transformSpi = transformSpiClass.newInstance(); + @SuppressWarnings("deprecation") + TransformSpi tmp = transformSpiClass.newInstance(); + transformSpi = tmp; } catch (InstantiationException ex) { Object exArgs[] = { algorithmURI }; throw new InvalidTransformException( @@ -345,7 +347,9 @@ } TransformSpi newTransformSpi = null; try { - newTransformSpi = transformSpiClass.newInstance(); + @SuppressWarnings("deprecation") + TransformSpi tmp = transformSpiClass.newInstance(); + newTransformSpi = tmp; } catch (InstantiationException ex) { Object exArgs[] = { algorithmURI }; throw new InvalidTransformException( diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolver.java --- a/jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolver.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolver.java Tue May 03 12:25:20 2016 -0700 @@ -99,8 +99,10 @@ ResourceResolver resolverTmp = resolver; if (!resolver.resolverSpi.engineIsThreadSafe()) { try { - resolverTmp = - new ResourceResolver(resolver.resolverSpi.getClass().newInstance()); + @SuppressWarnings("deprecation") + ResourceResolver tmp = new ResourceResolver(resolver.resolverSpi.getClass().newInstance()); + resolverTmp = tmp; + ; } catch (InstantiationException e) { throw new ResourceResolverException("", e, context.attr, context.baseUri); } catch (IllegalAccessException e) { @@ -246,6 +248,7 @@ public static void register(Class className, boolean start) { JavaUtils.checkRegisterPermission(); try { + @SuppressWarnings("deprecation") ResourceResolverSpi resourceResolverSpi = className.newInstance(); register(resourceResolverSpi, start); } catch (IllegalAccessException e) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.accessibility/share/classes/com/sun/java/accessibility/util/Translator.java --- a/jdk/src/jdk.accessibility/share/classes/com/sun/java/accessibility/util/Translator.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.accessibility/share/classes/com/sun/java/accessibility/util/Translator.java Tue May 03 12:25:20 2016 -0700 @@ -121,6 +121,7 @@ Class translatorClass = getTranslatorClass(o.getClass()); if (translatorClass != null) { try { + @SuppressWarnings("deprecation") Translator t = (Translator)translatorClass.newInstance(); t.setSource(o); a = t; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/AbstractCharsetProvider.java --- a/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/AbstractCharsetProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/AbstractCharsetProvider.java Tue May 03 12:25:20 2016 -0700 @@ -148,6 +148,7 @@ true, this.getClass().getClassLoader()); + @SuppressWarnings("deprecation") Charset cs = (Charset)c.newInstance(); cache.put(csn, new SoftReference(cs)); return cs; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Util.java --- a/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Util.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Util.java Tue May 03 12:25:20 2016 -0700 @@ -88,8 +88,9 @@ p = Security.getProvider(providerName); if (p == null) { try { - Class clazz = Class.forName(className); - p = (Provider)clazz.newInstance(); + @SuppressWarnings("deprecation") + Object o = Class.forName(className).newInstance(); + p = (Provider)o; } catch (Exception e) { throw new ProviderException ("Could not find provider " + providerName, e); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/SunPKCS11.java --- a/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/SunPKCS11.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/SunPKCS11.java Tue May 03 12:25:20 2016 -0700 @@ -1446,7 +1446,9 @@ } return null; } - return (CallbackHandler)c.newInstance(); + @SuppressWarnings("deprecation") + Object result = c.newInstance(); + return (CallbackHandler)result; } }); // save it diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.httpserver/share/classes/com/sun/net/httpserver/spi/HttpServerProvider.java --- a/jdk/src/jdk.httpserver/share/classes/com/sun/net/httpserver/spi/HttpServerProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.httpserver/share/classes/com/sun/net/httpserver/spi/HttpServerProvider.java Tue May 03 12:25:20 2016 -0700 @@ -89,9 +89,10 @@ if (cn == null) return false; try { - Class c = Class.forName(cn, true, - ClassLoader.getSystemClassLoader()); - provider = (HttpServerProvider)c.newInstance(); + @SuppressWarnings("deprecation") + Object o = Class.forName(cn, true, + ClassLoader.getSystemClassLoader()).newInstance(); + provider = (HttpServerProvider)o; return true; } catch (ClassNotFoundException | IllegalAccessException | diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/TerminalFactory.java --- a/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/TerminalFactory.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/TerminalFactory.java Tue May 03 12:25:20 2016 -0700 @@ -82,7 +82,9 @@ } else { try { - t = (Terminal) Thread.currentThread().getContextClassLoader().loadClass(type).newInstance(); + @SuppressWarnings("deprecation") + Object o = Thread.currentThread().getContextClassLoader().loadClass(type).newInstance(); + t = (Terminal) o; } catch (Exception e) { throw new IllegalArgumentException(MessageFormat.format("Invalid terminal type: {0}", type), e); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/console/internal/ConsoleRunner.java --- a/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/console/internal/ConsoleRunner.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/console/internal/ConsoleRunner.java Tue May 03 12:25:20 2016 -0700 @@ -61,6 +61,7 @@ List completorList = new ArrayList(); for (StringTokenizer tok = new StringTokenizer(completors, ","); tok.hasMoreTokens();) { + @SuppressWarnings("deprecation") Object obj = Class.forName(tok.nextToken()).newInstance(); completorList.add((Completer) obj); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java --- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java Tue May 03 12:25:20 2016 -0700 @@ -136,10 +136,10 @@ jartool.moduleVersion = Version.parse(arg); } }, - new Option(true, OptionType.CREATE_UPDATE, "--hash-dependencies") { + new Option(true, OptionType.CREATE_UPDATE, "--hash-modules") { void process(Main jartool, String opt, String arg) throws BadArgs { try { - jartool.dependenciesToHash = Pattern.compile(arg); + jartool.modulesToHash = Pattern.compile(arg); } catch (PatternSyntaxException e) { throw new BadArgs("err.badpattern", arg).showUsage(true); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java --- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java Tue May 03 12:25:20 2016 -0700 @@ -26,21 +26,25 @@ package sun.tools.jar; import java.io.*; +import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleDescriptor.Exports; import java.lang.module.ModuleDescriptor.Provides; import java.lang.module.ModuleDescriptor.Requires; import java.lang.module.ModuleDescriptor.Version; import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReader; import java.lang.module.ModuleReference; -import java.lang.reflect.Method; +import java.lang.module.ResolutionException; +import java.lang.module.ResolvedModule; import java.net.URI; import java.nio.file.Path; import java.nio.file.Files; import java.nio.file.Paths; import java.util.*; import java.util.function.Consumer; -import java.util.regex.Matcher; +import java.util.function.Function; +import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.zip.*; @@ -49,9 +53,12 @@ import java.util.jar.Manifest; import java.text.MessageFormat; -import jdk.internal.module.Hasher; +import jdk.internal.misc.JavaLangModuleAccess; +import jdk.internal.misc.SharedSecrets; +import jdk.internal.module.ModuleHashes; import jdk.internal.module.ModuleInfoExtender; import jdk.internal.util.jar.JarIndex; + import static jdk.internal.util.jar.JarIndex.INDEX_NAME; import static java.util.jar.JarFile.MANIFEST_NAME; import static java.util.stream.Collectors.joining; @@ -117,7 +124,7 @@ /* Modular jar related options */ boolean printModuleDescriptor; Version moduleVersion; - Pattern dependenciesToHash; + Pattern modulesToHash; ModuleFinder moduleFinder = ModuleFinder.empty(); private static final String MODULE_INFO = "module-info.class"; @@ -241,7 +248,7 @@ if (isModularJar()) { moduleInfoBytes = addExtendedModuleAttributes( readModuleInfo(moduleInfo)); - } else if (moduleVersion != null || dependenciesToHash != null) { + } else if (moduleVersion != null || modulesToHash != null) { error(getMsg("error.module.options.without.info")); return false; } @@ -801,7 +808,7 @@ } } else if (isModuleInfoEntry && ((newModuleInfoBytes != null) || (ename != null) - || moduleVersion != null || dependenciesToHash != null)) { + || moduleVersion != null || modulesToHash != null)) { if (newModuleInfoBytes == null) { // Update existing module-info.class newModuleInfoBytes = readModuleInfo(zis); @@ -861,7 +868,7 @@ if (!updateModuleInfo(newModuleInfoBytes, zos)) { updateOk = false; } - } else if (moduleVersion != null || dependenciesToHash != null) { + } else if (moduleVersion != null || modulesToHash != null) { error(getMsg("error.module.options.without.info")); updateOk = false; } @@ -1642,70 +1649,60 @@ return false; } - @SuppressWarnings("unchecked") + static String toString(Set set) { + if (set.isEmpty()) { return ""; } + return set.stream().map(e -> e.toString().toLowerCase(Locale.ROOT)) + .collect(joining(" ")); + } + + private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess(); + private void printModuleDescriptor(InputStream entryInputStream) throws IOException { ModuleDescriptor md = ModuleDescriptor.read(entryInputStream); StringBuilder sb = new StringBuilder(); - sb.append("\nName:\n " + md.toNameAndVersion()); - - Set requires = md.requires(); - if (!requires.isEmpty()) { - sb.append("\nRequires:"); - requires.forEach(r -> - sb.append("\n ").append(r.name()) - .append(toString(r.modifiers(), " [ ", " ]"))); - } + sb.append("\n").append(md.toNameAndVersion()); - Set s = md.uses(); - if (!s.isEmpty()) { - sb.append("\nUses: "); - s.forEach(sv -> sb.append("\n ").append(sv)); - } + md.requires().stream() + .sorted(Comparator.comparing(Requires::name)) + .forEach(r -> { + sb.append("\n requires "); + if (!r.modifiers().isEmpty()) + sb.append(toString(r.modifiers())).append(" "); + sb.append(r.name()); + }); - Set exports = md.exports(); - if (!exports.isEmpty()) { - sb.append("\nExports:"); - exports.forEach(sv -> sb.append("\n ").append(sv)); - } + md.uses().stream().sorted() + .forEach(p -> sb.append("\n uses ").append(p)); + + md.exports().stream() + .sorted(Comparator.comparing(Exports::source)) + .forEach(p -> sb.append("\n exports ").append(p)); + + md.conceals().stream().sorted() + .forEach(p -> sb.append("\n conceals ").append(p)); - Map provides = md.provides(); - if (!provides.isEmpty()) { - sb.append("\nProvides: "); - provides.values().forEach(p -> - sb.append("\n ").append(p.service()) - .append(" with ") - .append(toString(p.providers(), "", ""))); - } + md.provides().values().stream() + .sorted(Comparator.comparing(Provides::service)) + .forEach(p -> sb.append("\n provides ").append(p.service()) + .append(" with ") + .append(toString(p.providers()))); - Optional mc = md.mainClass(); - if (mc.isPresent()) - sb.append("\nMain class:\n " + mc.get()); + md.mainClass().ifPresent(v -> sb.append("\n main-class " + v)); + + md.osName().ifPresent(v -> sb.append("\n operating-system-name " + v)); - s = md.conceals(); - if (!s.isEmpty()) { - sb.append("\nConceals:"); - s.forEach(p -> sb.append("\n ").append(p)); - } + md.osArch().ifPresent(v -> sb.append("\n operating-system-architecture " + v)); - try { - Method m = ModuleDescriptor.class.getDeclaredMethod("hashes"); - m.setAccessible(true); - Optional optHashes = - (Optional) m.invoke(md); + md.osVersion().ifPresent(v -> sb.append("\n operating-system-version " + v)); - if (optHashes.isPresent()) { - Hasher.DependencyHashes hashes = optHashes.get(); - sb.append("\nHashes:"); - sb.append("\n Algorithm: " + hashes.algorithm()); - hashes.names().stream().forEach(mod -> - sb.append("\n ").append(mod) - .append(": ").append(hashes.hashFor(mod))); - } - } catch (ReflectiveOperationException x) { - throw new InternalError(x); - } + JLMA.hashes(md).ifPresent(hashes -> + hashes.names().stream().sorted().forEach( + mod -> sb.append("\n hashes ").append(mod).append(" ") + .append(hashes.algorithm()).append(" ") + .append(hashes.hashFor(mod)))); + output(sb.toString()); } @@ -1751,7 +1748,6 @@ md = ModuleDescriptor.read(in); } String name = md.name(); - Set dependences = md.requires(); Set exported = md.exports() .stream() .map(ModuleDescriptor.Exports::source) @@ -1778,9 +1774,17 @@ if (moduleVersion != null) extender.version(moduleVersion); - // --hash-dependencies - if (dependenciesToHash != null) - extender.hashes(hashDependences(name, dependences)); + // --hash-modules + if (modulesToHash != null) { + Hasher hasher = new Hasher(md, fname); + ModuleHashes moduleHashes = hasher.computeHashes(name); + if (moduleHashes != null) { + extender.hashes(moduleHashes); + } else { + // should it issue warning or silent? + System.out.println("warning: no module is recorded in hash in " + name); + } + } extender.write(baos); return baos.toByteArray(); @@ -1788,36 +1792,156 @@ } /** - * Examines the module dependences of the given module and computes the - * hash of any module that matches the pattern {@code dependenciesToHash}. + * Compute and record hashes */ - private Hasher.DependencyHashes - hashDependences(String name, - Set moduleDependences) - throws IOException - { - Map map = new HashMap<>(); - Matcher matcher = dependenciesToHash.matcher(""); - for (ModuleDescriptor.Requires md: moduleDependences) { - String dn = md.name(); - if (matcher.reset(dn).find()) { - Optional omref = moduleFinder.find(dn); - if (!omref.isPresent()) { - throw new IOException(formatMsg2("error.hash.dep", name , dn)); - } - map.put(dn, modRefToPath(omref.get())); + private class Hasher { + final ModuleFinder finder; + final Map moduleNameToPath; + final Set modules; + final Configuration configuration; + Hasher(ModuleDescriptor descriptor, String fname) throws IOException { + // Create a module finder that finds the modular JAR + // being created/updated + URI uri = Paths.get(fname).toUri(); + ModuleReference mref = new ModuleReference(descriptor, uri, + new Supplier<>() { + @Override + public ModuleReader get() { + throw new UnsupportedOperationException("should not reach here"); + } + }); + + // Compose a module finder with the module path and + // the modular JAR being created or updated + this.finder = ModuleFinder.compose(moduleFinder, + new ModuleFinder() { + @Override + public Optional find(String name) { + if (descriptor.name().equals(name)) + return Optional.of(mref); + else + return Optional.empty(); + } + + @Override + public Set findAll() { + return Collections.singleton(mref); + } + }); + + // Determine the modules that matches the modulesToHash pattern + this.modules = moduleFinder.findAll().stream() + .map(moduleReference -> moduleReference.descriptor().name()) + .filter(mn -> modulesToHash.matcher(mn).find()) + .collect(Collectors.toSet()); + + // a map from a module name to Path of the modular JAR + this.moduleNameToPath = moduleFinder.findAll().stream() + .map(ModuleReference::descriptor) + .map(ModuleDescriptor::name) + .collect(Collectors.toMap(Function.identity(), mn -> moduleToPath(mn))); + + Configuration config = null; + try { + config = Configuration.empty() + .resolveRequires(ModuleFinder.ofSystem(), finder, modules); + } catch (ResolutionException e) { + // should it throw an error? or emit a warning + System.out.println("warning: " + e.getMessage()); } + this.configuration = config; } - if (map.size() == 0) { - return null; - } else { - return Hasher.generate(map, "SHA-256"); + /** + * Compute hashes of the modules that depend upon the specified + * module directly or indirectly. + */ + ModuleHashes computeHashes(String name) { + // the transposed graph includes all modules in the resolved graph + Map> graph = transpose(); + + // find the modules that transitively depend upon the specified name + Deque deque = new ArrayDeque<>(); + deque.add(name); + Set mods = visitNodes(graph, deque); + + // filter modules matching the pattern specified --hash-modules + // as well as itself as the jmod file is being generated + Map modulesForHash = mods.stream() + .filter(mn -> !mn.equals(name) && modules.contains(mn)) + .collect(Collectors.toMap(Function.identity(), moduleNameToPath::get)); + + if (modulesForHash.isEmpty()) + return null; + + return ModuleHashes.generate(modulesForHash, "SHA-256"); + } + + /** + * Returns all nodes traversed from the given roots. + */ + private Set visitNodes(Map> graph, + Deque roots) { + Set visited = new HashSet<>(); + while (!roots.isEmpty()) { + String mn = roots.pop(); + if (!visited.contains(mn)) { + visited.add(mn); + + // the given roots may not be part of the graph + if (graph.containsKey(mn)) { + for (String dm : graph.get(mn)) { + if (!visited.contains(dm)) + roots.push(dm); + } + } + } + } + return visited; + } + + /** + * Returns a transposed graph from the resolved module graph. + */ + private Map> transpose() { + Map> transposedGraph = new HashMap<>(); + Deque deque = new ArrayDeque<>(modules); + + Set visited = new HashSet<>(); + while (!deque.isEmpty()) { + String mn = deque.pop(); + if (!visited.contains(mn)) { + visited.add(mn); + + // add an empty set + transposedGraph.computeIfAbsent(mn, _k -> new HashSet<>()); + + ResolvedModule resolvedModule = configuration.findModule(mn).get(); + for (ResolvedModule dm : resolvedModule.reads()) { + String name = dm.name(); + if (!visited.contains(name)) { + deque.push(name); + } + // reverse edge + transposedGraph.computeIfAbsent(name, _k -> new HashSet<>()) + .add(mn); + } + } + } + return transposedGraph; + } + + private Path moduleToPath(String name) { + ModuleReference mref = moduleFinder.find(name).orElseThrow( + () -> new InternalError(formatMsg2("error.hash.dep",name , name))); + + URI uri = mref.location().get(); + Path path = Paths.get(uri); + String fn = path.getFileName().toString(); + if (!fn.endsWith(".jar")) { + throw new UnsupportedOperationException(path + " is not a modular JAR"); + } + return path; } } - - private static Path modRefToPath(ModuleReference mref) { - URI location = mref.location().get(); - return Paths.get(location); - } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties --- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties Tue May 03 12:25:20 2016 -0700 @@ -57,7 +57,7 @@ error.hash.dep=\ Hashing module {0} dependences, unable to find module {1} on module path error.module.options.without.info=\ - One of --module-version or --hash-dependencies without module-info.class + One of --module-version or --hash-modules without module-info.class error.unexpected.module-info=\ Unexpected module descriptor {0} error.module.descriptor.not.found=\ @@ -178,11 +178,11 @@ main.help.opt.create.update.module-version=\ \ --module-version=VERSION The module version, when creating a modular\n\ \ jar, or updating a non-modular jar -main.help.opt.create.update.hash-dependencies=\ -\ --hash-dependencies=PATTERN Compute and record the hashes of module\n\ -\ dependencies matched by the given pattern, when\n\ -\ creating a modular jar, or updating a non-modular\n\ -\ jar +main.help.opt.create.update.hash-modules=\ +\ --hash-modules=PATTERN Compute and record the hashes of modules \n\ +\ matched by the given pattern and that depend upon\n\ +\ directly or indirectly on a modular jar being\n\ +\ created or a non-modular jar being updated main.help.opt.create.update.modulepath=\ \ --modulepath Location of module dependence for generating \ the hash @@ -201,7 +201,7 @@ \ located in the root of the given directories, or the root of the jar archive\n\ \ itself. The following operations are only valid when creating a modular jar,\n\ \ or updating an existing non-modular jar: '--module-version',\n\ -\ '--hash-dependencies', and '--modulepath'.\n\ +\ '--hash-modules', and '--modulepath'.\n\ \n\ \ Mandatory or optional arguments to long options are also mandatory or optional\n\ \ for any corresponding short options. \ No newline at end of file diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jdi/share/classes/com/sun/tools/jdi/ProcessAttachingConnector.java --- a/jdk/src/jdk.jdi/share/classes/com/sun/tools/jdi/ProcessAttachingConnector.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jdi/share/classes/com/sun/tools/jdi/ProcessAttachingConnector.java Tue May 03 12:25:20 2016 -0700 @@ -128,7 +128,9 @@ if (lib.equals("dt_shmem")) { try { Class c = Class.forName("com.sun.tools.jdi.SharedMemoryTransportService"); - ts = (TransportService)c.newInstance(); + @SuppressWarnings("deprecation") + Object tmp = c.newInstance(); + ts = (TransportService)tmp; } catch (Exception x) { } } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jdi/share/classes/com/sun/tools/jdi/RawCommandLineLauncher.java --- a/jdk/src/jdk.jdi/share/classes/com/sun/tools/jdi/RawCommandLineLauncher.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jdi/share/classes/com/sun/tools/jdi/RawCommandLineLauncher.java Tue May 03 12:25:20 2016 -0700 @@ -53,17 +53,19 @@ super(); try { - Class c = Class.forName("com.sun.tools.jdi.SharedMemoryTransportService"); - transportService = (TransportService)c.newInstance(); + @SuppressWarnings("deprecation") + Object o = + Class.forName("com.sun.tools.jdi.SharedMemoryTransportService").newInstance(); + transportService = (TransportService)o; transport = new Transport() { public String name() { return "dt_shmem"; } }; - } catch (ClassNotFoundException x) { - } catch (UnsatisfiedLinkError x) { - } catch (InstantiationException x) { - } catch (IllegalAccessException x) { + } catch (ClassNotFoundException | + UnsatisfiedLinkError | + InstantiationException | + IllegalAccessException x) { }; if (transportService == null) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jdi/share/classes/com/sun/tools/jdi/SunCommandLineLauncher.java --- a/jdk/src/jdk.jdi/share/classes/com/sun/tools/jdi/SunCommandLineLauncher.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jdi/share/classes/com/sun/tools/jdi/SunCommandLineLauncher.java Tue May 03 12:25:20 2016 -0700 @@ -64,18 +64,20 @@ * transport or the socket transport */ try { - Class c = Class.forName("com.sun.tools.jdi.SharedMemoryTransportService"); - transportService = (TransportService)c.newInstance(); + @SuppressWarnings("deprecation") + Object o = + Class.forName("com.sun.tools.jdi.SharedMemoryTransportService").newInstance(); + transportService = (TransportService)o; transport = new Transport() { public String name() { return "dt_shmem"; } }; usingSharedMemory = true; - } catch (ClassNotFoundException x) { - } catch (UnsatisfiedLinkError x) { - } catch (InstantiationException x) { - } catch (IllegalAccessException x) { + } catch (ClassNotFoundException | + UnsatisfiedLinkError | + InstantiationException | + IllegalAccessException x) { }; if (transportService == null) { transportService = new SocketTransportService(); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java Tue May 03 12:25:20 2016 -0700 @@ -95,27 +95,23 @@ private final Path root; private final Path mdir; - private final boolean genBom; private final Set modules = new HashSet<>(); /** * Default image builder constructor. * - * @param genBom true, generates a bom file. * @param root The image root directory. * @throws IOException */ - public DefaultImageBuilder(boolean genBom, Path root) throws IOException { + public DefaultImageBuilder(Path root) throws IOException { Objects.requireNonNull(root); - this.genBom = genBom; - this.root = root; this.mdir = root.resolve("lib"); Files.createDirectories(mdir); } - private void storeFiles(Set modules, String bom, Properties release) throws IOException { + private void storeFiles(Set modules, Properties release) throws IOException { if (release != null) { addModules(release, modules); File r = new File(root.toFile(), "release"); @@ -123,11 +119,6 @@ release.store(fo, null); } } - // Generate bom - if (genBom) { - File bomFile = new File(root.toFile(), "bom"); - createUtf8File(bomFile, bom); - } } private void addModules(Properties release, Set modules) throws IOException { @@ -144,7 +135,7 @@ } @Override - public void storeFiles(Pool files, String bom, Properties release) { + public void storeFiles(Pool files, Properties release) { try { for (ModuleData f : files.getContent()) { if (!f.getType().equals(Pool.ModuleDataType.CLASS_OR_RESOURCE)) { @@ -161,7 +152,7 @@ modules.add(m.getName()); } } - storeFiles(modules, bom, release); + storeFiles(modules, release); if (Files.getFileStore(root).supportsFileAttributeView(PosixFileAttributeView.class)) { // launchers in the bin directory need execute permission @@ -190,8 +181,8 @@ } @Override - public void storeFiles(Pool files, String bom) { - storeFiles(files, bom, new Properties()); + public void storeFiles(Pool files) { + storeFiles(files, new Properties()); } /** @@ -213,28 +204,48 @@ mainClass = ModuleDescriptor.read(stream).mainClass(); if (mainClass.isPresent()) { Path cmd = root.resolve("bin").resolve(module); - if (!Files.exists(cmd)) { - StringBuilder sb = new StringBuilder(); - sb.append("#!/bin/sh") - .append("\n"); - sb.append("JLINK_VM_OPTIONS=") - .append("\n"); - sb.append("DIR=`dirname $0`") - .append("\n"); - sb.append("$DIR/java $JLINK_VM_OPTIONS -m ") + // generate shell script for Unix platforms + StringBuilder sb = new StringBuilder(); + sb.append("#!/bin/sh") + .append("\n"); + sb.append("JLINK_VM_OPTIONS=") + .append("\n"); + sb.append("DIR=`dirname $0`") + .append("\n"); + sb.append("$DIR/java $JLINK_VM_OPTIONS -m ") + .append(module).append('/') + .append(mainClass.get()) + .append(" $@\n"); + + try (BufferedWriter writer = Files.newBufferedWriter(cmd, + StandardCharsets.ISO_8859_1, + StandardOpenOption.CREATE_NEW)) { + writer.write(sb.toString()); + } + if (Files.getFileStore(root.resolve("bin")) + .supportsFileAttributeView(PosixFileAttributeView.class)) { + setExecutable(cmd); + } + // generate .bat file for Windows + if (isWindows()) { + Path bat = root.resolve("bin").resolve(module + ".bat"); + sb = new StringBuilder(); + sb.append("@echo off") + .append("\r\n"); + sb.append("set JLINK_VM_OPTIONS=") + .append("\r\n"); + sb.append("set DIR=%~dp0") + .append("\r\n"); + sb.append("\"%DIR%\\java\" %JLINK_VM_OPTIONS% -m ") .append(module).append('/') .append(mainClass.get()) - .append(" $@\n"); + .append(" %*\r\n"); - try (BufferedWriter writer = Files.newBufferedWriter(cmd, + try (BufferedWriter writer = Files.newBufferedWriter(bat, StandardCharsets.ISO_8859_1, StandardOpenOption.CREATE_NEW)) { writer.write(sb.toString()); } - if (Files.getFileStore(root.resolve("bin")) - .supportsFileAttributeView(PosixFileAttributeView.class)) { - setExecutable(cmd); - } } } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/ImageBuilder.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/ImageBuilder.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/ImageBuilder.java Tue May 03 12:25:20 2016 -0700 @@ -42,22 +42,20 @@ * Store the external files. * * @param content Pool of module content. - * @param bom The options used to build the image file. * @param release the release properties * @throws PluginException */ - public default void storeFiles(Pool content, String bom, Properties release) { - storeFiles(content, bom); + public default void storeFiles(Pool content, Properties release) { + storeFiles(content); } /** * Store the external files. * * @param content Pool of module content. - * @param bom The options used to build the image file. * @throws PluginException */ - public default void storeFiles(Pool content, String bom) { + public default void storeFiles(Pool content) { throw new UnsupportedOperationException("storeFiles"); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java Tue May 03 12:25:20 2016 -0700 @@ -88,7 +88,7 @@ ByteOrder byteOrder) throws IOException { return ImageFileCreator.create(archives, byteOrder, - new ImagePluginStack(null)); + new ImagePluginStack()); } public static ExecutableImage create(Set archives, diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java Tue May 03 12:25:20 2016 -0700 @@ -68,20 +68,13 @@ private ImagePluginConfiguration() { } - public static ImagePluginStack parseConfiguration(Jlink.PluginsConfiguration plugins) - throws Exception { - return parseConfiguration(plugins, null); - } - /* * Create a stack of plugins from a a configuration. - * */ - public static ImagePluginStack parseConfiguration(Jlink.PluginsConfiguration pluginsConfiguration, - String bom) + public static ImagePluginStack parseConfiguration(Jlink.PluginsConfiguration pluginsConfiguration) throws Exception { if (pluginsConfiguration == null) { - return new ImagePluginStack(bom); + return new ImagePluginStack(); } Map> plugins = new LinkedHashMap<>(); for (Plugin.CATEGORY cat : CATEGORIES_ORDER) { @@ -150,7 +143,7 @@ } @Override - public void storeFiles(Pool files, String bom) { + public void storeFiles(Pool files) { throw new PluginException("No directory setup to store files"); } }; @@ -158,6 +151,6 @@ PluginContext ctxt = pluginsConfiguration.getPluginContext(); return new ImagePluginStack(builder, transformerPlugins, - lastSorter, postProcessingPlugins, ctxt, bom); + lastSorter, postProcessingPlugins, ctxt); } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java Tue May 03 12:25:20 2016 -0700 @@ -167,28 +167,25 @@ private final ImageBuilder imageBuilder; private final Properties release; - private final String bom; + + public ImagePluginStack() { + this(null, Collections.emptyList(), null, + Collections.emptyList(), null); + } - public ImagePluginStack(String bom) { - this(null, Collections.emptyList(), null, - Collections.emptyList(), null, bom); + public ImagePluginStack(ImageBuilder imageBuilder, + List contentPlugins, + Plugin lastSorter, + List postprocessingPlugins) { + this(imageBuilder, contentPlugins, lastSorter, + postprocessingPlugins, null); } public ImagePluginStack(ImageBuilder imageBuilder, List contentPlugins, Plugin lastSorter, List postprocessingPlugins, - String bom) { - this(imageBuilder, contentPlugins, lastSorter, - postprocessingPlugins, null, bom); - } - - public ImagePluginStack(ImageBuilder imageBuilder, - List contentPlugins, - Plugin lastSorter, - List postprocessingPlugins, - PluginContext ctxt, - String bom) { + PluginContext ctxt) { Objects.requireNonNull(contentPlugins); this.lastSorter = lastSorter; for (TransformerPlugin p : contentPlugins) { @@ -204,7 +201,6 @@ } this.imageBuilder = imageBuilder; this.release = ctxt != null? ctxt.getReleaseProperties() : new Properties(); - this.bom = bom; } public void operate(ImageProvider provider) throws Exception { @@ -479,7 +475,7 @@ } catch (Exception ignored) { } - imageBuilder.storeFiles(new LastPool(transformed), bom, release); + imageBuilder.storeFiles(new LastPool(transformed), release); } public ExecutableImage getExecutableImage() throws IOException { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java Tue May 03 12:25:20 2016 -0700 @@ -70,6 +70,7 @@ * ## Should use jdk.joptsimple some day. */ public class JlinkTask { + private static final boolean DEBUG = Boolean.getBoolean("jlink.debug"); private static void fail(Class type, String format, @@ -142,9 +143,6 @@ } task.options.packagedModulesPath = path; }, true, "--keep-packaged-modules"), - new Option(false, (task, opt, arg) -> { - task.options.genbom = true; - }, true, "--genbom"), new Option(true, (task, opt, arg) -> { task.options.saveoptsfile = arg; }, "--saveopts"), @@ -175,7 +173,6 @@ static class OptionsValues { boolean help; - boolean genbom; String saveoptsfile; boolean version; boolean fullVersion; @@ -219,18 +216,24 @@ } return EXIT_OK; - } catch (UncheckedIOException | PluginException | IOException | ResolutionException e) { + } catch (UncheckedIOException | PluginException | IllegalArgumentException | + IOException | ResolutionException e) { log.println(taskHelper.getMessage("error.prefix") + " " + e.getMessage()); - log.println(taskHelper.getMessage("main.usage.summary", PROGNAME)); + if (DEBUG) { + e.printStackTrace(log); + } return EXIT_ERROR; } catch (BadArgs e) { taskHelper.reportError(e.key, e.args); if (e.showUsage) { log.println(taskHelper.getMessage("main.usage.summary", PROGNAME)); } + if (DEBUG) { + e.printStackTrace(log); + } return EXIT_CMDERR; } catch (Throwable x) { - log.println(taskHelper.getMessage("main.msg.bug")); + log.println(taskHelper.getMessage("error.prefix") + " " + x.getMessage()); x.printStackTrace(log); return EXIT_ABNORMAL; } finally { @@ -238,16 +241,6 @@ } } - private static Map modulesToPath(Configuration cf) { - Map modPaths = new HashMap<>(); - for (ResolvedModule resolvedModule : cf.modules()) { - ModuleReference mref = resolvedModule.reference(); - URI uri = mref.location().get(); - modPaths.put(mref.descriptor().name(), Paths.get(uri)); - } - return modPaths; - } - /* * Jlink API entry point. */ @@ -275,8 +268,7 @@ null); // Then create the Plugin Stack - ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(plugins, - genBOMContent(config, plugins)); + ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(plugins); //Ask the stack to proceed; stack.operate(imageProvider); @@ -297,7 +289,7 @@ } private void postProcessOnly(Path existingImage) throws Exception { - PluginsConfiguration config = taskHelper.getPluginsConfig(null, false); + PluginsConfiguration config = taskHelper.getPluginsConfig(null); ExecutableImage img = DefaultImageBuilder.getExecutableImage(existingImage); if (img == null) { throw taskHelper.newBadArgs("err.existing.image.invalid"); @@ -327,8 +319,7 @@ // Then create the Plugin Stack ImagePluginStack stack = ImagePluginConfiguration. - parseConfiguration(taskHelper.getPluginsConfig(options.output, options.genbom), - genBOMContent()); + parseConfiguration(taskHelper.getPluginsConfig(options.output)); //Ask the stack to proceed stack.operate(imageProvider); @@ -358,6 +349,15 @@ return finder; } + + private static Path toPathLocation(ResolvedModule m) { + Optional ouri = m.reference().location(); + if (!ouri.isPresent()) + throw new InternalError(m + " does not have a location"); + URI uri = ouri.get(); + return Paths.get(uri); + } + private static ImageProvider createImageProvider(ModuleFinder finder, Set addMods, Set limitMods, @@ -374,7 +374,8 @@ ModuleFinder.empty(), addMods); - Map mods = modulesToPath(cf); + Map mods = cf.modules().stream() + .collect(Collectors.toMap(ResolvedModule::name, JlinkTask::toPathLocation)); return new ImageHelper(cf, mods, order, retainModulesPath); } @@ -399,21 +400,15 @@ map.put(mref.descriptor().name(), mref); }); + // add the other modules + otherMods.stream() + .map(finder::find) + .flatMap(Optional::stream) + .forEach(mref -> map.putIfAbsent(mref.descriptor().name(), mref)); + // set of modules that are observable Set mrefs = new HashSet<>(map.values()); - // add the other modules - for (String mod : otherMods) { - Optional omref = finder.find(mod); - if (omref.isPresent()) { - ModuleReference mref = omref.get(); - map.putIfAbsent(mod, mref); - mrefs.add(mref); - } else { - // no need to fail - } - } - return new ModuleFinder() { @Override public Optional find(String name) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java Tue May 03 12:25:20 2016 -0700 @@ -337,8 +337,8 @@ return null; } - private PluginsConfiguration getPluginsConfig(Path output, - boolean genbom) throws IOException, BadArgs { + private PluginsConfiguration getPluginsConfig(Path output + ) throws IOException, BadArgs { if (output != null) { if (Files.exists(output)) { throw new PluginException(PluginsResourceBundle. @@ -367,7 +367,7 @@ // recreate or postprocessing don't require an output directory. ImageBuilder builder = null; if (output != null) { - builder = new DefaultImageBuilder(genbom, output); + builder = new DefaultImageBuilder(output); } return new Jlink.PluginsConfiguration(pluginsList, @@ -676,9 +676,9 @@ + bundleHelper.getMessage(key, args)); } - public PluginsConfiguration getPluginsConfig(Path output, boolean genbom) + public PluginsConfiguration getPluginsConfig(Path output) throws IOException, BadArgs { - return pluginOptions.getPluginsConfig(output, genbom); + return pluginOptions.getPluginsConfig(output); } public Path getExistingImage() { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java Tue May 03 12:25:20 2016 -0700 @@ -139,7 +139,7 @@ // build the image Jlink.PluginsConfiguration pluginConfig = new Jlink.PluginsConfiguration( - plugins, new DefaultImageBuilder(true, outputDir), null); + plugins, new DefaultImageBuilder(outputDir), null); Jlink jlink = new Jlink(); jlink.build(jlinkConfig, pluginConfig); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultCompressPlugin.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultCompressPlugin.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultCompressPlugin.java Tue May 03 12:25:20 2016 -0700 @@ -125,7 +125,7 @@ zip = new ZipPlugin(resFilter); break; default: - throw new PluginException("Invalid level " + level); + throw new IllegalArgumentException("Invalid compression level " + level); } } else { ss = new StringSharingPlugin(resFilter); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeVMPlugin.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeVMPlugin.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeVMPlugin.java Tue May 03 12:25:20 2016 -0700 @@ -208,7 +208,7 @@ break; } default: { - throw new PluginException("Unknown option " + value); + throw new IllegalArgumentException("Unknown exclude VM option: " + value); } } predicate = new ResourceFilter(Utils.listParser.apply(exclude), true); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java Tue May 03 12:25:20 2016 -0700 @@ -164,7 +164,7 @@ try { return new Locale.LanguageRange(s); } catch (IllegalArgumentException iae) { - throw new PluginException(String.format( + throw new IllegalArgumentException(String.format( PluginsResourceBundle.getMessage(NAME + ".invalidtag"), s)); } }) diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java Tue May 03 12:25:20 2016 -0700 @@ -273,7 +273,7 @@ } else if (s.equals(FORNAME_REMOVAL)) { optimizers.add(new ForNameFolding()); } else { - throw new PluginException("Unknown optimization"); + throw new IllegalArgumentException("Unknown optimization: " + s); } } String f = config.get(LOG); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModuleDescriptorPlugin.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModuleDescriptorPlugin.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModuleDescriptorPlugin.java Tue May 03 12:25:20 2016 -0700 @@ -39,6 +39,8 @@ import java.util.Set; import java.util.stream.Collectors; +import jdk.internal.misc.JavaLangModuleAccess; +import jdk.internal.misc.SharedSecrets; import jdk.internal.module.Checks; import jdk.internal.module.ModuleInfoExtender; import jdk.internal.module.SystemModules; @@ -50,6 +52,7 @@ import jdk.tools.jlink.plugin.PluginException; import jdk.tools.jlink.plugin.Pool; import jdk.tools.jlink.plugin.TransformerPlugin; +import jdk.tools.jlink.internal.plugins.SystemModuleDescriptorPlugin.Builder.*; /** * Jlink plugin to reconstitute module descriptors for installed modules. @@ -63,6 +66,8 @@ * @see SystemModules */ public final class SystemModuleDescriptorPlugin implements TransformerPlugin { + private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess(); + // TODO: packager has the dependency on the plugin name // Keep it as "--installed-modules" until packager removes such // dependency (should not need to specify this plugin since it @@ -118,7 +123,8 @@ Pool.ModuleData data = module.get("module-info.class"); if (data == null) { // automatic module not supported yet - throw new PluginException("module-info.class not found for " + module.getName() + " module"); + throw new PluginException("module-info.class not found for " + + module.getName() + " module"); } assert module.getName().equals(data.getModule()); try { @@ -126,15 +132,20 @@ ModuleDescriptor md = ModuleDescriptor.read(bain); validateNames(md); - Builder.ModuleDescriptorBuilder mbuilder = builder.module(md, module.getAllPackages()); + ModuleDescriptorBuilder mbuilder = builder.module(md, module.getAllPackages()); + int packages = md.exports().size() + md.conceals().size(); if (md.conceals().isEmpty() && - (md.exports().size() + md.conceals().size()) != module.getAllPackages().size()) { + packages != module.getAllPackages().size()) { // add ConcealedPackages attribute if not exist bain.reset(); - ModuleInfoRewriter minfoWriter = new ModuleInfoRewriter(bain, mbuilder.conceals()); + ModuleInfoRewriter minfoWriter = + new ModuleInfoRewriter(bain, mbuilder.conceals()); // replace with the overridden version - data = new Pool.ModuleData(data.getModule(), data.getPath(), data.getType(), - minfoWriter.stream(), minfoWriter.size()); + data = new Pool.ModuleData(data.getModule(), + data.getPath(), + data.getType(), + minfoWriter.stream(), + minfoWriter.size()); } out.add(data); } catch (IOException e) { @@ -151,8 +162,12 @@ if (builder.isOverriddenClass(data.getPath())) { byte[] bytes = cwriter.toByteArray(); - Pool.ModuleData ndata = new Pool.ModuleData(data.getModule(), data.getPath(), data.getType(), - new ByteArrayInputStream(bytes), bytes.length); + Pool.ModuleData ndata = + new Pool.ModuleData(data.getModule(), + data.getPath(), + data.getType(), + new ByteArrayInputStream(bytes), + bytes.length); out.add(ndata); } else { out.add(data); @@ -230,6 +245,7 @@ // static variables in SystemModules class private static final String MODULE_NAMES = "MODULE_NAMES"; + private static final String MODULES_TO_HASH = "MODULES_TO_HASH"; private static final String PACKAGE_COUNT = "PACKAGES_IN_BOOT_LAYER"; private static final int BUILDER_VAR = 0; @@ -246,6 +262,9 @@ // list of all ModuleDescriptorBuilders, invoked in turn when building. private final List builders = new ArrayList<>(); + // module name to hash + private final Map modulesToHash = new HashMap<>(); + // map Set to a specialized builder to allow them to be // deduplicated as they are requested private final Map, StringSetBuilder> stringSets = new HashMap<>(); @@ -268,6 +287,11 @@ "[Ljava/lang/String;", null, null) .visitEnd(); + // public static String[] MODULES_TO_HASH = new String[] {....}; + cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, MODULES_TO_HASH, + "[Ljava/lang/String;", null, null) + .visitEnd(); + // public static int PACKAGES_IN_BOOT_LAYER; cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, PACKAGE_COUNT, "I", null, numPackages) @@ -283,15 +307,35 @@ int index = 0; for (ModuleDescriptorBuilder builder : builders) { - mv.visitInsn(DUP); // arrayref + mv.visitInsn(DUP); // arrayref pushInt(index++); - mv.visitLdcInsn(builder.md.name()); // value + mv.visitLdcInsn(builder.md.name()); // value mv.visitInsn(AASTORE); } mv.visitFieldInsn(PUTSTATIC, CLASSNAME, MODULE_NAMES, "[Ljava/lang/String;"); + // create the MODULES_TO_HASH array + pushInt(numModules); + mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); + + index = 0; + for (ModuleDescriptorBuilder builder : builders) { + String mn = builder.md.name(); + String recordedHash = modulesToHash.get(mn); + if (recordedHash != null) { + mv.visitInsn(DUP); // arrayref + pushInt(index); + mv.visitLdcInsn(recordedHash); // value + mv.visitInsn(AASTORE); + } + index++; + } + + mv.visitFieldInsn(PUTSTATIC, CLASSNAME, MODULES_TO_HASH, + "[Ljava/lang/String;"); + mv.visitInsn(RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); @@ -315,15 +359,19 @@ } } - // provides + // provides (preserve iteration order) for (ModuleDescriptor.Provides p : md.provides().values()) { - stringSets.computeIfAbsent(p.providers(), s -> new StringSetBuilder(s)) + stringSets.computeIfAbsent(p.providers(), s -> new StringSetBuilder(s, true)) .increment(); } // uses stringSets.computeIfAbsent(md.uses(), s -> new StringSetBuilder(s)) .increment(); + + // hashes + JLMA.hashes(md).ifPresent(mh -> modulesToHash.putAll(mh.hashes())); + return builder; } @@ -484,13 +532,17 @@ conceals(pn); } - if (md.version().isPresent()) { - version(md.version().get()); - } + // version + md.version().ifPresent(this::version); + + // main class + md.mainClass().ifPresent(this::mainClass); - if (md.mainClass().isPresent()) { - mainClass(md.mainClass().get()); - } + // hashes + JLMA.hashes(md).ifPresent(mh -> { + algorithm(mh.algorithm()); + mh.names().forEach(mn -> moduleHash(mn, mh.hashFor(mn))); + }); putModuleDescriptor(); } @@ -603,7 +655,7 @@ /* * Invoke Builder.provides(String service, Set providers) * - * Set providers = new HashSet<>(); + * Set providers = new LinkedHashSet<>(); * providers.add(impl); * : * : @@ -652,6 +704,22 @@ mv.visitInsn(POP); } + void algorithm(String alg) { + mv.visitVarInsn(ALOAD, BUILDER_VAR); + mv.visitLdcInsn(alg); + mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, + "algorithm", STRING_SIG, false); + mv.visitInsn(POP); + } + + void moduleHash(String name, String hashString) { + mv.visitVarInsn(ALOAD, BUILDER_VAR); + mv.visitLdcInsn(name); + mv.visitLdcInsn(hashString); + mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, + "moduleHash", STRING_STRING_SIG, false); + mv.visitInsn(POP); + } } /* @@ -663,10 +731,17 @@ */ class StringSetBuilder { final Set names; + final boolean linked; int refCount; int localVarIndex; + + StringSetBuilder(Set names, boolean linked) { + this.names = names; + this.linked = linked; + } + StringSetBuilder(Set names) { - this.names = names; + this(names, false); } void increment() { @@ -704,11 +779,11 @@ "singleton", "(Ljava/lang/Object;)Ljava/util/Set;", false); mv.visitVarInsn(ASTORE, index); } else { - mv.visitTypeInsn(NEW, "java/util/HashSet"); + String cn = linked ? "java/util/LinkedHashSet" : "java/util/HashSet"; + mv.visitTypeInsn(NEW, cn); mv.visitInsn(DUP); pushInt(initialCapacity(names.size())); - mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashSet", - "", "(I)V", false); + mv.visitMethodInsn(INVOKESPECIAL, cn, "", "(I)V", false); mv.visitVarInsn(ASTORE, index); for (String t : names) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/Plugin.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/Plugin.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/Plugin.java Tue May 03 12:25:20 2016 -0700 @@ -201,6 +201,8 @@ * This method is called prior to invoke the plugin. * * @param config The plugin configuration. + * @throws IllegalArgumentException if a mandatory argument is missing or + * if an argument has invalid value. */ public default void configure(Map config) { } @@ -211,6 +213,9 @@ * * @param config The plugin configuration. * @param ctx The plugin context + * @throws IllegalArgumentException if a mandatory argument is missing or + * if an argument has invalid value. + * */ public default void configure(Map config, PluginContext ctx) { configure(config); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties Tue May 03 12:25:20 2016 -0700 @@ -33,9 +33,6 @@ main.opt.endian=\ \ --endian Byte order of generated jimage (default:native) -main.opt.genbom=\ -\ --genbom Generate a bom file containing jlink info - main.opt.saveopts=\ \ --saveopts Save jlink options in the given file diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java Tue May 03 12:25:20 2016 -0700 @@ -26,6 +26,7 @@ package jdk.tools.jmod; import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -34,14 +35,17 @@ import java.io.OutputStream; import java.io.PrintStream; import java.io.UncheckedIOException; -import java.lang.module.FindException; +import java.lang.module.Configuration; +import java.lang.module.ModuleReader; import java.lang.module.ModuleReference; import java.lang.module.ModuleFinder; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleDescriptor.Exports; +import java.lang.module.ModuleDescriptor.Provides; import java.lang.module.ModuleDescriptor.Requires; -import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleDescriptor.Version; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; +import java.lang.module.ResolutionException; +import java.lang.module.ResolvedModule; import java.net.URI; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; @@ -51,13 +55,16 @@ import java.nio.file.PathMatcher; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; import java.text.MessageFormat; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Formatter; +import java.util.Comparator; +import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -68,6 +75,7 @@ import java.util.ResourceBundle; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.jar.JarEntry; @@ -89,16 +97,14 @@ import jdk.internal.joptsimple.OptionSet; import jdk.internal.joptsimple.OptionSpec; import jdk.internal.joptsimple.ValueConverter; +import jdk.internal.misc.JavaLangModuleAccess; +import jdk.internal.misc.SharedSecrets; import jdk.internal.module.ConfigurableModuleFinder; import jdk.internal.module.ConfigurableModuleFinder.Phase; -import jdk.internal.module.Hasher; -import jdk.internal.module.Hasher.DependencyHashes; +import jdk.internal.module.ModuleHashes; import jdk.internal.module.ModuleInfoExtender; -import static java.util.function.Function.identity; import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toMap; /** * Implementation for the jmod tool. @@ -127,21 +133,6 @@ } } - static void fail(Class type, - String format, - Object... args) throws T { - String msg = new Formatter().format(format, args).toString(); - try { - T t = type.getConstructor(String.class).newInstance(msg); - throw t; - } catch (InstantiationException | - InvocationTargetException | - NoSuchMethodException | - IllegalAccessException e) { - throw new InternalError("Unable to create an instance of " + type, e); - } - } - private static final String PROGNAME = "jmod"; private static final String MODULE_INFO = "module-info.class"; @@ -161,7 +152,8 @@ enum Mode { CREATE, LIST, - DESCRIBE + DESCRIBE, + HASH }; static class Options { @@ -179,7 +171,8 @@ String osName; String osArch; String osVersion; - Pattern dependenciesToHash; + Pattern modulesToHash; + boolean dryrun; List excludes; } @@ -211,6 +204,9 @@ case DESCRIBE: ok = describe(); break; + case HASH: + ok = hashModules(); + break; default: throw new AssertionError("Unknown mode: " + options.mode.name()); } @@ -248,26 +244,8 @@ } } - private Map modulesToPath(Set modules) { - ModuleFinder finder = options.moduleFinder; - - Map modPaths = new HashMap<>(); - for (ModuleDescriptor m : modules) { - String name = m.name(); - - Optional omref = finder.find(name); - if (!omref.isPresent()) { - // this should not happen, module path bug? - fail(InternalError.class, - "Selected module %s not on module path", - name); - } - - URI uri = omref.get().location().get(); - modPaths.put(name, Paths.get(uri)); - - } - return modPaths; + private boolean hashModules() { + return new Hasher(options.moduleFinder).run(); } private boolean describe() throws IOException { @@ -297,6 +275,8 @@ .collect(joining(" ")); } + private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess(); + private boolean printModuleDescriptor(InputStream in) throws IOException { @@ -311,74 +291,45 @@ StringBuilder sb = new StringBuilder(); sb.append("\n").append(md.toNameAndVersion()); - List requires = md.requires().stream().sorted().collect(toList()); - if (!requires.isEmpty()) { - requires.forEach(r -> { - sb.append("\n requires "); - if (!r.modifiers().isEmpty()) - sb.append(toString(r.modifiers())).append(" "); - sb.append(r.name()); - }); - } - - List l = md.uses().stream().sorted().collect(toList()); - if (!l.isEmpty()) { - l.forEach(sv -> sb.append("\n uses ").append(sv)); - } + md.requires().stream() + .sorted(Comparator.comparing(Requires::name)) + .forEach(r -> { + sb.append("\n requires "); + if (!r.modifiers().isEmpty()) + sb.append(toString(r.modifiers())).append(" "); + sb.append(r.name()); + }); - List exports = sortExports(md.exports()); - if (!exports.isEmpty()) { - exports.forEach(ex -> sb.append("\n exports ").append(ex)); - } + md.uses().stream().sorted() + .forEach(s -> sb.append("\n uses ").append(s)); - l = md.conceals().stream().sorted().collect(toList()); - if (!l.isEmpty()) { - l.forEach(p -> sb.append("\n conceals ").append(p)); - } + md.exports().stream() + .sorted(Comparator.comparing(Exports::source)) + .forEach(p -> sb.append("\n exports ").append(p)); - Map provides = md.provides(); - if (!provides.isEmpty()) { - provides.values().forEach(p -> - sb.append("\n provides ").append(p.service()) - .append(" with ") - .append(toString(p.providers()))); - } + md.conceals().stream().sorted() + .forEach(p -> sb.append("\n conceals ").append(p)); - Optional mc = md.mainClass(); - if (mc.isPresent()) - sb.append("\n main-class " + mc.get()); - - + md.provides().values().stream() + .sorted(Comparator.comparing(Provides::service)) + .forEach(p -> sb.append("\n provides ").append(p.service()) + .append(" with ") + .append(toString(p.providers()))); - Optional osname = md.osName(); - if (osname.isPresent()) - sb.append("\n operating-system-name " + osname.get()); + md.mainClass().ifPresent(v -> sb.append("\n main-class " + v)); - Optional osarch = md.osArch(); - if (osarch.isPresent()) - sb.append("\n operating-system-architecture " + osarch.get()); - - Optional osversion = md.osVersion(); - if (osversion.isPresent()) - sb.append("\n operating-system-version " + osversion.get()); + md.osName().ifPresent(v -> sb.append("\n operating-system-name " + v)); - try { - Method m = ModuleDescriptor.class.getDeclaredMethod("hashes"); - m.setAccessible(true); - @SuppressWarnings("unchecked") - Optional optHashes = - (Optional) m.invoke(md); + md.osArch().ifPresent(v -> sb.append("\n operating-system-architecture " + v)); + + md.osVersion().ifPresent(v -> sb.append("\n operating-system-version " + v)); - if (optHashes.isPresent()) { - Hasher.DependencyHashes hashes = optHashes.get(); - hashes.names().stream().forEach(mod -> - sb.append("\n hashes ").append(mod).append(" ") - .append(hashes.algorithm()).append(" ") - .append(hashes.hashFor(mod))); - } - } catch (ReflectiveOperationException x) { - throw new InternalError(x); - } + JLMA.hashes(md).ifPresent( + hashes -> hashes.names().stream().sorted().forEach( + mod -> sb.append("\n hashes ").append(mod).append(" ") + .append(hashes.algorithm()).append(" ") + .append(hashes.hashFor(mod)))); + out.println(sb.toString()); return true; } @@ -387,21 +338,6 @@ return false; } - static List sortExports(Set exports) { - Map map = - exports.stream() - .collect(toMap(ModuleDescriptor.Exports::source, - identity())); - List sources = exports.stream() - .map(ModuleDescriptor.Exports::source) - .sorted() - .collect(toList()); - - List l = new ArrayList<>(); - sources.forEach(e -> l.add(map.get(e))); - return l; - } - private boolean create() throws IOException { JmodFileWriter jmod = new JmodFileWriter(); @@ -410,8 +346,9 @@ Path target = options.jmodFile; Path tempTarget = target.resolveSibling(target.getFileName() + ".tmp"); try { - try (OutputStream out = Files.newOutputStream(tempTarget)) { - jmod.write(out); + try (OutputStream out = Files.newOutputStream(tempTarget); + BufferedOutputStream bos = new BufferedOutputStream(out)) { + jmod.write(bos); } Files.move(tempTarget, target); } catch (Exception e) { @@ -428,7 +365,6 @@ } private class JmodFileWriter { - final ModuleFinder moduleFinder = options.moduleFinder; final List cmds = options.cmds; final List libs = options.libs; final List configs = options.configs; @@ -438,8 +374,8 @@ final String osName = options.osName; final String osArch = options.osArch; final String osVersion = options.osVersion; - final Pattern dependenciesToHash = options.dependenciesToHash; final List excludes = options.excludes; + final Hasher hasher = hasher(); JmodFileWriter() { } @@ -545,11 +481,13 @@ if (moduleVersion != null) extender.version(moduleVersion); - // --hash-dependencies - if (dependenciesToHash != null) { - String name = descriptor.name(); - Set dependences = descriptor.requires(); - extender.hashes(hashDependences(name, dependences)); + if (hasher != null) { + ModuleHashes moduleHashes = hasher.computeHashes(descriptor.name()); + if (moduleHashes != null) { + extender.hashes(moduleHashes); + } else { + warning("warn.no.module.hashes", descriptor.name()); + } } // write the (possibly extended or modified) module-info.class @@ -561,38 +499,56 @@ } } - /** - * Examines the module dependences of the given module - * and computes the hash of any module that matches the - * pattern {@code dependenciesToHash}. + /* + * Hasher resolves a module graph using the --hash-modules PATTERN + * as the roots. + * + * The jmod file is being created and does not exist in the + * given modulepath. */ - DependencyHashes hashDependences(String name, Set moduleDependences) - throws IOException - { - Set descriptors = new HashSet<>(); - for (Requires md: moduleDependences) { - String dn = md.name(); - if (dependenciesToHash.matcher(dn).find()) { - try { - Optional omref = moduleFinder.find(dn); - if (!omref.isPresent()) { - throw new RuntimeException("Hashing module " + name - + " dependencies, unable to find module " + dn - + " on module path"); + private Hasher hasher() { + if (options.modulesToHash == null) + return null; + + try { + Supplier miSupplier = newModuleInfoSupplier(); + if (miSupplier == null) { + throw new IOException(MODULE_INFO + " not found"); + } + + ModuleDescriptor descriptor; + try (InputStream in = miSupplier.get()) { + descriptor = ModuleDescriptor.read(in); + } + + URI uri = options.jmodFile.toUri(); + ModuleReference mref = new ModuleReference(descriptor, uri, new Supplier<>() { + @Override + public ModuleReader get() { + throw new UnsupportedOperationException(); + } + }); + + // compose a module finder with the module path and also + // a module finder that can find the jmod file being created + ModuleFinder finder = ModuleFinder.compose(options.moduleFinder, + new ModuleFinder() { + @Override + public Optional find(String name) { + if (descriptor.name().equals(name)) + return Optional.of(mref); + else return Optional.empty(); } - descriptors.add(omref.get().descriptor()); - } catch (FindException x) { - throw new IOException("error reading module path", x); - } - } - } - Map map = modulesToPath(descriptors); - if (map.size() == 0) { - return null; - } else { - // use SHA-256 for now, easy to make this configurable if needed - return Hasher.generate(map, "SHA-256"); + @Override + public Set findAll() { + return Collections.singleton(mref); + } + }); + + return new Hasher(finder); + } catch (IOException e) { + throw new UncheckedIOException(e); } } @@ -765,6 +721,273 @@ } } + /** + * Compute and record hashes + */ + private class Hasher { + final ModuleFinder moduleFinder; + final Map moduleNameToPath; + final Set modules; + final Configuration configuration; + final boolean dryrun = options.dryrun; + Hasher(ModuleFinder finder) { + this.moduleFinder = finder; + // Determine the modules that matches the pattern {@code modulesToHash} + this.modules = moduleFinder.findAll().stream() + .map(mref -> mref.descriptor().name()) + .filter(mn -> options.modulesToHash.matcher(mn).find()) + .collect(Collectors.toSet()); + + // a map from a module name to Path of the packaged module + this.moduleNameToPath = moduleFinder.findAll().stream() + .map(mref -> mref.descriptor().name()) + .collect(Collectors.toMap(Function.identity(), mn -> moduleToPath(mn))); + + // get a resolved module graph + Configuration config = null; + try { + config = Configuration.empty() + .resolveRequires(ModuleFinder.ofSystem(), moduleFinder, modules); + } catch (ResolutionException e) { + warning("warn.module.resolution.fail", e.getMessage()); + } + this.configuration = config; + } + + /** + * This method is for jmod hash command. + * + * Identify the base modules in the module graph, i.e. no outgoing edge + * to any of the modules to be hashed. + * + * For each base module M, compute the hashes of all modules that depend + * upon M directly or indirectly. Then update M's module-info.class + * to record the hashes. + */ + boolean run() { + if (configuration == null) + return false; + + // transposed graph containing the the packaged modules and + // its transitive dependences matching --hash-modules + Map> graph = new HashMap<>(); + for (String root : modules) { + Deque deque = new ArrayDeque<>(); + deque.add(root); + Set visited = new HashSet<>(); + while (!deque.isEmpty()) { + String mn = deque.pop(); + if (!visited.contains(mn)) { + visited.add(mn); + + if (modules.contains(mn)) + graph.computeIfAbsent(mn, _k -> new HashSet<>()); + + ResolvedModule resolvedModule = configuration.findModule(mn).get(); + for (ResolvedModule dm : resolvedModule.reads()) { + String name = dm.name(); + if (!visited.contains(name)) { + deque.push(name); + } + + // reverse edge + if (modules.contains(name) && modules.contains(mn)) { + graph.computeIfAbsent(name, _k -> new HashSet<>()).add(mn); + } + } + } + } + } + + if (dryrun) + out.println("Dry run:"); + + // each node in a transposed graph is a matching packaged module + // in which the hash of the modules that depend upon it is recorded + graph.entrySet().stream() + .filter(e -> !e.getValue().isEmpty()) + .forEach(e -> { + String mn = e.getKey(); + Map modulesForHash = e.getValue().stream() + .collect(Collectors.toMap(Function.identity(), + moduleNameToPath::get)); + ModuleHashes hashes = ModuleHashes.generate(modulesForHash, "SHA-256"); + if (dryrun) { + out.format("%s%n", mn); + hashes.names().stream() + .sorted() + .forEach(name -> out.format(" hashes %s %s %s%n", + name, hashes.algorithm(), hashes.hashFor(name))); + } else { + try { + updateModuleInfo(mn, hashes); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + }); + return true; + } + + /** + * Compute hashes of the specified module. + * + * It records the hashing modules that depend upon the specified + * module directly or indirectly. + */ + ModuleHashes computeHashes(String name) { + if (configuration == null) + return null; + + // the transposed graph includes all modules in the resolved graph + Map> graph = transpose(); + + // find the modules that transitively depend upon the specified name + Deque deque = new ArrayDeque<>(); + deque.add(name); + Set mods = visitNodes(graph, deque); + + // filter modules matching the pattern specified --hash-modules + // as well as itself as the jmod file is being generated + Map modulesForHash = mods.stream() + .filter(mn -> !mn.equals(name) && modules.contains(mn)) + .collect(Collectors.toMap(Function.identity(), moduleNameToPath::get)); + + if (modulesForHash.isEmpty()) + return null; + + return ModuleHashes.generate(modulesForHash, "SHA-256"); + } + + /** + * Returns all nodes traversed from the given roots. + */ + private Set visitNodes(Map> graph, + Deque roots) { + Set visited = new HashSet<>(); + while (!roots.isEmpty()) { + String mn = roots.pop(); + if (!visited.contains(mn)) { + visited.add(mn); + // the given roots may not be part of the graph + if (graph.containsKey(mn)) { + for (String dm : graph.get(mn)) { + if (!visited.contains(dm)) { + roots.push(dm); + } + } + } + } + } + return visited; + } + + /** + * Returns a transposed graph from the resolved module graph. + */ + private Map> transpose() { + Map> transposedGraph = new HashMap<>(); + Deque deque = new ArrayDeque<>(modules); + + Set visited = new HashSet<>(); + while (!deque.isEmpty()) { + String mn = deque.pop(); + if (!visited.contains(mn)) { + visited.add(mn); + + transposedGraph.computeIfAbsent(mn, _k -> new HashSet<>()); + + ResolvedModule resolvedModule = configuration.findModule(mn).get(); + for (ResolvedModule dm : resolvedModule.reads()) { + String name = dm.name(); + if (!visited.contains(name)) { + deque.push(name); + } + + // reverse edge + transposedGraph.computeIfAbsent(name, _k -> new HashSet<>()) + .add(mn); + } + } + } + return transposedGraph; + } + + /** + * Reads the given input stream of module-info.class and write + * the extended module-info.class with the given ModuleHashes + * + * @param in InputStream of module-info.class + * @param out OutputStream to write the extended module-info.class + * @param hashes ModuleHashes + */ + private void recordHashes(InputStream in, OutputStream out, ModuleHashes hashes) + throws IOException + { + ModuleInfoExtender extender = ModuleInfoExtender.newExtender(in); + extender.hashes(hashes); + extender.write(out); + } + + private void updateModuleInfo(String name, ModuleHashes moduleHashes) + throws IOException + { + Path target = moduleNameToPath.get(name); + Path tempTarget = target.resolveSibling(target.getFileName() + ".tmp"); + ZipFile zip = new ZipFile(target.toFile()); + try { + try (OutputStream out = Files.newOutputStream(tempTarget); + ZipOutputStream zos = new ZipOutputStream(out)) { + zip.stream().forEach(e -> { + try { + InputStream in = zip.getInputStream(e); + if (e.getName().equals(MODULE_INFO) || + e.getName().equals(Section.CLASSES.jmodDir() + "/" + MODULE_INFO)) { + ZipEntry ze = new ZipEntry(e.getName()); + ze.setTime(System.currentTimeMillis()); + zos.putNextEntry(ze); + recordHashes(in, zos, moduleHashes); + zos.closeEntry(); + } else { + zos.putNextEntry(e); + zos.write(in.readAllBytes()); + zos.closeEntry(); + } + } catch (IOException x) { + throw new UncheckedIOException(x); + } + }); + } + } catch (IOException|RuntimeException e) { + if (Files.exists(tempTarget)) { + try { + Files.delete(tempTarget); + } catch (IOException ioe) { + e.addSuppressed(ioe); + } + } + throw e; + } finally { + zip.close(); + } + out.println(getMessage("module.hashes.recorded", name)); + Files.move(tempTarget, target, StandardCopyOption.REPLACE_EXISTING); + } + + private Path moduleToPath(String name) { + ModuleReference mref = moduleFinder.find(name).orElseThrow( + () -> new InternalError("Selected module " + name + " not on module path")); + + URI uri = mref.location().get(); + Path path = Paths.get(uri); + String fn = path.getFileName().toString(); + if (!fn.endsWith(".jar") && !fn.endsWith(".jmod")) { + throw new InternalError(path + " is not a modular JAR or jmod file"); + } + return path; + } + } + enum Section { NATIVE_LIBS("native"), NATIVE_CMDS("bin"), @@ -921,7 +1144,8 @@ builder.append("\n").append(" Main operation modes:\n "); builder.append(getMessage("main.opt.mode.create")).append("\n "); builder.append(getMessage("main.opt.mode.list")).append("\n "); - builder.append(getMessage("main.opt.mode.describe")).append("\n\n"); + builder.append(getMessage("main.opt.mode.describe")).append("\n "); + builder.append(getMessage("main.opt.mode.hash")).append("\n\n"); String cmdfile = null; String[] lines = content.split("\n"); @@ -964,13 +1188,16 @@ .withValuesSeparatedBy(File.pathSeparatorChar) .withValuesConvertedBy(DirPathConverter.INSTANCE); + OptionSpec dryrun + = parser.accepts("dry-run", getMessage("main.opt.dry-run")); + OptionSpec excludes = parser.accepts("exclude", getMessage("main.opt.exclude")) .withRequiredArg() .withValuesConvertedBy(new GlobConverter()); - OptionSpec hashDependencies - = parser.accepts("hash-dependencies", getMessage("main.opt.hash-dependencies")) + OptionSpec hashModules + = parser.accepts("hash-modules", getMessage("main.opt.hash-modules")) .withRequiredArg() .withValuesConvertedBy(new PatternConverter()); @@ -1049,6 +1276,8 @@ options.cmds = opts.valuesOf(cmds); if (opts.has(config)) options.configs = opts.valuesOf(config); + if (opts.has(dryrun)) + options.dryrun = true; if (opts.has(excludes)) options.excludes = opts.valuesOf(excludes); if (opts.has(libs)) @@ -1069,27 +1298,39 @@ options.osArch = opts.valueOf(osArch); if (opts.has(osVersion)) options.osVersion = opts.valueOf(osVersion); - if (opts.has(hashDependencies)) { - options.dependenciesToHash = opts.valueOf(hashDependencies); - // if storing hashes of dependencies then the module path is required + if (opts.has(hashModules)) { + options.modulesToHash = opts.valueOf(hashModules); + // if storing hashes then the module path is required if (options.moduleFinder == null) - throw new CommandException("err.modulepath.must.be.specified").showUsage(true); + throw new CommandException("err.modulepath.must.be.specified") + .showUsage(true); } - if (words.size() <= 1) - throw new CommandException("err.jmod.must.be.specified").showUsage(true); - Path path = Paths.get(words.get(1)); - if (options.mode.equals(Mode.CREATE) && Files.exists(path)) - throw new CommandException("err.file.already.exists", path); - else if ((options.mode.equals(Mode.LIST) || - options.mode.equals(Mode.DESCRIBE)) - && Files.notExists(path)) - throw new CommandException("err.jmod.not.found", path); - options.jmodFile = path; + if (options.mode.equals(Mode.HASH)) { + if (options.moduleFinder == null || options.modulesToHash == null) + throw new CommandException("err.modulepath.must.be.specified") + .showUsage(true); + } else { + if (words.size() <= 1) + throw new CommandException("err.jmod.must.be.specified").showUsage(true); + Path path = Paths.get(words.get(1)); - if (words.size() > 2) - throw new CommandException("err.unknown.option", - words.subList(2, words.size())).showUsage(true); + if (options.mode.equals(Mode.CREATE) && Files.exists(path)) + throw new CommandException("err.file.already.exists", path); + else if ((options.mode.equals(Mode.LIST) || + options.mode.equals(Mode.DESCRIBE)) + && Files.notExists(path)) + throw new CommandException("err.jmod.not.found", path); + + if (options.dryrun) { + throw new CommandException("err.invalid.dryrun.option"); + } + options.jmodFile = path; + + if (words.size() > 2) + throw new CommandException("err.unknown.option", + words.subList(2, words.size())).showUsage(true); + } if (options.mode.equals(Mode.CREATE) && options.classpath == null) throw new CommandException("err.classpath.must.be.specified").showUsage(true); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties Tue May 03 12:25:20 2016 -0700 @@ -1,9 +1,9 @@ main.usage.summary=\ -Usage: {0} (create|list|describe) \n\ +Usage: {0} (create|list|describe|hash) \n\ use --help for a list of possible options main.usage=\ -Usage: {0} (create|list|describe) +Usage: {0} (create|list|describe|hash) error.prefix=Error: warn.prefix=Warning: @@ -14,6 +14,8 @@ \list - Prints the names of all the entries main.opt.mode.describe=\ \describe - Prints the module details +main.opt.mode.hash=\ +\hash - Records hashes of tied modules. main.opt.help=Print this usage message main.opt.version=Version information @@ -21,9 +23,9 @@ main.opt.libs=Location of native libraries main.opt.cmds=Location of native commands main.opt.config=Location of user-editable config files +main.opt.dry-run=Dry run of hash mode main.opt.exclude=Exclude files, given as a PATTERN main.opt.module-version= Module version -main.opt.modulepath=Module path main.opt.main-class=Main class main.opt.main-class.arg=class-name main.opt.os-name=Operating system name @@ -32,18 +34,25 @@ main.opt.os-arch.arg=os-arch main.opt.os-version=Operating system version main.opt.os-version.arg=os-version -main.opt.hash-dependencies=Compute and record hashes of dependencies matched by the pattern +main.opt.modulepath=Module path +main.opt.hash-modules=Compute and record hashes to tie a packaged module\ +\ with modules matching the given pattern and depending upon it directly\ +\ or indirectly. The hashes are recorded in the JMOD file being created, or\ +\ a JMOD file or modular JAR on the module path specified the jmod hash command. + main.opt.cmdfile=Read options from the specified file -err.missing.mode=one of create, list, or describe must be specified -err.invalid.mode=mode must be one of create, list, or describe: {0} +module.hashes.recorded=Hashes are recorded in module {0} + +err.missing.mode=one of create, list, describe, or hash must be specified +err.invalid.mode=mode must be one of create, list, describe, or hash: {0} err.classpath.must.be.specified=--class-path must be specified err.jmod.must.be.specified=jmod-file must be specified err.invalid.version=invalid module version {0} -err.output.must.be.specified:--output must be specified -err.mods.must.be.specified:--mods must be specified -err.modulepath.must.be.specified:--module-path must be specified when hashing dependencies -err.invalid.main-class:invalid main-class name: {0} +err.output.must.be.specified=--output must be specified +err.mods.must.be.specified=--mods must be specified +err.modulepath.must.be.specified=--module-path must be specified when hashing modules +err.invalid.main-class=invalid main-class name: {0} err.path.not.found=path not found: {0} err.path.not.valid=invalid path: {0} err.path.not.a.dir=path must be a directory: {0} @@ -54,5 +63,9 @@ err.unknown.option=unknown option(s): {0} err.missing.arg=no value given for {0} err.internal.error=internal error: {0} {1} {2} +err.invalid.dryrun.option=--dry-run can only be used with hash mode err.module.descriptor.not.found=Module descriptor not found warn.invalid.arg=Invalid classname or pathname not exist: {0} +warn.no.module.hashes=No hashes recorded: no module specified for hashing depends on {0} +warn.module.resolution.fail=No hashes recorded: {0} + diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/src/jdk.localedata/share/classes/sun/util/resources/provider/LocaleDataProvider.java --- a/jdk/src/jdk.localedata/share/classes/sun/util/resources/provider/LocaleDataProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/src/jdk.localedata/share/classes/sun/util/resources/provider/LocaleDataProvider.java Tue May 03 12:25:20 2016 -0700 @@ -46,7 +46,7 @@ Class c = Class.forName(LocaleDataProvider.class.getModule(), bundleName); if (c != null && ResourceBundle.class.isAssignableFrom(c)) { try { - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "deprecation"}) ResourceBundle rb = ((Class) c).newInstance(); return rb; } catch (InstantiationException | IllegalAccessException e) { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/ProblemList.txt --- a/jdk/test/ProblemList.txt Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/ProblemList.txt Tue May 03 12:25:20 2016 -0700 @@ -319,6 +319,8 @@ tools/launcher/FXLauncherTest.java 8068049 linux-all,macosx-all +tools/pack200/Pack200Props.java 8155857 generic-all + ############################################################################ # jdk_jdi diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/TEST.ROOT --- a/jdk/test/TEST.ROOT Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/TEST.ROOT Tue May 03 12:25:20 2016 -0700 @@ -26,9 +26,12 @@ # Allow querying of sun.arch.data.model in @requires clauses requires.properties=sun.arch.data.model -# Tests using jtreg 4.2 b01 features -requiredVersion=4.2 b01 +# Tests using jtreg 4.2 b02 features +requiredVersion=4.2 b02 # Path to libraries in the topmost test directory. This is needed so @library # does not need ../../ notation to reach them external.lib.roots = ../../ + +# Use new form of -Xpatch +useNewXpatch=true diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/com/sun/corba/5036554/TestCorbaBug.sh --- a/jdk/test/com/sun/corba/5036554/TestCorbaBug.sh Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/com/sun/corba/5036554/TestCorbaBug.sh Tue May 03 12:25:20 2016 -0700 @@ -81,9 +81,9 @@ chmod -fR 777 bug -${COMPILEJAVA}${FS}bin${FS}javac -d . bug${FS}*.java +${COMPILEJAVA}${FS}bin${FS}javac -addmods java.corba -d . bug${FS}*.java -${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -cp . bug/JavaBug > test.out 2>&1 +${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -addmods java.corba -cp . bug/JavaBug > test.out 2>&1 grep "NullPointerException" test.out diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/com/sun/corba/7130985/CorbaExceptionsCompileTest.java --- a/jdk/test/com/sun/corba/7130985/CorbaExceptionsCompileTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/com/sun/corba/7130985/CorbaExceptionsCompileTest.java Tue May 03 12:25:20 2016 -0700 @@ -27,7 +27,8 @@ * @summary Four helper classes missing in Sun JDK * @library /lib/testlibrary * @build jdk.testlibrary.* - * @run main CorbaExceptionsCompileTest + * @compile -addmods java.corba CorbaExceptionsCompileTest.java + * @run main/othervm -addmods java.corba CorbaExceptionsCompileTest */ import java.io.*; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/com/sun/corba/se/impl/io/HookPutFieldsTest.java --- a/jdk/test/com/sun/corba/se/impl/io/HookPutFieldsTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/com/sun/corba/se/impl/io/HookPutFieldsTest.java Tue May 03 12:25:20 2016 -0700 @@ -25,6 +25,8 @@ * @test * @bug 7095856 * @summary OutputStreamHook doesn't handle null values + * @compile -addmods java.corba HookPutFieldsTest.java + * @run main/othervm -addmods java.corba HookPutFieldsTest */ import java.net.InetAddress; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/com/sun/corba/se/impl/orb/SetDefaultORBTest.java --- a/jdk/test/com/sun/corba/se/impl/orb/SetDefaultORBTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/com/sun/corba/se/impl/orb/SetDefaultORBTest.java Tue May 03 12:25:20 2016 -0700 @@ -25,7 +25,8 @@ * @test * @bug 8028215 * @summary SetDefaultORBTest setting ORB impl via properties test - * @run main/othervm SetDefaultORBTest + * @compile -addmods java.corba SetDefaultORBTest.java + * @run main/othervm -addmods java.corba SetDefaultORBTest * */ diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/com/sun/net/httpserver/bugs/B6373555.java --- a/jdk/test/com/sun/net/httpserver/bugs/B6373555.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/com/sun/net/httpserver/bugs/B6373555.java Tue May 03 12:25:20 2016 -0700 @@ -29,7 +29,6 @@ import java.net.*; import java.io.*; -import javax.xml.soap.*; import java.util.*; import com.sun.net.httpserver.*; import java.util.concurrent.*; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleBaseTest.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleBaseTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleBaseTest.java Tue May 03 12:25:20 2016 -0700 @@ -148,6 +148,7 @@ COMPARE_AND_EXCHANGE_ACQUIRE(TestAccessType.COMPARE_AND_EXCHANGE), COMPARE_AND_EXCHANGE_RELEASE(TestAccessType.COMPARE_AND_EXCHANGE), WEAK_COMPARE_AND_SET(TestAccessType.COMPARE_AND_SET), + WEAK_COMPARE_AND_SET_VOLATILE(TestAccessType.COMPARE_AND_SET), WEAK_COMPARE_AND_SET_ACQUIRE(TestAccessType.COMPARE_AND_SET), WEAK_COMPARE_AND_SET_RELEASE(TestAccessType.COMPARE_AND_SET), GET_AND_SET(TestAccessType.GET_AND_SET), diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessBoolean.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessBoolean.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessBoolean.java Tue May 03 12:25:20 2016 -0700 @@ -104,6 +104,7 @@ assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -280,6 +281,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(recv, true, false); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(recv, true, false); }); @@ -362,6 +367,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(true, false); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(true, false); }); @@ -434,6 +443,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(recv, true, false); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(recv, true, false); }); @@ -506,6 +519,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(true, false); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(true, false); }); @@ -585,6 +602,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, i, true, false); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, i, true, false); }); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessByte.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessByte.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessByte.java Tue May 03 12:25:20 2016 -0700 @@ -104,6 +104,7 @@ assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -280,6 +281,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(recv, (byte)1, (byte)2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(recv, (byte)1, (byte)2); }); @@ -362,6 +367,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile((byte)1, (byte)2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire((byte)1, (byte)2); }); @@ -434,6 +443,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(recv, (byte)1, (byte)2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(recv, (byte)1, (byte)2); }); @@ -506,6 +519,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile((byte)1, (byte)2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire((byte)1, (byte)2); }); @@ -585,6 +602,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, i, (byte)1, (byte)2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, i, (byte)1, (byte)2); }); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessChar.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessChar.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessChar.java Tue May 03 12:25:20 2016 -0700 @@ -104,6 +104,7 @@ assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -280,6 +281,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(recv, 'a', 'b'); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(recv, 'a', 'b'); }); @@ -362,6 +367,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile('a', 'b'); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire('a', 'b'); }); @@ -434,6 +443,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(recv, 'a', 'b'); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(recv, 'a', 'b'); }); @@ -506,6 +519,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile('a', 'b'); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire('a', 'b'); }); @@ -585,6 +602,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, i, 'a', 'b'); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, i, 'a', 'b'); }); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessDouble.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessDouble.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessDouble.java Tue May 03 12:25:20 2016 -0700 @@ -104,6 +104,7 @@ assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -280,6 +281,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(recv, 1.0d, 2.0d); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(recv, 1.0d, 2.0d); }); @@ -362,6 +367,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(1.0d, 2.0d); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(1.0d, 2.0d); }); @@ -434,6 +443,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(recv, 1.0d, 2.0d); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(recv, 1.0d, 2.0d); }); @@ -506,6 +519,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(1.0d, 2.0d); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(1.0d, 2.0d); }); @@ -585,6 +602,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, i, 1.0d, 2.0d); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, i, 1.0d, 2.0d); }); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessFloat.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessFloat.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessFloat.java Tue May 03 12:25:20 2016 -0700 @@ -104,6 +104,7 @@ assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -280,6 +281,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(recv, 1.0f, 2.0f); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(recv, 1.0f, 2.0f); }); @@ -362,6 +367,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(1.0f, 2.0f); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(1.0f, 2.0f); }); @@ -434,6 +443,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(recv, 1.0f, 2.0f); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(recv, 1.0f, 2.0f); }); @@ -506,6 +519,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(1.0f, 2.0f); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(1.0f, 2.0f); }); @@ -585,6 +602,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, i, 1.0f, 2.0f); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, i, 1.0f, 2.0f); }); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessInt.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessInt.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessInt.java Tue May 03 12:25:20 2016 -0700 @@ -104,6 +104,7 @@ assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -421,12 +422,19 @@ assertEquals(x, 2, "weakCompareAndSetRelease int"); } + { + boolean r = vh.weakCompareAndSetVolatile(recv, 2, 1); + assertEquals(r, true, "weakCompareAndSetVolatile int"); + int x = (int) vh.get(recv); + assertEquals(x, 1, "weakCompareAndSetVolatile int value"); + } + // Compare set and get { - int o = (int) vh.getAndSet(recv, 1); - assertEquals(o, 2, "getAndSet int"); + int o = (int) vh.getAndSet(recv, 2); + assertEquals(o, 1, "getAndSet int"); int x = (int) vh.get(recv); - assertEquals(x, 1, "getAndSet int value"); + assertEquals(x, 2, "getAndSet int value"); } vh.set(recv, 1); @@ -549,18 +557,25 @@ } { - boolean r = (boolean) vh.weakCompareAndSetRelease( 1, 2); + boolean r = (boolean) vh.weakCompareAndSetRelease(1, 2); assertEquals(r, true, "weakCompareAndSetRelease int"); int x = (int) vh.get(); assertEquals(x, 2, "weakCompareAndSetRelease int"); } + { + boolean r = (boolean) vh.weakCompareAndSetVolatile(2, 1); + assertEquals(r, true, "weakCompareAndSetVolatile int"); + int x = (int) vh.get(); + assertEquals(x, 1, "weakCompareAndSetVolatile int value"); + } + // Compare set and get { - int o = (int) vh.getAndSet( 1); - assertEquals(o, 2, "getAndSet int"); + int o = (int) vh.getAndSet( 2); + assertEquals(o, 1, "getAndSet int"); int x = (int) vh.get(); - assertEquals(x, 1, "getAndSet int value"); + assertEquals(x, 2, "getAndSet int value"); } vh.set(1); @@ -692,12 +707,19 @@ assertEquals(x, 2, "weakCompareAndSetRelease int"); } + { + boolean r = vh.weakCompareAndSetVolatile(array, i, 2, 1); + assertEquals(r, true, "weakCompareAndSetVolatile int"); + int x = (int) vh.get(array, i); + assertEquals(x, 1, "weakCompareAndSetVolatile int value"); + } + // Compare set and get { - int o = (int) vh.getAndSet(array, i, 1); - assertEquals(o, 2, "getAndSet int"); + int o = (int) vh.getAndSet(array, i, 2); + assertEquals(o, 1, "getAndSet int"); int x = (int) vh.get(array, i); - assertEquals(x, 1, "getAndSet int value"); + assertEquals(x, 2, "getAndSet int value"); } vh.set(array, i, 1); @@ -778,6 +800,10 @@ }); checkIOOBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, 1, 2); + }); + + checkIOOBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, 1, 2); }); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessLong.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessLong.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessLong.java Tue May 03 12:25:20 2016 -0700 @@ -104,6 +104,7 @@ assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -421,12 +422,19 @@ assertEquals(x, 2L, "weakCompareAndSetRelease long"); } + { + boolean r = vh.weakCompareAndSetVolatile(recv, 2L, 1L); + assertEquals(r, true, "weakCompareAndSetVolatile long"); + long x = (long) vh.get(recv); + assertEquals(x, 1L, "weakCompareAndSetVolatile long value"); + } + // Compare set and get { - long o = (long) vh.getAndSet(recv, 1L); - assertEquals(o, 2L, "getAndSet long"); + long o = (long) vh.getAndSet(recv, 2L); + assertEquals(o, 1L, "getAndSet long"); long x = (long) vh.get(recv); - assertEquals(x, 1L, "getAndSet long value"); + assertEquals(x, 2L, "getAndSet long value"); } vh.set(recv, 1L); @@ -549,18 +557,25 @@ } { - boolean r = (boolean) vh.weakCompareAndSetRelease( 1L, 2L); + boolean r = (boolean) vh.weakCompareAndSetRelease(1L, 2L); assertEquals(r, true, "weakCompareAndSetRelease long"); long x = (long) vh.get(); assertEquals(x, 2L, "weakCompareAndSetRelease long"); } + { + boolean r = (boolean) vh.weakCompareAndSetVolatile(2L, 1L); + assertEquals(r, true, "weakCompareAndSetVolatile long"); + long x = (long) vh.get(); + assertEquals(x, 1L, "weakCompareAndSetVolatile long value"); + } + // Compare set and get { - long o = (long) vh.getAndSet( 1L); - assertEquals(o, 2L, "getAndSet long"); + long o = (long) vh.getAndSet( 2L); + assertEquals(o, 1L, "getAndSet long"); long x = (long) vh.get(); - assertEquals(x, 1L, "getAndSet long value"); + assertEquals(x, 2L, "getAndSet long value"); } vh.set(1L); @@ -692,12 +707,19 @@ assertEquals(x, 2L, "weakCompareAndSetRelease long"); } + { + boolean r = vh.weakCompareAndSetVolatile(array, i, 2L, 1L); + assertEquals(r, true, "weakCompareAndSetVolatile long"); + long x = (long) vh.get(array, i); + assertEquals(x, 1L, "weakCompareAndSetVolatile long value"); + } + // Compare set and get { - long o = (long) vh.getAndSet(array, i, 1L); - assertEquals(o, 2L, "getAndSet long"); + long o = (long) vh.getAndSet(array, i, 2L); + assertEquals(o, 1L, "getAndSet long"); long x = (long) vh.get(array, i); - assertEquals(x, 1L, "getAndSet long value"); + assertEquals(x, 2L, "getAndSet long value"); } vh.set(array, i, 1L); @@ -778,6 +800,10 @@ }); checkIOOBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, 1L, 2L); + }); + + checkIOOBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, 1L, 2L); }); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessShort.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessShort.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessShort.java Tue May 03 12:25:20 2016 -0700 @@ -104,6 +104,7 @@ assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -280,6 +281,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(recv, (short)1, (short)2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(recv, (short)1, (short)2); }); @@ -362,6 +367,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile((short)1, (short)2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire((short)1, (short)2); }); @@ -434,6 +443,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(recv, (short)1, (short)2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(recv, (short)1, (short)2); }); @@ -506,6 +519,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile((short)1, (short)2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire((short)1, (short)2); }); @@ -585,6 +602,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, i, (short)1, (short)2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, i, (short)1, (short)2); }); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessString.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessString.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestAccessString.java Tue May 03 12:25:20 2016 -0700 @@ -104,6 +104,7 @@ assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -435,12 +436,19 @@ assertEquals(x, "bar", "weakCompareAndSetRelease String"); } + { + boolean r = vh.weakCompareAndSetVolatile(recv, "bar", "foo"); + assertEquals(r, true, "weakCompareAndSetVolatile String"); + String x = (String) vh.get(recv); + assertEquals(x, "foo", "weakCompareAndSetVolatile String value"); + } + // Compare set and get { - String o = (String) vh.getAndSet(recv, "foo"); - assertEquals(o, "bar", "getAndSet String"); + String o = (String) vh.getAndSet(recv, "bar"); + assertEquals(o, "foo", "getAndSet String"); String x = (String) vh.get(recv); - assertEquals(x, "foo", "getAndSet String value"); + assertEquals(x, "bar", "getAndSet String value"); } } @@ -561,18 +569,25 @@ } { - boolean r = (boolean) vh.weakCompareAndSetRelease( "foo", "bar"); + boolean r = (boolean) vh.weakCompareAndSetRelease("foo", "bar"); assertEquals(r, true, "weakCompareAndSetRelease String"); String x = (String) vh.get(); assertEquals(x, "bar", "weakCompareAndSetRelease String"); } + { + boolean r = (boolean) vh.weakCompareAndSetVolatile("bar", "foo"); + assertEquals(r, true, "weakCompareAndSetVolatile String"); + String x = (String) vh.get(); + assertEquals(x, "foo", "weakCompareAndSetVolatile String value"); + } + // Compare set and get { - String o = (String) vh.getAndSet( "foo"); - assertEquals(o, "bar", "getAndSet String"); + String o = (String) vh.getAndSet( "bar"); + assertEquals(o, "foo", "getAndSet String"); String x = (String) vh.get(); - assertEquals(x, "foo", "getAndSet String value"); + assertEquals(x, "bar", "getAndSet String value"); } } @@ -702,12 +717,19 @@ assertEquals(x, "bar", "weakCompareAndSetRelease String"); } + { + boolean r = vh.weakCompareAndSetVolatile(array, i, "bar", "foo"); + assertEquals(r, true, "weakCompareAndSetVolatile String"); + String x = (String) vh.get(array, i); + assertEquals(x, "foo", "weakCompareAndSetVolatile String value"); + } + // Compare set and get { - String o = (String) vh.getAndSet(array, i, "foo"); - assertEquals(o, "bar", "getAndSet String"); + String o = (String) vh.getAndSet(array, i, "bar"); + assertEquals(o, "foo", "getAndSet String"); String x = (String) vh.get(array, i); - assertEquals(x, "foo", "getAndSet String value"); + assertEquals(x, "bar", "getAndSet String value"); } } @@ -786,6 +808,10 @@ }); checkIOOBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, "foo", "bar"); + }); + + checkIOOBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, "foo", "bar"); }); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsChar.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsChar.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsChar.java Tue May 03 12:25:20 2016 -0700 @@ -93,6 +93,7 @@ assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -204,6 +205,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -265,6 +270,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -306,6 +315,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsDouble.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsDouble.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsDouble.java Tue May 03 12:25:20 2016 -0700 @@ -93,6 +93,7 @@ assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -239,6 +240,10 @@ }); checkROBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkROBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -334,6 +339,10 @@ }); checkIOOBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkIOOBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -415,6 +424,10 @@ }); checkIOOBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkIOOBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -487,6 +500,10 @@ }); checkISE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkISE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -562,6 +579,10 @@ }); checkISE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkISE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -699,12 +720,19 @@ assertEquals(x, VALUE_2, "weakCompareAndSetRelease double"); } + { + boolean r = vh.weakCompareAndSetVolatile(array, i, VALUE_2, VALUE_1); + assertEquals(r, true, "weakCompareAndSetVolatile double"); + double x = (double) vh.get(array, i); + assertEquals(x, VALUE_1, "weakCompareAndSetVolatile double value"); + } + // Compare set and get { - double o = (double) vh.getAndSet(array, i, VALUE_1); - assertEquals(o, VALUE_2, "getAndSet double"); + double o = (double) vh.getAndSet(array, i, VALUE_2); + assertEquals(o, VALUE_1, "getAndSet double"); double x = (double) vh.get(array, i); - assertEquals(x, VALUE_1, "getAndSet double value"); + assertEquals(x, VALUE_2, "getAndSet double value"); } } @@ -832,12 +860,19 @@ assertEquals(x, VALUE_2, "weakCompareAndSetRelease double"); } + { + boolean r = vh.weakCompareAndSetVolatile(array, i, VALUE_2, VALUE_1); + assertEquals(r, true, "weakCompareAndSetVolatile double"); + double x = (double) vh.get(array, i); + assertEquals(x, VALUE_1, "weakCompareAndSetVolatile double value"); + } + // Compare set and get { - double o = (double) vh.getAndSet(array, i, VALUE_1); - assertEquals(o, VALUE_2, "getAndSet double"); + double o = (double) vh.getAndSet(array, i, VALUE_2); + assertEquals(o, VALUE_1, "getAndSet double"); double x = (double) vh.get(array, i); - assertEquals(x, VALUE_1, "getAndSet double value"); + assertEquals(x, VALUE_2, "getAndSet double value"); } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsFloat.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsFloat.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsFloat.java Tue May 03 12:25:20 2016 -0700 @@ -93,6 +93,7 @@ assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -239,6 +240,10 @@ }); checkROBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkROBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -334,6 +339,10 @@ }); checkIOOBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkIOOBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -415,6 +424,10 @@ }); checkIOOBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkIOOBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -487,6 +500,10 @@ }); checkISE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkISE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -562,6 +579,10 @@ }); checkISE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkISE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -699,12 +720,19 @@ assertEquals(x, VALUE_2, "weakCompareAndSetRelease float"); } + { + boolean r = vh.weakCompareAndSetVolatile(array, i, VALUE_2, VALUE_1); + assertEquals(r, true, "weakCompareAndSetVolatile float"); + float x = (float) vh.get(array, i); + assertEquals(x, VALUE_1, "weakCompareAndSetVolatile float value"); + } + // Compare set and get { - float o = (float) vh.getAndSet(array, i, VALUE_1); - assertEquals(o, VALUE_2, "getAndSet float"); + float o = (float) vh.getAndSet(array, i, VALUE_2); + assertEquals(o, VALUE_1, "getAndSet float"); float x = (float) vh.get(array, i); - assertEquals(x, VALUE_1, "getAndSet float value"); + assertEquals(x, VALUE_2, "getAndSet float value"); } } @@ -832,12 +860,19 @@ assertEquals(x, VALUE_2, "weakCompareAndSetRelease float"); } + { + boolean r = vh.weakCompareAndSetVolatile(array, i, VALUE_2, VALUE_1); + assertEquals(r, true, "weakCompareAndSetVolatile float"); + float x = (float) vh.get(array, i); + assertEquals(x, VALUE_1, "weakCompareAndSetVolatile float value"); + } + // Compare set and get { - float o = (float) vh.getAndSet(array, i, VALUE_1); - assertEquals(o, VALUE_2, "getAndSet float"); + float o = (float) vh.getAndSet(array, i, VALUE_2); + assertEquals(o, VALUE_1, "getAndSet float"); float x = (float) vh.get(array, i); - assertEquals(x, VALUE_1, "getAndSet float value"); + assertEquals(x, VALUE_2, "getAndSet float value"); } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsInt.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsInt.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsInt.java Tue May 03 12:25:20 2016 -0700 @@ -38,10 +38,10 @@ import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; +import java.util.EnumSet; import java.util.List; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; +import static org.testng.Assert.*; public class VarHandleTestByteArrayAsInt extends VarHandleBaseByteArrayTest { static final int SIZE = Integer.BYTES; @@ -93,6 +93,7 @@ assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -232,6 +233,10 @@ }); checkROBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkROBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -320,6 +325,10 @@ }); checkIOOBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkIOOBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -408,6 +417,10 @@ }); checkIOOBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkIOOBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -487,6 +500,10 @@ }); checkISE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkISE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -569,6 +586,10 @@ }); checkISE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkISE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -713,12 +734,19 @@ assertEquals(x, VALUE_2, "weakCompareAndSetRelease int"); } + { + boolean r = vh.weakCompareAndSetVolatile(array, i, VALUE_2, VALUE_1); + assertEquals(r, true, "weakCompareAndSetVolatile int"); + int x = (int) vh.get(array, i); + assertEquals(x, VALUE_1, "weakCompareAndSetVolatile int value"); + } + // Compare set and get { - int o = (int) vh.getAndSet(array, i, VALUE_1); - assertEquals(o, VALUE_2, "getAndSet int"); + int o = (int) vh.getAndSet(array, i, VALUE_2); + assertEquals(o, VALUE_1, "getAndSet int"); int x = (int) vh.get(array, i); - assertEquals(x, VALUE_1, "getAndSet int value"); + assertEquals(x, VALUE_2, "getAndSet int value"); } vh.set(array, i, VALUE_1); @@ -855,12 +883,19 @@ assertEquals(x, VALUE_2, "weakCompareAndSetRelease int"); } + { + boolean r = vh.weakCompareAndSetVolatile(array, i, VALUE_2, VALUE_1); + assertEquals(r, true, "weakCompareAndSetVolatile int"); + int x = (int) vh.get(array, i); + assertEquals(x, VALUE_1, "weakCompareAndSetVolatile int value"); + } + // Compare set and get { - int o = (int) vh.getAndSet(array, i, VALUE_1); - assertEquals(o, VALUE_2, "getAndSet int"); + int o = (int) vh.getAndSet(array, i, VALUE_2); + assertEquals(o, VALUE_1, "getAndSet int"); int x = (int) vh.get(array, i); - assertEquals(x, VALUE_1, "getAndSet int value"); + assertEquals(x, VALUE_2, "getAndSet int value"); } vh.set(array, i, VALUE_1); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsLong.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsLong.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsLong.java Tue May 03 12:25:20 2016 -0700 @@ -93,6 +93,7 @@ assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -232,6 +233,10 @@ }); checkROBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkROBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -320,6 +325,10 @@ }); checkIOOBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkIOOBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -408,6 +417,10 @@ }); checkIOOBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkIOOBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -487,6 +500,10 @@ }); checkISE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkISE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -569,6 +586,10 @@ }); checkISE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkISE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -713,12 +734,19 @@ assertEquals(x, VALUE_2, "weakCompareAndSetRelease long"); } + { + boolean r = vh.weakCompareAndSetVolatile(array, i, VALUE_2, VALUE_1); + assertEquals(r, true, "weakCompareAndSetVolatile long"); + long x = (long) vh.get(array, i); + assertEquals(x, VALUE_1, "weakCompareAndSetVolatile long value"); + } + // Compare set and get { - long o = (long) vh.getAndSet(array, i, VALUE_1); - assertEquals(o, VALUE_2, "getAndSet long"); + long o = (long) vh.getAndSet(array, i, VALUE_2); + assertEquals(o, VALUE_1, "getAndSet long"); long x = (long) vh.get(array, i); - assertEquals(x, VALUE_1, "getAndSet long value"); + assertEquals(x, VALUE_2, "getAndSet long value"); } vh.set(array, i, VALUE_1); @@ -855,12 +883,19 @@ assertEquals(x, VALUE_2, "weakCompareAndSetRelease long"); } + { + boolean r = vh.weakCompareAndSetVolatile(array, i, VALUE_2, VALUE_1); + assertEquals(r, true, "weakCompareAndSetVolatile long"); + long x = (long) vh.get(array, i); + assertEquals(x, VALUE_1, "weakCompareAndSetVolatile long value"); + } + // Compare set and get { - long o = (long) vh.getAndSet(array, i, VALUE_1); - assertEquals(o, VALUE_2, "getAndSet long"); + long o = (long) vh.getAndSet(array, i, VALUE_2); + assertEquals(o, VALUE_1, "getAndSet long"); long x = (long) vh.get(array, i); - assertEquals(x, VALUE_1, "getAndSet long value"); + assertEquals(x, VALUE_2, "getAndSet long value"); } vh.set(array, i, VALUE_1); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsShort.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsShort.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestByteArrayAsShort.java Tue May 03 12:25:20 2016 -0700 @@ -93,6 +93,7 @@ assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -204,6 +205,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -265,6 +270,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -306,6 +315,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessInt.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessInt.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessInt.java Tue May 03 12:25:20 2016 -0700 @@ -228,12 +228,19 @@ assertEquals(x, 2, "weakCompareAndSetRelease int"); } + { + boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_VOLATILE).invokeExact(recv, 2, 1); + assertEquals(r, true, "weakCompareAndSetVolatile int"); + int x = (int) hs.get(TestAccessMode.GET).invokeExact(recv); + assertEquals(x, 1, "weakCompareAndSetVolatile int value"); + } + // Compare set and get { - int o = (int) hs.get(TestAccessMode.GET_AND_SET).invokeExact(recv, 1); - assertEquals(o, 2, "getAndSet int"); + int o = (int) hs.get(TestAccessMode.GET_AND_SET).invokeExact(recv, 2); + assertEquals(o, 1, "getAndSet int"); int x = (int) hs.get(TestAccessMode.GET).invokeExact(recv); - assertEquals(x, 1, "getAndSet int value"); + assertEquals(x, 2, "getAndSet int value"); } hs.get(TestAccessMode.SET).invokeExact(recv, 1); @@ -356,18 +363,25 @@ } { - boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_RELEASE).invokeExact( 1, 2); + boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_RELEASE).invokeExact(1, 2); assertEquals(r, true, "weakCompareAndSetRelease int"); int x = (int) hs.get(TestAccessMode.GET).invokeExact(); assertEquals(x, 2, "weakCompareAndSetRelease int"); } + { + boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_VOLATILE).invokeExact(2, 1); + assertEquals(r, true, "weakCompareAndSetVolatile int"); + int x = (int) hs.get(TestAccessMode.GET).invokeExact(); + assertEquals(x, 1, "weakCompareAndSetVolatile int value"); + } + // Compare set and get { - int o = (int) hs.get(TestAccessMode.GET_AND_SET).invokeExact( 1); - assertEquals(o, 2, "getAndSet int"); + int o = (int) hs.get(TestAccessMode.GET_AND_SET).invokeExact(2); + assertEquals(o, 1, "getAndSet int"); int x = (int) hs.get(TestAccessMode.GET).invokeExact(); - assertEquals(x, 1, "getAndSet int value"); + assertEquals(x, 2, "getAndSet int value"); } hs.get(TestAccessMode.SET).invokeExact(1); @@ -499,12 +513,19 @@ assertEquals(x, 2, "weakCompareAndSetRelease int"); } + { + boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_VOLATILE).invokeExact(array, i, 2, 1); + assertEquals(r, true, "weakCompareAndSetVolatile int"); + int x = (int) hs.get(TestAccessMode.GET).invokeExact(array, i); + assertEquals(x, 1, "weakCompareAndSetVolatile int value"); + } + // Compare set and get { - int o = (int) hs.get(TestAccessMode.GET_AND_SET).invokeExact(array, i, 1); - assertEquals(o, 2, "getAndSet int"); + int o = (int) hs.get(TestAccessMode.GET_AND_SET).invokeExact(array, i, 2); + assertEquals(o, 1, "getAndSet int"); int x = (int) hs.get(TestAccessMode.GET).invokeExact(array, i); - assertEquals(x, 1, "getAndSet int value"); + assertEquals(x, 2, "getAndSet int value"); } hs.get(TestAccessMode.SET).invokeExact(array, i, 1); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessLong.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessLong.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessLong.java Tue May 03 12:25:20 2016 -0700 @@ -228,12 +228,19 @@ assertEquals(x, 2L, "weakCompareAndSetRelease long"); } + { + boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_VOLATILE).invokeExact(recv, 2L, 1L); + assertEquals(r, true, "weakCompareAndSetVolatile long"); + long x = (long) hs.get(TestAccessMode.GET).invokeExact(recv); + assertEquals(x, 1L, "weakCompareAndSetVolatile long value"); + } + // Compare set and get { - long o = (long) hs.get(TestAccessMode.GET_AND_SET).invokeExact(recv, 1L); - assertEquals(o, 2L, "getAndSet long"); + long o = (long) hs.get(TestAccessMode.GET_AND_SET).invokeExact(recv, 2L); + assertEquals(o, 1L, "getAndSet long"); long x = (long) hs.get(TestAccessMode.GET).invokeExact(recv); - assertEquals(x, 1L, "getAndSet long value"); + assertEquals(x, 2L, "getAndSet long value"); } hs.get(TestAccessMode.SET).invokeExact(recv, 1L); @@ -356,18 +363,25 @@ } { - boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_RELEASE).invokeExact( 1L, 2L); + boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_RELEASE).invokeExact(1L, 2L); assertEquals(r, true, "weakCompareAndSetRelease long"); long x = (long) hs.get(TestAccessMode.GET).invokeExact(); assertEquals(x, 2L, "weakCompareAndSetRelease long"); } + { + boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_VOLATILE).invokeExact(2L, 1L); + assertEquals(r, true, "weakCompareAndSetVolatile long"); + long x = (long) hs.get(TestAccessMode.GET).invokeExact(); + assertEquals(x, 1L, "weakCompareAndSetVolatile long value"); + } + // Compare set and get { - long o = (long) hs.get(TestAccessMode.GET_AND_SET).invokeExact( 1L); - assertEquals(o, 2L, "getAndSet long"); + long o = (long) hs.get(TestAccessMode.GET_AND_SET).invokeExact(2L); + assertEquals(o, 1L, "getAndSet long"); long x = (long) hs.get(TestAccessMode.GET).invokeExact(); - assertEquals(x, 1L, "getAndSet long value"); + assertEquals(x, 2L, "getAndSet long value"); } hs.get(TestAccessMode.SET).invokeExact(1L); @@ -499,12 +513,19 @@ assertEquals(x, 2L, "weakCompareAndSetRelease long"); } + { + boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_VOLATILE).invokeExact(array, i, 2L, 1L); + assertEquals(r, true, "weakCompareAndSetVolatile long"); + long x = (long) hs.get(TestAccessMode.GET).invokeExact(array, i); + assertEquals(x, 1L, "weakCompareAndSetVolatile long value"); + } + // Compare set and get { - long o = (long) hs.get(TestAccessMode.GET_AND_SET).invokeExact(array, i, 1L); - assertEquals(o, 2L, "getAndSet long"); + long o = (long) hs.get(TestAccessMode.GET_AND_SET).invokeExact(array, i, 2L); + assertEquals(o, 1L, "getAndSet long"); long x = (long) hs.get(TestAccessMode.GET).invokeExact(array, i); - assertEquals(x, 1L, "getAndSet long value"); + assertEquals(x, 2L, "getAndSet long value"); } hs.get(TestAccessMode.SET).invokeExact(array, i, 1L); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessString.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessString.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessString.java Tue May 03 12:25:20 2016 -0700 @@ -228,12 +228,19 @@ assertEquals(x, "bar", "weakCompareAndSetRelease String"); } + { + boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_VOLATILE).invokeExact(recv, "bar", "foo"); + assertEquals(r, true, "weakCompareAndSetVolatile String"); + String x = (String) hs.get(TestAccessMode.GET).invokeExact(recv); + assertEquals(x, "foo", "weakCompareAndSetVolatile String value"); + } + // Compare set and get { - String o = (String) hs.get(TestAccessMode.GET_AND_SET).invokeExact(recv, "foo"); - assertEquals(o, "bar", "getAndSet String"); + String o = (String) hs.get(TestAccessMode.GET_AND_SET).invokeExact(recv, "bar"); + assertEquals(o, "foo", "getAndSet String"); String x = (String) hs.get(TestAccessMode.GET).invokeExact(recv); - assertEquals(x, "foo", "getAndSet String value"); + assertEquals(x, "bar", "getAndSet String value"); } } @@ -352,18 +359,25 @@ } { - boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_RELEASE).invokeExact( "foo", "bar"); + boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_RELEASE).invokeExact("foo", "bar"); assertEquals(r, true, "weakCompareAndSetRelease String"); String x = (String) hs.get(TestAccessMode.GET).invokeExact(); assertEquals(x, "bar", "weakCompareAndSetRelease String"); } + { + boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_VOLATILE).invokeExact("bar", "foo"); + assertEquals(r, true, "weakCompareAndSetVolatile String"); + String x = (String) hs.get(TestAccessMode.GET).invokeExact(); + assertEquals(x, "foo", "weakCompareAndSetVolatile String value"); + } + // Compare set and get { - String o = (String) hs.get(TestAccessMode.GET_AND_SET).invokeExact( "foo"); - assertEquals(o, "bar", "getAndSet String"); + String o = (String) hs.get(TestAccessMode.GET_AND_SET).invokeExact("bar"); + assertEquals(o, "foo", "getAndSet String"); String x = (String) hs.get(TestAccessMode.GET).invokeExact(); - assertEquals(x, "foo", "getAndSet String value"); + assertEquals(x, "bar", "getAndSet String value"); } } @@ -491,12 +505,19 @@ assertEquals(x, "bar", "weakCompareAndSetRelease String"); } + { + boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_VOLATILE).invokeExact(array, i, "bar", "foo"); + assertEquals(r, true, "weakCompareAndSetVolatile String"); + String x = (String) hs.get(TestAccessMode.GET).invokeExact(array, i); + assertEquals(x, "foo", "weakCompareAndSetVolatile String value"); + } + // Compare set and get { - String o = (String) hs.get(TestAccessMode.GET_AND_SET).invokeExact(array, i, "foo"); - assertEquals(o, "bar", "getAndSet String"); + String o = (String) hs.get(TestAccessMode.GET_AND_SET).invokeExact(array, i, "bar"); + assertEquals(o, "foo", "getAndSet String"); String x = (String) hs.get(TestAccessMode.GET).invokeExact(array, i); - assertEquals(x, "foo", "getAndSet String value"); + assertEquals(x, "bar", "getAndSet String value"); } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodTypeInt.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodTypeInt.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodTypeInt.java Tue May 03 12:25:20 2016 -0700 @@ -374,6 +374,32 @@ }); + // WeakCompareAndSetVolatile + // Incorrect argument types + checkNPE(() -> { // null receiver + boolean r = vh.weakCompareAndSetVolatile(null, 1, 1); + }); + checkCCE(() -> { // receiver reference class + boolean r = vh.weakCompareAndSetVolatile(Void.class, 1, 1); + }); + checkWMTE(() -> { // expected reference class + boolean r = vh.weakCompareAndSetVolatile(recv, Void.class, 1); + }); + checkWMTE(() -> { // actual reference class + boolean r = vh.weakCompareAndSetVolatile(recv, 1, Void.class); + }); + checkWMTE(() -> { // receiver primitive class + boolean r = vh.weakCompareAndSetVolatile(0, 1, 1); + }); + // Incorrect arity + checkWMTE(() -> { // 0 + boolean r = vh.weakCompareAndSetVolatile(); + }); + checkWMTE(() -> { // > + boolean r = vh.weakCompareAndSetVolatile(recv, 1, 1, Void.class); + }); + + // WeakCompareAndSetAcquire // Incorrect argument types checkNPE(() -> { // null receiver @@ -972,6 +998,23 @@ }); + // WeakCompareAndSetVolatile + // Incorrect argument types + checkWMTE(() -> { // expected reference class + boolean r = vh.weakCompareAndSetVolatile(Void.class, 1); + }); + checkWMTE(() -> { // actual reference class + boolean r = vh.weakCompareAndSetVolatile(1, Void.class); + }); + // Incorrect arity + checkWMTE(() -> { // 0 + boolean r = vh.weakCompareAndSetVolatile(); + }); + checkWMTE(() -> { // > + boolean r = vh.weakCompareAndSetVolatile(1, 1, Void.class); + }); + + // WeakCompareAndSetAcquire // Incorrect argument types checkWMTE(() -> { // expected reference class @@ -1566,6 +1609,35 @@ }); + // WeakCompareAndSetVolatile + // Incorrect argument types + checkNPE(() -> { // null receiver + boolean r = vh.weakCompareAndSetVolatile(null, 0, 1, 1); + }); + checkCCE(() -> { // receiver reference class + boolean r = vh.weakCompareAndSetVolatile(Void.class, 0, 1, 1); + }); + checkWMTE(() -> { // expected reference class + boolean r = vh.weakCompareAndSetVolatile(array, 0, Void.class, 1); + }); + checkWMTE(() -> { // actual reference class + boolean r = vh.weakCompareAndSetVolatile(array, 0, 1, Void.class); + }); + checkWMTE(() -> { // receiver primitive class + boolean r = vh.weakCompareAndSetVolatile(0, 0, 1, 1); + }); + checkWMTE(() -> { // index reference class + boolean r = vh.weakCompareAndSetVolatile(array, Void.class, 1, 1); + }); + // Incorrect arity + checkWMTE(() -> { // 0 + boolean r = vh.weakCompareAndSetVolatile(); + }); + checkWMTE(() -> { // > + boolean r = vh.weakCompareAndSetVolatile(array, 0, 1, 1, Void.class); + }); + + // WeakCompareAndSetAcquire // Incorrect argument types checkNPE(() -> { // null receiver diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodTypeLong.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodTypeLong.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodTypeLong.java Tue May 03 12:25:20 2016 -0700 @@ -374,6 +374,32 @@ }); + // WeakCompareAndSetVolatile + // Incorrect argument types + checkNPE(() -> { // null receiver + boolean r = vh.weakCompareAndSetVolatile(null, 1L, 1L); + }); + checkCCE(() -> { // receiver reference class + boolean r = vh.weakCompareAndSetVolatile(Void.class, 1L, 1L); + }); + checkWMTE(() -> { // expected reference class + boolean r = vh.weakCompareAndSetVolatile(recv, Void.class, 1L); + }); + checkWMTE(() -> { // actual reference class + boolean r = vh.weakCompareAndSetVolatile(recv, 1L, Void.class); + }); + checkWMTE(() -> { // receiver primitive class + boolean r = vh.weakCompareAndSetVolatile(0, 1L, 1L); + }); + // Incorrect arity + checkWMTE(() -> { // 0 + boolean r = vh.weakCompareAndSetVolatile(); + }); + checkWMTE(() -> { // > + boolean r = vh.weakCompareAndSetVolatile(recv, 1L, 1L, Void.class); + }); + + // WeakCompareAndSetAcquire // Incorrect argument types checkNPE(() -> { // null receiver @@ -972,6 +998,23 @@ }); + // WeakCompareAndSetVolatile + // Incorrect argument types + checkWMTE(() -> { // expected reference class + boolean r = vh.weakCompareAndSetVolatile(Void.class, 1L); + }); + checkWMTE(() -> { // actual reference class + boolean r = vh.weakCompareAndSetVolatile(1L, Void.class); + }); + // Incorrect arity + checkWMTE(() -> { // 0 + boolean r = vh.weakCompareAndSetVolatile(); + }); + checkWMTE(() -> { // > + boolean r = vh.weakCompareAndSetVolatile(1L, 1L, Void.class); + }); + + // WeakCompareAndSetAcquire // Incorrect argument types checkWMTE(() -> { // expected reference class @@ -1566,6 +1609,35 @@ }); + // WeakCompareAndSetVolatile + // Incorrect argument types + checkNPE(() -> { // null receiver + boolean r = vh.weakCompareAndSetVolatile(null, 0, 1L, 1L); + }); + checkCCE(() -> { // receiver reference class + boolean r = vh.weakCompareAndSetVolatile(Void.class, 0, 1L, 1L); + }); + checkWMTE(() -> { // expected reference class + boolean r = vh.weakCompareAndSetVolatile(array, 0, Void.class, 1L); + }); + checkWMTE(() -> { // actual reference class + boolean r = vh.weakCompareAndSetVolatile(array, 0, 1L, Void.class); + }); + checkWMTE(() -> { // receiver primitive class + boolean r = vh.weakCompareAndSetVolatile(0, 0, 1L, 1L); + }); + checkWMTE(() -> { // index reference class + boolean r = vh.weakCompareAndSetVolatile(array, Void.class, 1L, 1L); + }); + // Incorrect arity + checkWMTE(() -> { // 0 + boolean r = vh.weakCompareAndSetVolatile(); + }); + checkWMTE(() -> { // > + boolean r = vh.weakCompareAndSetVolatile(array, 0, 1L, 1L, Void.class); + }); + + // WeakCompareAndSetAcquire // Incorrect argument types checkNPE(() -> { // null receiver diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodTypeString.java --- a/jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodTypeString.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/VarHandleTestMethodTypeString.java Tue May 03 12:25:20 2016 -0700 @@ -374,6 +374,32 @@ }); + // WeakCompareAndSetVolatile + // Incorrect argument types + checkNPE(() -> { // null receiver + boolean r = vh.weakCompareAndSetVolatile(null, "foo", "foo"); + }); + checkCCE(() -> { // receiver reference class + boolean r = vh.weakCompareAndSetVolatile(Void.class, "foo", "foo"); + }); + checkCCE(() -> { // expected reference class + boolean r = vh.weakCompareAndSetVolatile(recv, Void.class, "foo"); + }); + checkCCE(() -> { // actual reference class + boolean r = vh.weakCompareAndSetVolatile(recv, "foo", Void.class); + }); + checkWMTE(() -> { // receiver primitive class + boolean r = vh.weakCompareAndSetVolatile(0, "foo", "foo"); + }); + // Incorrect arity + checkWMTE(() -> { // 0 + boolean r = vh.weakCompareAndSetVolatile(); + }); + checkWMTE(() -> { // > + boolean r = vh.weakCompareAndSetVolatile(recv, "foo", "foo", Void.class); + }); + + // WeakCompareAndSetAcquire // Incorrect argument types checkNPE(() -> { // null receiver @@ -878,6 +904,23 @@ }); + // WeakCompareAndSetVolatile + // Incorrect argument types + checkCCE(() -> { // expected reference class + boolean r = vh.weakCompareAndSetVolatile(Void.class, "foo"); + }); + checkCCE(() -> { // actual reference class + boolean r = vh.weakCompareAndSetVolatile("foo", Void.class); + }); + // Incorrect arity + checkWMTE(() -> { // 0 + boolean r = vh.weakCompareAndSetVolatile(); + }); + checkWMTE(() -> { // > + boolean r = vh.weakCompareAndSetVolatile("foo", "foo", Void.class); + }); + + // WeakCompareAndSetAcquire // Incorrect argument types checkCCE(() -> { // expected reference class @@ -1407,6 +1450,35 @@ }); + // WeakCompareAndSetVolatile + // Incorrect argument types + checkNPE(() -> { // null receiver + boolean r = vh.weakCompareAndSetVolatile(null, 0, "foo", "foo"); + }); + checkCCE(() -> { // receiver reference class + boolean r = vh.weakCompareAndSetVolatile(Void.class, 0, "foo", "foo"); + }); + checkCCE(() -> { // expected reference class + boolean r = vh.weakCompareAndSetVolatile(array, 0, Void.class, "foo"); + }); + checkCCE(() -> { // actual reference class + boolean r = vh.weakCompareAndSetVolatile(array, 0, "foo", Void.class); + }); + checkWMTE(() -> { // receiver primitive class + boolean r = vh.weakCompareAndSetVolatile(0, 0, "foo", "foo"); + }); + checkWMTE(() -> { // index reference class + boolean r = vh.weakCompareAndSetVolatile(array, Void.class, "foo", "foo"); + }); + // Incorrect arity + checkWMTE(() -> { // 0 + boolean r = vh.weakCompareAndSetVolatile(); + }); + checkWMTE(() -> { // > + boolean r = vh.weakCompareAndSetVolatile(array, 0, "foo", "foo", Void.class); + }); + + // WeakCompareAndSetAcquire // Incorrect argument types checkNPE(() -> { // null receiver diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/X-VarHandleTestAccess.java.template --- a/jdk/test/java/lang/invoke/VarHandles/X-VarHandleTestAccess.java.template Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/X-VarHandleTestAccess.java.template Tue May 03 12:25:20 2016 -0700 @@ -105,6 +105,7 @@ assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -114,6 +115,7 @@ assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -297,6 +299,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(recv, $value1$, $value2$); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(recv, $value1$, $value2$); }); @@ -383,6 +389,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile($value1$, $value2$); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire($value1$, $value2$); }); @@ -514,12 +524,19 @@ assertEquals(x, $value2$, "weakCompareAndSetRelease $type$"); } + { + boolean r = vh.weakCompareAndSetVolatile(recv, $value2$, $value1$); + assertEquals(r, true, "weakCompareAndSetVolatile $type$"); + $type$ x = ($type$) vh.get(recv); + assertEquals(x, $value1$, "weakCompareAndSetVolatile $type$ value"); + } + // Compare set and get { - $type$ o = ($type$) vh.getAndSet(recv, $value1$); - assertEquals(o, $value2$, "getAndSet $type$"); + $type$ o = ($type$) vh.getAndSet(recv, $value2$); + assertEquals(o, $value1$, "getAndSet $type$"); $type$ x = ($type$) vh.get(recv); - assertEquals(x, $value1$, "getAndSet $type$ value"); + assertEquals(x, $value2$, "getAndSet $type$ value"); } #end[CAS] @@ -559,6 +576,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(recv, $value1$, $value2$); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(recv, $value1$, $value2$); }); @@ -684,18 +705,25 @@ } { - boolean r = (boolean) vh.weakCompareAndSetRelease( $value1$, $value2$); + boolean r = (boolean) vh.weakCompareAndSetRelease($value1$, $value2$); assertEquals(r, true, "weakCompareAndSetRelease $type$"); $type$ x = ($type$) vh.get(); assertEquals(x, $value2$, "weakCompareAndSetRelease $type$"); } + { + boolean r = (boolean) vh.weakCompareAndSetVolatile($value2$, $value1$); + assertEquals(r, true, "weakCompareAndSetVolatile $type$"); + $type$ x = ($type$) vh.get(); + assertEquals(x, $value1$, "weakCompareAndSetVolatile $type$ value"); + } + // Compare set and get { - $type$ o = ($type$) vh.getAndSet( $value1$); - assertEquals(o, $value2$, "getAndSet $type$"); + $type$ o = ($type$) vh.getAndSet( $value2$); + assertEquals(o, $value1$, "getAndSet $type$"); $type$ x = ($type$) vh.get(); - assertEquals(x, $value1$, "getAndSet $type$ value"); + assertEquals(x, $value2$, "getAndSet $type$ value"); } #end[CAS] @@ -735,6 +763,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile($value1$, $value2$); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire($value1$, $value2$); }); @@ -869,12 +901,19 @@ assertEquals(x, $value2$, "weakCompareAndSetRelease $type$"); } + { + boolean r = vh.weakCompareAndSetVolatile(array, i, $value2$, $value1$); + assertEquals(r, true, "weakCompareAndSetVolatile $type$"); + $type$ x = ($type$) vh.get(array, i); + assertEquals(x, $value1$, "weakCompareAndSetVolatile $type$ value"); + } + // Compare set and get { - $type$ o = ($type$) vh.getAndSet(array, i, $value1$); - assertEquals(o, $value2$, "getAndSet $type$"); + $type$ o = ($type$) vh.getAndSet(array, i, $value2$); + assertEquals(o, $value1$, "getAndSet $type$"); $type$ x = ($type$) vh.get(array, i); - assertEquals(x, $value1$, "getAndSet $type$ value"); + assertEquals(x, $value2$, "getAndSet $type$ value"); } #end[CAS] @@ -918,6 +957,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, i, $value1$, $value2$); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, i, $value1$, $value2$); }); @@ -997,6 +1040,10 @@ }); checkIOOBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, $value1$, $value2$); + }); + + checkIOOBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, $value1$, $value2$); }); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/X-VarHandleTestByteArrayView.java.template --- a/jdk/test/java/lang/invoke/VarHandles/X-VarHandleTestByteArrayView.java.template Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/X-VarHandleTestByteArrayView.java.template Tue May 03 12:25:20 2016 -0700 @@ -94,6 +94,7 @@ assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -103,6 +104,7 @@ assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET)); + assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_VOLATILE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE)); assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET)); @@ -221,6 +223,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -291,6 +297,10 @@ }); checkROBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkROBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -322,6 +332,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -375,6 +389,10 @@ }); checkUOE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkUOE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -461,6 +479,10 @@ }); checkIOOBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkIOOBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -553,6 +575,10 @@ }); checkIOOBE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkIOOBE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -636,6 +662,10 @@ }); checkISE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkISE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -722,6 +752,10 @@ }); checkISE(() -> { + boolean r = vh.weakCompareAndSetVolatile(array, ci, VALUE_1, VALUE_2); + }); + + checkISE(() -> { boolean r = vh.weakCompareAndSetAcquire(array, ci, VALUE_1, VALUE_2); }); @@ -870,12 +904,19 @@ assertEquals(x, VALUE_2, "weakCompareAndSetRelease $type$"); } + { + boolean r = vh.weakCompareAndSetVolatile(array, i, VALUE_2, VALUE_1); + assertEquals(r, true, "weakCompareAndSetVolatile $type$"); + $type$ x = ($type$) vh.get(array, i); + assertEquals(x, VALUE_1, "weakCompareAndSetVolatile $type$ value"); + } + // Compare set and get { - $type$ o = ($type$) vh.getAndSet(array, i, VALUE_1); - assertEquals(o, VALUE_2, "getAndSet $type$"); + $type$ o = ($type$) vh.getAndSet(array, i, VALUE_2); + assertEquals(o, VALUE_1, "getAndSet $type$"); $type$ x = ($type$) vh.get(array, i); - assertEquals(x, VALUE_1, "getAndSet $type$ value"); + assertEquals(x, VALUE_2, "getAndSet $type$ value"); } #end[CAS] @@ -1016,12 +1057,19 @@ assertEquals(x, VALUE_2, "weakCompareAndSetRelease $type$"); } + { + boolean r = vh.weakCompareAndSetVolatile(array, i, VALUE_2, VALUE_1); + assertEquals(r, true, "weakCompareAndSetVolatile $type$"); + $type$ x = ($type$) vh.get(array, i); + assertEquals(x, VALUE_1, "weakCompareAndSetVolatile $type$ value"); + } + // Compare set and get { - $type$ o = ($type$) vh.getAndSet(array, i, VALUE_1); - assertEquals(o, VALUE_2, "getAndSet $type$"); + $type$ o = ($type$) vh.getAndSet(array, i, VALUE_2); + assertEquals(o, VALUE_1, "getAndSet $type$"); $type$ x = ($type$) vh.get(array, i); - assertEquals(x, VALUE_1, "getAndSet $type$ value"); + assertEquals(x, VALUE_2, "getAndSet $type$ value"); } #end[CAS] diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/X-VarHandleTestMethodHandleAccess.java.template --- a/jdk/test/java/lang/invoke/VarHandles/X-VarHandleTestMethodHandleAccess.java.template Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/X-VarHandleTestMethodHandleAccess.java.template Tue May 03 12:25:20 2016 -0700 @@ -229,12 +229,19 @@ assertEquals(x, $value2$, "weakCompareAndSetRelease $type$"); } + { + boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_VOLATILE).invokeExact(recv, $value2$, $value1$); + assertEquals(r, true, "weakCompareAndSetVolatile $type$"); + $type$ x = ($type$) hs.get(TestAccessMode.GET).invokeExact(recv); + assertEquals(x, $value1$, "weakCompareAndSetVolatile $type$ value"); + } + // Compare set and get { - $type$ o = ($type$) hs.get(TestAccessMode.GET_AND_SET).invokeExact(recv, $value1$); - assertEquals(o, $value2$, "getAndSet $type$"); + $type$ o = ($type$) hs.get(TestAccessMode.GET_AND_SET).invokeExact(recv, $value2$); + assertEquals(o, $value1$, "getAndSet $type$"); $type$ x = ($type$) hs.get(TestAccessMode.GET).invokeExact(recv); - assertEquals(x, $value1$, "getAndSet $type$ value"); + assertEquals(x, $value2$, "getAndSet $type$ value"); } #end[CAS] @@ -387,18 +394,25 @@ } { - boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_RELEASE).invokeExact( $value1$, $value2$); + boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_RELEASE).invokeExact($value1$, $value2$); assertEquals(r, true, "weakCompareAndSetRelease $type$"); $type$ x = ($type$) hs.get(TestAccessMode.GET).invokeExact(); assertEquals(x, $value2$, "weakCompareAndSetRelease $type$"); } + { + boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_VOLATILE).invokeExact($value2$, $value1$); + assertEquals(r, true, "weakCompareAndSetVolatile $type$"); + $type$ x = ($type$) hs.get(TestAccessMode.GET).invokeExact(); + assertEquals(x, $value1$, "weakCompareAndSetVolatile $type$ value"); + } + // Compare set and get { - $type$ o = ($type$) hs.get(TestAccessMode.GET_AND_SET).invokeExact( $value1$); - assertEquals(o, $value2$, "getAndSet $type$"); + $type$ o = ($type$) hs.get(TestAccessMode.GET_AND_SET).invokeExact($value2$); + assertEquals(o, $value1$, "getAndSet $type$"); $type$ x = ($type$) hs.get(TestAccessMode.GET).invokeExact(); - assertEquals(x, $value1$, "getAndSet $type$ value"); + assertEquals(x, $value2$, "getAndSet $type$ value"); } #end[CAS] @@ -560,12 +574,19 @@ assertEquals(x, $value2$, "weakCompareAndSetRelease $type$"); } + { + boolean r = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_VOLATILE).invokeExact(array, i, $value2$, $value1$); + assertEquals(r, true, "weakCompareAndSetVolatile $type$"); + $type$ x = ($type$) hs.get(TestAccessMode.GET).invokeExact(array, i); + assertEquals(x, $value1$, "weakCompareAndSetVolatile $type$ value"); + } + // Compare set and get { - $type$ o = ($type$) hs.get(TestAccessMode.GET_AND_SET).invokeExact(array, i, $value1$); - assertEquals(o, $value2$, "getAndSet $type$"); + $type$ o = ($type$) hs.get(TestAccessMode.GET_AND_SET).invokeExact(array, i, $value2$); + assertEquals(o, $value1$, "getAndSet $type$"); $type$ x = ($type$) hs.get(TestAccessMode.GET).invokeExact(array, i); - assertEquals(x, $value1$, "getAndSet $type$ value"); + assertEquals(x, $value2$, "getAndSet $type$ value"); } #end[CAS] diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarHandles/X-VarHandleTestMethodType.java.template --- a/jdk/test/java/lang/invoke/VarHandles/X-VarHandleTestMethodType.java.template Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarHandles/X-VarHandleTestMethodType.java.template Tue May 03 12:25:20 2016 -0700 @@ -375,6 +375,32 @@ }); + // WeakCompareAndSetVolatile + // Incorrect argument types + checkNPE(() -> { // null receiver + boolean r = vh.weakCompareAndSetVolatile(null, $value1$, $value1$); + }); + checkCCE(() -> { // receiver reference class + boolean r = vh.weakCompareAndSetVolatile(Void.class, $value1$, $value1$); + }); + check{#if[String]?CCE:WMTE}(() -> { // expected reference class + boolean r = vh.weakCompareAndSetVolatile(recv, Void.class, $value1$); + }); + check{#if[String]?CCE:WMTE}(() -> { // actual reference class + boolean r = vh.weakCompareAndSetVolatile(recv, $value1$, Void.class); + }); + checkWMTE(() -> { // receiver primitive class + boolean r = vh.weakCompareAndSetVolatile(0, $value1$, $value1$); + }); + // Incorrect arity + checkWMTE(() -> { // 0 + boolean r = vh.weakCompareAndSetVolatile(); + }); + checkWMTE(() -> { // > + boolean r = vh.weakCompareAndSetVolatile(recv, $value1$, $value1$, Void.class); + }); + + // WeakCompareAndSetAcquire // Incorrect argument types checkNPE(() -> { // null receiver @@ -981,6 +1007,23 @@ }); + // WeakCompareAndSetVolatile + // Incorrect argument types + check{#if[String]?CCE:WMTE}(() -> { // expected reference class + boolean r = vh.weakCompareAndSetVolatile(Void.class, $value1$); + }); + check{#if[String]?CCE:WMTE}(() -> { // actual reference class + boolean r = vh.weakCompareAndSetVolatile($value1$, Void.class); + }); + // Incorrect arity + checkWMTE(() -> { // 0 + boolean r = vh.weakCompareAndSetVolatile(); + }); + checkWMTE(() -> { // > + boolean r = vh.weakCompareAndSetVolatile($value1$, $value1$, Void.class); + }); + + // WeakCompareAndSetAcquire // Incorrect argument types check{#if[String]?CCE:WMTE}(() -> { // expected reference class @@ -1583,6 +1626,35 @@ }); + // WeakCompareAndSetVolatile + // Incorrect argument types + checkNPE(() -> { // null receiver + boolean r = vh.weakCompareAndSetVolatile(null, 0, $value1$, $value1$); + }); + checkCCE(() -> { // receiver reference class + boolean r = vh.weakCompareAndSetVolatile(Void.class, 0, $value1$, $value1$); + }); + check{#if[String]?CCE:WMTE}(() -> { // expected reference class + boolean r = vh.weakCompareAndSetVolatile(array, 0, Void.class, $value1$); + }); + check{#if[String]?CCE:WMTE}(() -> { // actual reference class + boolean r = vh.weakCompareAndSetVolatile(array, 0, $value1$, Void.class); + }); + checkWMTE(() -> { // receiver primitive class + boolean r = vh.weakCompareAndSetVolatile(0, 0, $value1$, $value1$); + }); + checkWMTE(() -> { // index reference class + boolean r = vh.weakCompareAndSetVolatile(array, Void.class, $value1$, $value1$); + }); + // Incorrect arity + checkWMTE(() -> { // 0 + boolean r = vh.weakCompareAndSetVolatile(); + }); + checkWMTE(() -> { // > + boolean r = vh.weakCompareAndSetVolatile(array, 0, $value1$, $value1$, Void.class); + }); + + // WeakCompareAndSetAcquire // Incorrect argument types checkNPE(() -> { // null receiver diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/invoke/VarargsArrayTest.java --- a/jdk/test/java/lang/invoke/VarargsArrayTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/invoke/VarargsArrayTest.java Tue May 03 12:25:20 2016 -0700 @@ -37,7 +37,7 @@ * @library /lib/testlibrary /lib/testlibrary/jsr292 * @compile/module=java.base java/lang/invoke/MethodHandleHelper.java * @run main/bootclasspath VarargsArrayTest - * @run main/bootclasspath -DVarargsArrayTest.MAX_ARITY=255 -DVarargsArrayTest.START_ARITY=250 + * @run main/bootclasspath/othervm -DVarargsArrayTest.MAX_ARITY=255 -DVarargsArrayTest.START_ARITY=250 * VarargsArrayTest */ diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/module/ModuleFinderTest.java --- a/jdk/test/java/lang/module/ModuleFinderTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/lang/module/ModuleFinderTest.java Tue May 03 12:25:20 2016 -0700 @@ -340,7 +340,7 @@ */ public void testOfWithUnrecognizedEntry() throws Exception { Path dir = Files.createTempDirectory(USER_DIR, "mods"); - Path mod = Files.createTempFile(dir, "m", "mod"); + Path mod = Files.createTempFile(dir, "m", ".junk"); ModuleFinder finder = ModuleFinder.of(mod); try { @@ -361,6 +361,48 @@ /** + * Test ModuleFinder.of with a file path to a directory containing a file + * that will not be recognized as a module. + */ + public void testOfWithUnrecognizedEntryInDirectory() throws Exception { + Path dir = Files.createTempDirectory(USER_DIR, "mods"); + Files.createTempFile(dir, "m", ".junk"); + + ModuleFinder finder = ModuleFinder.of(dir); + try { + finder.find("java.rhubarb"); + assertTrue(false); + } catch (FindException e) { + // expected + } + + finder = ModuleFinder.of(dir); + try { + finder.findAll(); + assertTrue(false); + } catch (FindException e) { + // expected + } + } + + + /** + * Test ModuleFinder.of with a file path to a directory containing a file + * starting with ".", the file should be ignored. + */ + public void testOfWithHiddenEntryInDirectory() throws Exception { + Path dir = Files.createTempDirectory(USER_DIR, "mods"); + Files.createTempFile(dir, ".marker", ""); + + ModuleFinder finder = ModuleFinder.of(dir); + assertFalse(finder.find("java.rhubarb").isPresent()); + + finder = ModuleFinder.of(dir); + assertTrue(finder.findAll().isEmpty()); + } + + + /** * Test ModuleFinder.of with a directory that contains two * versions of the same module */ diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/reflect/WeakPairMap/Driver.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/reflect/WeakPairMap/Driver.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,35 @@ +/* + * 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. + * + * 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 8888888 + * @summary Functional test for WeakPairMap + * @build java.base/java.lang.reflect.WeakPairMapTest + * @run main Driver + */ +public class Driver { + public static void main(String[] args) { + java.lang.reflect.WeakPairMapTest.main(args); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/lang/reflect/WeakPairMap/java.base/java/lang/reflect/WeakPairMapTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/reflect/WeakPairMap/java.base/java/lang/reflect/WeakPairMapTest.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,175 @@ +/* + * 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.reflect; + +import java.lang.ref.Reference; +import java.util.Objects; + +/** + * Functional test for WeakPairMap + * + * @author Peter Levart + */ +public class WeakPairMapTest { + public static void main(String[] args) { + WeakPairMap pm = new WeakPairMap<>(); + Object key1 = new Object(); + Object key2 = new Object(); + + // check for emptiness + assertEquals(pm.containsKeyPair(key1, key2), false); + assertEquals(pm.get(key1, key2), null); + + // check for NPE(s) + for (Object k1 : new Object[]{null, key1}) { + for (Object k2 : new Object[]{null, key2}) { + for (String v : new String[]{null, "abc"}) { + + if (k1 != null && k2 != null && v != null) { + // skip non-null args + continue; + } + + try { + pm.put(k1, k2, v); + throw new AssertionError("Unexpected code path, k1=" + + k1 + ", k2=" + k2 + ", v=" + v); + } catch (NullPointerException e) { + // expected + } + + try { + pm.putIfAbsent(k1, k2, v); + throw new AssertionError("Unexpected code path, k1=" + + k1 + ", k2=" + k2 + ", v=" + v); + } catch (NullPointerException e) { + // expected + } + + if (k1 != null && k2 != null) { + // skip non-null args + continue; + } + + try { + pm.computeIfAbsent(k1, k2, (_k1, _k2) -> v); + throw new AssertionError("Unexpected code path, k1=" + + k1 + ", k2=" + k2 + ", v=" + v); + } catch (NullPointerException e) { + // expected + } + + try { + pm.containsKeyPair(k1, k2); + throw new AssertionError("Unexpected code path, k1=" + + k1 + ", k2=" + k2); + } catch (NullPointerException e) { + // expected + } + + try { + pm.get(k1, k2); + throw new AssertionError("Unexpected code path, k1=" + + k1 + ", k2=" + k2); + } catch (NullPointerException e) { + // expected + } + } + } + } + + // how much to wait when it is expected for entry to be retained + final long retentionTimeout = 500L; + // how much to wait when it is expected for entry to be removed + final long cleanupTimeout = 30_000L; + + // check insertion + assertEquals(pm.putIfAbsent(key1, key2, "abc"), null); + assertEquals(pm.get(key1, key2), "abc"); + + // check retention while both keys are still reachable + assertEquals(gcAndWaitRemoved(pm, "abc", retentionTimeout), false); + assertEquals(pm.get(key1, key2), "abc"); + + // check cleanup when both keys are unreachable + key1 = null; + key2 = null; + assertEquals(gcAndWaitRemoved(pm, "abc", cleanupTimeout), true); + + // new insertion + key1 = new Object(); + key2 = new Object(); + assertEquals(pm.putIfAbsent(key1, key2, "abc"), null); + assertEquals(pm.get(key1, key2), "abc"); + + // check retention while both keys are still reachable + assertEquals(gcAndWaitRemoved(pm, "abc", retentionTimeout), false); + assertEquals(pm.get(key1, key2), "abc"); + + // check cleanup when 1st key is unreachable + key1 = null; + assertEquals(gcAndWaitRemoved(pm, "abc", cleanupTimeout), true); + Reference.reachabilityFence(key2); + + // new insertion + key1 = new Object(); + key2 = new Object(); + assertEquals(pm.putIfAbsent(key1, key2, "abc"), null); + assertEquals(pm.get(key1, key2), "abc"); + + // check retention while both keys are still reachable + assertEquals(gcAndWaitRemoved(pm, "abc", retentionTimeout), false); + assertEquals(pm.get(key1, key2), "abc"); + + // check cleanup when 2nd key is unreachable + key2 = null; + assertEquals(gcAndWaitRemoved(pm, "abc", cleanupTimeout), true); + Reference.reachabilityFence(key1); + } + + /** + * Trigger GC and wait for at most {@code millis} ms for given value to + * be removed from given WeakPairMap. + * + * @return true if element has been removed or false if not + */ + static boolean gcAndWaitRemoved(WeakPairMap pm, V value, + long millis) { + System.gc(); + for (int i = 0; i < (millis + 99) / 100 && pm.values().contains(value); i++) { + try { + Thread.sleep(100L); + } catch (InterruptedException e) { + throw new AssertionError("Interrupted"); + } + } + return !pm.values().contains(value); + } + + static void assertEquals(Object actual, Object expected) { + if (!Objects.equals(actual, expected)) { + throw new AssertionError("Expected: " + expected + ", actual: " + actual); + } + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/APIErrors.java --- a/jdk/test/java/net/httpclient/APIErrors.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/net/httpclient/APIErrors.java Tue May 03 12:25:20 2016 -0700 @@ -26,6 +26,7 @@ * @bug 8087112 * @library /lib/testlibrary/ * @build jdk.testlibrary.SimpleSSLContext ProxyServer + * @build TestKit * @compile ../../../com/sun/net/httpserver/LogFilter.java * @compile ../../../com/sun/net/httpserver/FileServerHandler.java * @run main/othervm APIErrors @@ -73,26 +74,6 @@ } } - static void reject(Runnable r, Class extype) { - try { - r.run(); - throw new RuntimeException("Expected: " + extype); - } catch (Throwable t) { - if (!extype.isAssignableFrom(t.getClass())) { - throw new RuntimeException("Wrong exception type: " + extype + " / " - +t.getClass()); - } - } - } - - static void accept(Runnable r) { - try { - r.run(); - } catch (Throwable t) { - throw new RuntimeException("Unexpected exception: " + t); - } - } - static void checkNonNull(Supplier r) { if (r.get() == null) throw new RuntimeException("Unexpected null return:"); @@ -108,12 +89,14 @@ System.out.println("Test 1"); HttpClient.Builder cb = HttpClient.create(); InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 5000); - reject(() -> { cb.priority(-1);}, IllegalArgumentException.class); - reject(() -> { cb.priority(500);}, IllegalArgumentException.class); - accept(() -> { cb.priority(1);}); - accept(() -> { cb.priority(255);}); - - accept(() -> {clients.add(cb.build()); clients.add(cb.build());}); + TestKit.assertThrows(IllegalArgumentException.class, () -> cb.priority(-1)); + TestKit.assertThrows(IllegalArgumentException.class, () -> cb.priority(500)); + TestKit.assertNotThrows(() -> cb.priority(1)); + TestKit.assertNotThrows(() -> cb.priority(255)); + TestKit.assertNotThrows(() -> { + clients.add(cb.build()); + clients.add(cb.build()); + }); } static void test2() throws Exception { @@ -139,7 +122,7 @@ static void test3() throws Exception { System.out.println("Test 3"); - reject(()-> { + TestKit.assertThrows(IllegalStateException.class, ()-> { try { HttpRequest r1 = request(); HttpResponse resp = r1.response(); @@ -147,9 +130,9 @@ } catch (IOException |InterruptedException e) { throw new RuntimeException(e); } - }, IllegalStateException.class); + }); - reject(()-> { + TestKit.assertThrows(IllegalStateException.class, ()-> { try { HttpRequest r1 = request(); HttpResponse resp = r1.response(); @@ -157,8 +140,8 @@ } catch (IOException |InterruptedException | ExecutionException e) { throw new RuntimeException(e); } - }, IllegalStateException.class); - reject(()-> { + }); + TestKit.assertThrows(IllegalStateException.class, ()-> { try { HttpRequest r1 = request(); HttpResponse resp1 = r1.responseAsync().get(); @@ -166,7 +149,7 @@ } catch (IOException |InterruptedException | ExecutionException e) { throw new RuntimeException(e); } - }, IllegalStateException.class); + }); } static class Auth extends java.net.Authenticator { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/EchoHandler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/EchoHandler.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2015, 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. + * + * 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 com.sun.net.httpserver.*; +import java.net.*; +import java.net.http.*; +import java.io.*; +import java.util.concurrent.*; +import javax.net.ssl.*; +import java.nio.file.*; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import jdk.testlibrary.SimpleSSLContext; +import static java.net.http.HttpRequest.*; +import static java.net.http.HttpResponse.*; +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class EchoHandler implements HttpHandler { + public EchoHandler() {} + + @Override + public void handle(HttpExchange t) + throws IOException { + try { + System.err.println("EchoHandler received request to " + t.getRequestURI()); + InputStream is = t.getRequestBody(); + Headers map = t.getRequestHeaders(); + Headers map1 = t.getResponseHeaders(); + map1.add("X-Hello", "world"); + map1.add("X-Bye", "universe"); + String fixedrequest = map.getFirst("XFixed"); + File outfile = File.createTempFile("foo", "bar"); + FileOutputStream fos = new FileOutputStream(outfile); + int count = (int) is.transferTo(fos); + is.close(); + fos.close(); + InputStream is1 = new FileInputStream(outfile); + OutputStream os = null; + // return the number of bytes received (no echo) + String summary = map.getFirst("XSummary"); + if (fixedrequest != null && summary == null) { + t.sendResponseHeaders(200, count); + os = t.getResponseBody(); + is1.transferTo(os); + } else { + t.sendResponseHeaders(200, 0); + os = t.getResponseBody(); + is1.transferTo(os); + + if (summary != null) { + String s = Integer.toString(count); + os.write(s.getBytes()); + } + } + outfile.delete(); + os.close(); + is1.close(); + } catch (Throwable e) { + e.printStackTrace(); + throw new IOException(e); + } + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/LightWeightHttpServer.java --- a/jdk/test/java/net/httpclient/LightWeightHttpServer.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/net/httpclient/LightWeightHttpServer.java Tue May 03 12:25:20 2016 -0700 @@ -22,10 +22,10 @@ */ /** - * @library /lib/testlibrary/ - * @build jdk.testlibrary.SimpleSSLContext ProxyServer - * @compile ../../../com/sun/net/httpserver/LogFilter.java - * @compile ../../../com/sun/net/httpserver/FileServerHandler.java + * library /lib/testlibrary/ / + * build jdk.testlibrary.SimpleSSLContext ProxyServer EchoHandler + * compile ../../../com/sun/net/httpserver/LogFilter.java + * compile ../../../com/sun/net/httpserver/FileServerHandler.java */ import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpContext; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/ManyRequests.java --- a/jdk/test/java/net/httpclient/ManyRequests.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/net/httpclient/ManyRequests.java Tue May 03 12:25:20 2016 -0700 @@ -24,18 +24,17 @@ /** * @test * @bug 8087112 - * @library /lib/testlibrary/ - * @build jdk.testlibrary.SimpleSSLContext + * @library /lib/testlibrary/ / + * @build jdk.testlibrary.SimpleSSLContext EchoHandler * @compile ../../../com/sun/net/httpserver/LogFilter.java * @compile ../../../com/sun/net/httpserver/FileServerHandler.java - * @run main/othervm ManyRequests + * @run main/othervm/timeout=40 -Djava.net.http.HttpClient.log=ssl ManyRequests * @summary Send a large number of requests asynchronously */ //package javaapplication16; -import com.sun.net.httpserver.HttpsConfigurator; -import com.sun.net.httpserver.HttpsServer; +import com.sun.net.httpserver.*; import java.io.IOException; import java.io.UncheckedIOException; import java.net.http.HttpClient; @@ -47,18 +46,25 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.Random; +import java.util.logging.*; import java.util.concurrent.CompletableFuture; -import javax.net.ssl.SSLContext; +import javax.net.ssl.*; import jdk.testlibrary.SimpleSSLContext; public class ManyRequests { + volatile static int counter = 0; + public static void main(String[] args) throws Exception { + Logger logger = Logger.getLogger("com.sun.net.httpserver"); + logger.setLevel(Level.ALL); + logger.info("TEST"); + SSLContext ctx = new SimpleSSLContext().get(); InetSocketAddress addr = new InetSocketAddress(0); HttpsServer server = HttpsServer.create(addr, 0); - server.setHttpsConfigurator(new HttpsConfigurator(ctx)); + server.setHttpsConfigurator(new Configurator(ctx)); HttpClient client = HttpClient.create() .sslContext(ctx) @@ -72,7 +78,8 @@ } } - static final int REQUESTS = 1000; + //static final int REQUESTS = 1000; + static final int REQUESTS = 20; static void test(HttpsServer server, HttpClient client) throws Exception { int port = server.getAddress().getPort(); @@ -102,6 +109,9 @@ resp.bodyAsync(HttpResponse.ignoreBody()); String s = "Expected 200, got: " + resp.statusCode(); return completedWithIOException(s); + } else { + counter++; + System.out.println("Result from " + counter); } return resp.bodyAsync(HttpResponse.asByteArray()) .thenApply((b) -> new Pair<>(resp, b)); @@ -114,14 +124,18 @@ }); } + // wait for them all to complete and throw exception in case of error - CompletableFuture.allOf(results).join(); + //try { + CompletableFuture.allOf(results).join(); + //} catch (Exception e) { + //e.printStackTrace(); + //throw e; + //} } static CompletableFuture completedWithIOException(String message) { - CompletableFuture cf = new CompletableFuture<>(); - cf.completeExceptionally(new IOException(message)); - return cf; + return CompletableFuture.failedFuture(new IOException(message)); } static final class Pair { @@ -192,3 +206,14 @@ throw new RuntimeException(sb.toString()); } } + +class Configurator extends HttpsConfigurator { + public Configurator(SSLContext ctx) { + super(ctx); + } + + public void configure (HttpsParameters params) { + params.setSSLParameters (getSSLContext().getSupportedSSLParameters()); + } +} + diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/RequestBodyTest.java --- a/jdk/test/java/net/httpclient/RequestBodyTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/net/httpclient/RequestBodyTest.java Tue May 03 12:25:20 2016 -0700 @@ -23,10 +23,11 @@ /** * @test @bug 8087112 - * @library /lib/testlibrary/ - * @build jdk.testlibrary.SimpleSSLContext ProxyServer + * @library /lib/testlibrary/ / * @compile ../../../com/sun/net/httpserver/LogFilter.java * @compile ../../../com/sun/net/httpserver/FileServerHandler.java + * @build LightWeightHttpServer + * @build jdk.testlibrary.SimpleSSLContext ProxyServer * @run main/othervm RequestBodyTest */ diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/SmokeTest.java --- a/jdk/test/java/net/httpclient/SmokeTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/net/httpclient/SmokeTest.java Tue May 03 12:25:20 2016 -0700 @@ -24,15 +24,13 @@ /** * @test * @bug 8087112 - * @library /lib/testlibrary/ - * @build jdk.testlibrary.SimpleSSLContext ProxyServer + * @library /lib/testlibrary/ / + * @build jdk.testlibrary.SimpleSSLContext ProxyServer EchoHandler * @compile ../../../com/sun/net/httpserver/LogFilter.java * @compile ../../../com/sun/net/httpserver/FileServerHandler.java * @run main/othervm SmokeTest */ -//package javaapplication16; - import com.sun.net.httpserver.*; import java.net.*; import java.net.http.*; @@ -69,6 +67,7 @@ */ public class SmokeTest { static SSLContext ctx; + static SSLParameters sslparams; static HttpServer s1 ; static HttpsServer s2; static ExecutorService executor; @@ -107,6 +106,7 @@ client = HttpClient.create() .sslContext(ctx) + .sslParameters(sslparams) .followRedirects(HttpClient.Redirect.ALWAYS) .executorService(Executors.newCachedThreadPool()) .build(); @@ -285,6 +285,7 @@ HttpClient cl = HttpClient.create() .proxy(ProxySelector.of(proxyAddr)) .sslContext(ctx) + .sslParameters(sslparams) .build(); CompletableFuture fut = cl.request(uri) @@ -672,7 +673,8 @@ s1.setExecutor(executor); s2.setExecutor(executor); ctx = new SimpleSSLContext().get(); - s2.setHttpsConfigurator(new HttpsConfigurator(ctx)); + sslparams = ctx.getSupportedSSLParameters(); + s2.setHttpsConfigurator(new Configurator(ctx)); s1.start(); s2.start(); @@ -689,6 +691,16 @@ } } +class Configurator extends HttpsConfigurator { + public Configurator(SSLContext ctx) { + super(ctx); + } + + public void configure (HttpsParameters params) { + params.setSSLParameters (getSSLContext().getSupportedSSLParameters()); + } +} + class UploadServer extends Thread { int statusCode; ServerSocket ss; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/TestKit.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/TestKit.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,101 @@ +/* + * 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. + * + * 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.regex.Pattern; + +import static java.util.Objects.requireNonNull; + +// +// A set of testing utility functions +// +public final class TestKit { + + private TestKit() { } + + public static void assertNotThrows(ThrowingProcedure code) { + requireNonNull(code, "code"); + assertNotThrows(() -> { + code.run(); + return null; + }); + } + + public static V assertNotThrows(ThrowingFunction code) { + requireNonNull(code, "code"); + try { + return code.run(); + } catch (Throwable t) { + throw new RuntimeException("Expected to run normally, but threw " + + t.getClass().getCanonicalName(), t); + } + } + + public static T assertThrows(Class clazz, + ThrowingProcedure code) { + requireNonNull(clazz, "clazz"); + requireNonNull(code, "code"); + try { + code.run(); + } catch (Throwable t) { + if (clazz.isInstance(t)) { + return clazz.cast(t); + } + throw new RuntimeException("Expected to catch an exception of type " + + clazz.getCanonicalName() + ", but caught " + + t.getClass().getCanonicalName(), t); + + } + throw new RuntimeException("Expected to catch an exception of type " + + clazz.getCanonicalName() + ", but caught nothing"); + } + + public interface ThrowingProcedure { + void run() throws Throwable; + } + + public interface ThrowingFunction { + V run() throws Throwable; + } + + // The rationale behind asking for a regex is to not pollute variable names + // space in the scope of assertion: if it's something as simple as checking + // a message, we can do it inside + public static T assertThrows(Class clazz, + String messageRegex, + ThrowingProcedure code) { + requireNonNull(messageRegex, "messagePattern"); + T t = assertThrows(clazz, code); + String m = t.getMessage(); + if (m == null) { + throw new RuntimeException(String.format( + "Expected exception message to match the regex '%s', " + + "but the message was null", messageRegex), t); + } + if (!Pattern.matches(messageRegex, m)) { + throw new RuntimeException(String.format( + "Expected exception message to match the regex '%s', " + + "actual message: %s", messageRegex, m), t); + } + return t; + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/TestKitTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/TestKitTest.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,130 @@ +/* + * 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. + * + * 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.util.IllegalFormatException; +import java.util.Set; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +/* + * @test + * @compile TestKit.java + * @run testng TestKitTest + */ +public final class TestKitTest { + + public static void main(String[] args) { + testAssertNotThrows(); + testAssertThrows(); + } + + private static void testAssertNotThrows() { + Integer integer = TestKit.assertNotThrows( + () -> TestKit.assertNotThrows(() -> 1) + ); + assertEquals(integer, Integer.valueOf(1)); + + RuntimeException re = TestKit.assertThrows( + RuntimeException.class, + () -> TestKit.assertNotThrows(() -> { throw new IOException(); }) + ); + assertEquals(re.getMessage(), + "Expected to run normally, but threw " + + "java.io.IOException"); + + TestKit.assertNotThrows( + () -> TestKit.assertNotThrows(() -> { }) + ); + + re = TestKit.assertThrows( + RuntimeException.class, + () -> TestKit.assertNotThrows((TestKit.ThrowingProcedure) () -> { throw new IOException(); }) + ); + assertEquals(re.getMessage(), + "Expected to run normally, but threw " + + "java.io.IOException"); + } + + private static void testAssertThrows() { + NullPointerException npe = TestKit.assertThrows( + NullPointerException.class, + () -> TestKit.assertThrows(null, null) + ); + assertNotNull(npe); + assertTrue(Set.of("clazz", "code").contains(npe.getMessage()), npe.getMessage()); + + npe = TestKit.assertThrows( + NullPointerException.class, + () -> TestKit.assertThrows(IOException.class, null) + ); + assertNotNull(npe); + assertEquals(npe.getMessage(), "code"); + + npe = TestKit.assertThrows( + NullPointerException.class, + () -> TestKit.assertThrows(null, () -> { }) + ); + assertEquals(npe.getMessage(), "clazz"); + + npe = TestKit.assertThrows( + NullPointerException.class, + () -> { throw new NullPointerException(); } + ); + assertNotNull(npe); + assertNull(npe.getMessage()); + assertEquals(npe.getClass(), NullPointerException.class); + + RuntimeException re = TestKit.assertThrows( + RuntimeException.class, + () -> TestKit.assertThrows(NullPointerException.class, () -> { }) + ); + assertEquals(re.getClass(), RuntimeException.class); + assertEquals(re.getMessage(), + "Expected to catch an exception of type " + + "java.lang.NullPointerException, but caught nothing"); + + re = TestKit.assertThrows( + RuntimeException.class, + () -> { throw new NullPointerException(); } + ); + assertNotNull(re); + assertNull(re.getMessage()); + assertEquals(re.getClass(), NullPointerException.class); + + re = TestKit.assertThrows( + RuntimeException.class, + () -> TestKit.assertThrows( + IllegalFormatException.class, + () -> { throw new IndexOutOfBoundsException(); } + )); + assertNotNull(re); + assertEquals(re.getClass(), RuntimeException.class); + assertEquals(re.getMessage(), + "Expected to catch an exception of type java.util.IllegalFormatException" + + ", but caught java.lang.IndexOutOfBoundsException"); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/http2/BasicTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/http2/BasicTest.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2015, 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. + * + * 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 8087112 + * @library /lib/testlibrary + * @build jdk.testlibrary.SimpleSSLContext + * @modules java.httpclient + * @compile/module=java.httpclient java/net/http/BodyOutputStream.java + * @compile/module=java.httpclient java/net/http/BodyInputStream.java + * @compile/module=java.httpclient java/net/http/EchoHandler.java + * @compile/module=java.httpclient java/net/http/Http2Handler.java + * @compile/module=java.httpclient java/net/http/Http2TestExchange.java + * @compile/module=java.httpclient java/net/http/Http2TestServerConnection.java + * @compile/module=java.httpclient java/net/http/Http2TestServer.java + * @compile/module=java.httpclient java/net/http/OutgoingPushPromise.java + * @compile/module=java.httpclient java/net/http/TestUtil.java + * @run testng/othervm -Djava.net.http.HttpClient.log=ssl,requests,responses,errors BasicTest + */ + +import java.io.*; +import java.net.*; +import java.net.http.*; +import static java.net.http.HttpClient.Version.HTTP_2; +import javax.net.ssl.*; +import java.nio.file.*; +import java.util.concurrent.*; +import jdk.testlibrary.SimpleSSLContext; + + +import org.testng.annotations.Test; +import org.testng.annotations.Parameters; + +@Test +public class BasicTest { + static int httpPort, httpsPort; + static Http2TestServer httpServer, httpsServer; + static HttpClient client = null; + static ExecutorService exec; + static SSLContext sslContext; + + static String httpURIString, httpsURIString; + + static void initialize() throws Exception { + try { + SimpleSSLContext sslct = new SimpleSSLContext(); + sslContext = sslct.get(); + client = getClient(); + exec = client.executorService(); + httpServer = new Http2TestServer(false, 0, new EchoHandler(), + exec, sslContext); + httpPort = httpServer.getAddress().getPort(); + + httpsServer = new Http2TestServer(true, 0, new EchoHandler(), + exec, sslContext); + + httpsPort = httpsServer.getAddress().getPort(); + httpURIString = "http://127.0.0.1:" + Integer.toString(httpPort) + + "/foo/"; + httpsURIString = "https://127.0.0.1:" + Integer.toString(httpsPort) + + "/bar/"; + + httpServer.start(); + httpsServer.start(); + } catch (Throwable e) { + System.err.println("Throwing now"); + e.printStackTrace(); + throw e; + } + } + + @Test(timeOut=30000) + public static void test() throws Exception { + try { + initialize(); + simpleTest(false); + simpleTest(true); + streamTest(false); + streamTest(true); + Thread.sleep(1000 * 4); + } finally { + httpServer.stop(); + httpsServer.stop(); + exec.shutdownNow(); + } + } + + static HttpClient getClient() { + if (client == null) { + client = HttpClient.create() + .sslContext(sslContext) + .version(HTTP_2) + .build(); + } + return client; + } + + static URI getURI(boolean secure) { + if (secure) + return URI.create(httpsURIString); + else + return URI.create(httpURIString); + } + + static void checkStatus(int expected, int found) throws Exception { + if (expected != found) { + System.err.printf ("Test failed: wrong status code %d/%d\n", + expected, found); + throw new RuntimeException("Test failed"); + } + } + + static void checkStrings(String expected, String found) throws Exception { + if (!expected.equals(found)) { + System.err.printf ("Test failed: wrong string %s/%s\n", + expected, found); + throw new RuntimeException("Test failed"); + } + } + + static Void compareFiles(Path path1, Path path2) { + return java.net.http.TestUtil.compareFiles(path1, path2); + } + + static Path tempFile() { + return java.net.http.TestUtil.tempFile(); + } + + static final String SIMPLE_STRING = "Hello world Goodbye world"; + + static final int LOOPS = 13; + static final int FILESIZE = 64 * 1024; + + static void streamTest(boolean secure) throws Exception { + URI uri = getURI(secure); + System.err.printf("streamTest %b to %s\n" , secure, uri); + + HttpClient client = getClient(); + Path src = java.net.http.TestUtil.getAFile(FILESIZE * 4); + HttpRequest req = client.request(uri) + .body(HttpRequest.fromFile(src)) + .POST(); + + CompletableFuture response = req.responseAsync() + .thenCompose(resp -> { + if (resp.statusCode() != 200) + throw new RuntimeException(); + return resp.bodyAsync(HttpResponse.asInputStream()); + }); + InputStream is = response.join(); + File dest = File.createTempFile("foo","bar"); + dest.deleteOnExit(); + FileOutputStream os = new FileOutputStream(dest); + is.transferTo(os); + is.close(); + os.close(); + int count = 0; + compareFiles(src, dest.toPath()); + System.err.println("DONE"); + } + + + static void simpleTest(boolean secure) throws Exception { + URI uri = getURI(secure); + System.err.println("Request to " + uri); + + // Do a simple warmup request + + HttpClient client = getClient(); + HttpRequest req = client.request(uri) + .body(HttpRequest.fromString(SIMPLE_STRING)) + .POST(); + HttpResponse response = req.response(); + HttpHeaders h = response.headers(); + + checkStatus(200, response.statusCode()); + + String responseBody = response.body(HttpResponse.asString()); + checkStrings(SIMPLE_STRING, responseBody); + + checkStrings(h.firstValue("x-hello").get(), "world"); + checkStrings(h.firstValue("x-bye").get(), "universe"); + + // Do loops asynchronously + + CompletableFuture[] responses = new CompletableFuture[LOOPS]; + final Path source = java.net.http.TestUtil.getAFile(FILESIZE); + for (int i = 0; i < LOOPS; i++) { + responses[i] = client.request(uri) + .body(HttpRequest.fromFile(source)) + .version(HTTP_2) + .POST() + .responseAsync() + .thenCompose(r -> r.bodyAsync(HttpResponse.asFile(tempFile()))) + .thenApply(path -> compareFiles(path, source)); + Thread.sleep(100); + } + CompletableFuture.allOf(responses).join(); + System.err.println("DONE"); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/http2/ServerPush.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/http2/ServerPush.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2015, 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. + * + * 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 8087112 + * @library /lib/testlibrary + * @build jdk.testlibrary.SimpleSSLContext + * @modules java.httpclient + * @compile/module=java.httpclient java/net/http/BodyOutputStream.java + * @compile/module=java.httpclient java/net/http/BodyInputStream.java + * @compile/module=java.httpclient java/net/http/PushHandler.java + * @compile/module=java.httpclient java/net/http/Http2Handler.java + * @compile/module=java.httpclient java/net/http/Http2TestExchange.java + * @compile/module=java.httpclient java/net/http/Http2TestServerConnection.java + * @compile/module=java.httpclient java/net/http/Http2TestServer.java + * @compile/module=java.httpclient java/net/http/OutgoingPushPromise.java + * @compile/module=java.httpclient java/net/http/TestUtil.java + * @run testng/othervm -Djava.net.http.HttpClient.log=requests,responses ServerPush + */ + +import java.io.*; +import java.net.*; +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.net.http.*; +import java.util.*; +import java.util.concurrent.*; +import org.testng.annotations.Test; + +public class ServerPush { + + static ExecutorService e = Executors.newCachedThreadPool(); + + static final int LOOPS = 13; + static final int FILE_SIZE = 32 * 1024; + + static Path tempFile; + + @Test(timeOut=30000) + public static void test() throws Exception { + Http2TestServer server = null; + Path dir = null; + try { + server = new Http2TestServer(false, 0, + new PushHandler(FILE_SIZE, LOOPS)); + tempFile = TestUtil.getAFile(FILE_SIZE); + + System.err.println("Server listening on port " + server.getAddress().getPort()); + server.start(); + int port = server.getAddress().getPort(); + dir = Files.createTempDirectory("serverPush"); + + URI uri = new URI("http://127.0.0.1:" + Integer.toString(port) + "/foo"); + HttpRequest request = HttpRequest.create(uri) + .version(HttpClient.Version.HTTP_2) + .GET(); + + CompletableFuture> cf = + request.multiResponseAsync(HttpResponse.multiFile(dir)); + Map results = cf.get(); + + //HttpResponse resp = request.response(); + System.err.println(results.size()); + Set uris = results.keySet(); + for (URI u : uris) { + Path result = results.get(u); + System.err.printf("%s -> %s\n", u.toString(), result.toString()); + TestUtil.compareFiles(result, tempFile); + } + System.out.println("TEST OK"); + } finally { + e.shutdownNow(); + server.stop(); + Files.walkFileTree(dir, new SimpleFileVisitor() { + public FileVisitResult postVisitDirectory(Path dir, IOException exc) { + dir.toFile().delete(); + return FileVisitResult.CONTINUE; + } + public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { + path.toFile().delete(); + return FileVisitResult.CONTINUE; + } + }); + } + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/http2/TEST.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/http2/TEST.properties Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,1 @@ +bootclasspath.dirs = /java/net/httpclient diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/BodyInputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/BodyInputStream.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,107 @@ +package java.net.http; + +import java.io.*; +import java.nio.ByteBuffer; + +/** + * InputStream reads frames off stream q and supplies read demand from any + * DataFrames it finds. Window updates are sent back on the connections send + * q. + */ +class BodyInputStream extends InputStream { + + final Queue q; + final int streamid; + boolean closed; + boolean eof; + final Http2TestServerConnection conn; + + @SuppressWarnings({"rawtypes","unchecked"}) + BodyInputStream(Queue q, int streamid, Http2TestServerConnection conn) { + this.q = q; + this.streamid = streamid; + this.conn = conn; + } + + DataFrame df; + ByteBuffer[] buffers; + ByteBuffer buffer; + int nextIndex = -1; + + private DataFrame getData() throws IOException { + if (eof) { + return null; + } + Http2Frame frame; + do { + frame = q.take(); + if (frame.type() == ResetFrame.TYPE) { + conn.handleStreamReset((ResetFrame) frame); // throws IOException + } + // ignoring others for now Wupdates handled elsewhere + if (frame.type() != DataFrame.TYPE) { + System.out.println("Ignoring " + frame.toString() + " CHECK THIS"); + } + } while (frame.type() != DataFrame.TYPE); + df = (DataFrame) frame; + int len = df.getDataLength(); + eof = frame.getFlag(DataFrame.END_STREAM); + // acknowledge + conn.sendWindowUpdates(len, streamid); + return (DataFrame) frame; + } + + // null return means EOF + private ByteBuffer getBuffer() throws IOException { + if (buffer == null || !buffer.hasRemaining()) { + if (nextIndex == -1 || nextIndex == buffers.length) { + DataFrame df = getData(); + if (df == null) { + return null; + } + int len = df.getDataLength(); + if ((len == 0) && eof) { + return null; + } + buffers = df.getData(); + nextIndex = 0; + } + buffer = buffers[nextIndex++]; + } + return buffer; + } + + @Override + public int read(byte[] buf, int offset, int length) throws IOException { + if (closed) { + throw new IOException("closed"); + } + ByteBuffer b = getBuffer(); + if (b == null) { + return -1; + } + int remaining = b.remaining(); + if (remaining < length) { + length = remaining; + } + b.get(buf, offset, length); + return length; + } + + byte[] one = new byte[1]; + + @Override + public int read() throws IOException { + int c = read(one, 0, 1); + if (c == -1) { + return -1; + } + return one[0]; + } + + @Override + public void close() { + // TODO reset this stream + closed = true; + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/BodyOutputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/BodyOutputStream.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,106 @@ +package java.net.http; + +import java.io.*; +import java.nio.ByteBuffer; + +/** + * OutputStream. Incoming window updates handled by the main connection + * reader thread. + */ +@SuppressWarnings({"rawtypes","unchecked"}) +class BodyOutputStream extends OutputStream { + final static byte[] EMPTY_BARRAY = new byte[0]; + + final int streamid; + int window; + boolean closed; + boolean goodToGo = false; // not allowed to send until headers sent + final Http2TestServerConnection conn; + final Queue outputQ; + + BodyOutputStream(int streamid, int initialWindow, Http2TestServerConnection conn) { + this.window = initialWindow; + this.streamid = streamid; + this.conn = conn; + this.outputQ = conn.outputQ; + conn.registerStreamWindowUpdater(streamid, this::updateWindow); + } + + // called from connection reader thread as all incoming window + // updates are handled there. + synchronized void updateWindow(int update) { + window += update; + notifyAll(); + } + + void waitForWindow(int demand) throws InterruptedException { + // first wait for the connection window + conn.obtainConnectionWindow(demand); + // now wait for the stream window + synchronized (this) { + while (demand > 0) { + int n = Math.min(demand, window); + demand -= n; + window -= n; + if (demand > 0) { + wait(); + } + } + } + } + + void goodToGo() { + goodToGo = true; + } + + @Override + public void write(byte[] buf, int offset, int len) throws IOException { + if (closed) { + throw new IOException("closed"); + } + + if (!goodToGo) { + throw new IllegalStateException("sendResponseHeaders must be called first"); + } + try { + waitForWindow(len); + send(buf, offset, len, 0); + } catch (InterruptedException ex) { + throw new IOException(ex); + } + } + + private void send(byte[] buf, int offset, int len, int flags) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(len); + buffer.put(buf, offset, len); + buffer.flip(); + DataFrame df = new DataFrame(); + assert streamid != 0; + df.streamid(streamid); + df.setFlags(flags); + df.setData(buffer); + outputQ.put(df); + } + + byte[] one = new byte[1]; + + @Override + public void write(int b) throws IOException { + one[0] = (byte) b; + write(one, 0, 1); + } + + @Override + public void close() { + if (closed) { + return; + } + closed = true; + try { + send(EMPTY_BARRAY, 0, 0, DataFrame.END_STREAM); + } catch (IOException ex) { + System.err.println("TestServer: OutputStream.close exception: " + ex); + ex.printStackTrace(); + } + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/EchoHandler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/EchoHandler.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net.http; + +import java.util.*; +import java.util.concurrent.*; +import java.io.*; +import java.net.*; + +public class EchoHandler implements Http2Handler { + public EchoHandler() {} + + @Override + public void handle(Http2TestExchange t) + throws IOException { + try { + System.err.println("EchoHandler received request to " + t.getRequestURI()); + InputStream is = t.getRequestBody(); + HttpHeadersImpl map = t.getRequestHeaders(); + HttpHeadersImpl map1 = t.getResponseHeaders(); + map1.addHeader("X-Hello", "world"); + map1.addHeader("X-Bye", "universe"); + String fixedrequest = map.firstValue("XFixed").orElse(null); + File outfile = File.createTempFile("foo", "bar"); + FileOutputStream fos = new FileOutputStream(outfile); + int count = (int) is.transferTo(fos); + System.err.printf("EchoHandler read %d bytes\n", count); + is.close(); + fos.close(); + InputStream is1 = new FileInputStream(outfile); + OutputStream os = null; + // return the number of bytes received (no echo) + String summary = map.firstValue("XSummary").orElse(null); + if (fixedrequest != null && summary == null) { + t.sendResponseHeaders(200, count); + os = t.getResponseBody(); + is1.transferTo(os); + } else { + t.sendResponseHeaders(200, 0); + os = t.getResponseBody(); + int count1 = (int)is1.transferTo(os); + System.err.printf("EchoHandler wrote %d bytes\n", count1); + + if (summary != null) { + String s = Integer.toString(count); + os.write(s.getBytes()); + } + } + outfile.delete(); + os.close(); + is1.close(); + } catch (Throwable e) { + e.printStackTrace(); + throw new IOException(e); + } + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/Http2Handler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/Http2Handler.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,18 @@ +package java.net.http; + +import java.io.IOException; + +/** + * A handler which is invoked to process HTTP exchanges. Each + * HTTP exchange is handled by one of these handlers. + */ +public interface Http2Handler { + /** + * Handle the given request and generate an appropriate response. + * @param exchange the exchange containing the request from the + * client and used to send the response + * @throws NullPointerException if exchange is null + */ + public abstract void handle (Http2TestExchange exchange) throws IOException; +} + diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/Http2TestExchange.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/Http2TestExchange.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,126 @@ +package java.net.http; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.net.URI; +import java.net.InetSocketAddress; + +public class Http2TestExchange { + + final HttpHeadersImpl reqheaders; + final HttpHeadersImpl rspheaders; + final URI uri; + final String method; + final InputStream is; + final BodyOutputStream os; + final int streamid; + final boolean pushAllowed; + final Http2TestServerConnection conn; + final Http2TestServer server; + + int responseCode = -1; + long responseLength; + + Http2TestExchange(int streamid, String method, HttpHeadersImpl reqheaders, + HttpHeadersImpl rspheaders, URI uri, InputStream is, + BodyOutputStream os, Http2TestServerConnection conn, boolean pushAllowed) { + this.reqheaders = reqheaders; + this.rspheaders = rspheaders; + this.uri = uri; + this.method = method; + this.is = is; + this.streamid = streamid; + this.os = os; + this.pushAllowed = pushAllowed; + this.conn = conn; + this.server = conn.server; + } + + public HttpHeadersImpl getRequestHeaders() { + return reqheaders; + } + + public HttpHeadersImpl getResponseHeaders() { + return rspheaders; + } + + public URI getRequestURI() { + return uri; + } + + public String getRequestMethod() { + return method; + } + + public void close() { + try { + is.close(); + os.close(); + } catch (IOException e) { + System.err.println("TestServer: HttpExchange.close exception: " + e); + e.printStackTrace(); + } + } + + public InputStream getRequestBody() { + return is; + } + + public OutputStream getResponseBody() { + return os; + } + + public void sendResponseHeaders(int rCode, long responseLength) throws IOException { + this.responseLength = responseLength; + if (responseLength > 0 || responseLength < 0) { + long clen = responseLength > 0 ? responseLength : 0; + rspheaders.setHeader("Content-length", Long.toString(clen)); + } + + rspheaders.setHeader(":status", Integer.toString(rCode)); + + Http2TestServerConnection.ResponseHeaders response + = new Http2TestServerConnection.ResponseHeaders(rspheaders); + response.streamid(streamid); + response.setFlag(HeaderFrame.END_HEADERS); + conn.outputQ.put(response); + os.goodToGo(); + System.err.println("Sent response headers " + rCode); + } + + public InetSocketAddress getRemoteAddress() { + return (InetSocketAddress) conn.socket.getRemoteSocketAddress(); + } + + public int getResponseCode() { + return responseCode; + } + + public InetSocketAddress getLocalAddress() { + return server.getAddress(); + } + + public String getProtocol() { + return "HTTP/2"; + } + + public boolean serverPushAllowed() { + return pushAllowed; + } + + public void serverPush(URI uri, HttpHeadersImpl headers, InputStream content) { + OutgoingPushPromise pp = new OutgoingPushPromise( + streamid, uri, headers, content); + headers.setHeader(":method", "GET"); + headers.setHeader(":scheme", uri.getScheme()); + headers.setHeader(":authority", uri.getAuthority()); + headers.setHeader(":path", uri.getPath()); + try { + conn.outputQ.put(pp); + // writeLoop will spin up thread to read the InputStream + } catch (IOException ex) { + System.err.println("TestServer: pushPromise exception: " + ex); + } + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/Http2TestServer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/Http2TestServer.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2015, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net.http; + +import java.io.IOException; +import java.net.*; +import java.util.HashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import javax.net.ServerSocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; + +/** + * Waits for incoming TCP connections from a client and establishes + * a HTTP2 connection. Two threads are created per connection. One for reading + * and one for writing. Incoming requests are dispatched to the supplied + * Http2Handler on additional threads. All threads + * obtained from the supplied ExecutorService. + */ +public class Http2TestServer { + final ServerSocket server; + boolean secure; + SettingsFrame serverSettings, clientSettings; + final ExecutorService exec; + volatile boolean stopping = false; + final Http2Handler handler; + final SSLContext sslContext; + final HashMap connections; + + private static ThreadFactory defaultThreadFac = + (Runnable r) -> { + Thread t = new Thread(r); + t.setName("Test-server-pool"); + return t; + }; + + + private static ExecutorService getDefaultExecutor() { + return Executors.newCachedThreadPool(defaultThreadFac); + } + + public Http2TestServer(boolean secure, int port, Http2Handler handler) throws Exception { + this(secure, port, handler, getDefaultExecutor(), null); + } + + public InetSocketAddress getAddress() { + return (InetSocketAddress)server.getLocalSocketAddress(); + } + + /** + * Create a Http2Server listening on the given port. Currently needs + * to know in advance whether incoming connections are plain TCP "h2c" + * or TLS "h2"/ + * + * @param secure https or http + * @param port listen port + * @param handler the handler which receives incoming requests + * @param exec executor service (cached thread pool is used if null) + * @param context the SSLContext used when secure is true + * @throws Exception + */ + public Http2TestServer(boolean secure, int port, Http2Handler handler, + ExecutorService exec, SSLContext context) throws Exception { + if (secure) { + server = initSecure(port); + } else { + server = initPlaintext(port); + } + this.secure = secure; + this.exec = exec == null ? getDefaultExecutor() : exec; + this.handler = handler; + this.sslContext = context; + this.connections = new HashMap<>(); + } + + final ServerSocket initPlaintext(int port) throws Exception { + return new ServerSocket(port); + } + + public void stop() { + // TODO: clean shutdown GoAway + stopping = true; + for (Http2TestServerConnection connection : connections.values()) { + connection.close(); + } + try { + server.close(); + } catch (IOException e) {} + exec.shutdownNow(); + } + + + final ServerSocket initSecure(int port) throws Exception { + ServerSocketFactory fac; + if (sslContext != null) { + fac = sslContext.getServerSocketFactory(); + } else { + fac = SSLServerSocketFactory.getDefault(); + } + SSLServerSocket se = (SSLServerSocket) fac.createServerSocket(port); + SSLParameters sslp = se.getSSLParameters(); + sslp.setApplicationProtocols(new String[]{"h2"}); + se.setSSLParameters(sslp); + se.setEnabledCipherSuites(se.getSupportedCipherSuites()); + se.setEnabledProtocols(se.getSupportedProtocols()); + // other initialisation here + return se; + } + + /** + * Start thread which waits for incoming connections. + * + * @throws Exception + */ + public void start() { + exec.submit(() -> { + try { + while (!stopping) { + Socket socket = server.accept(); + InetSocketAddress addr = (InetSocketAddress) socket.getRemoteSocketAddress(); + Http2TestServerConnection c = new Http2TestServerConnection(this, socket); + connections.put(addr, c); + c.run(); + } + } catch (Throwable e) { + if (!stopping) { + System.err.println("TestServer: start exception: " + e); + e.printStackTrace(); + } + } + }); + } + +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/Http2TestServerConnection.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/Http2TestServerConnection.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,730 @@ +/* + * Copyright (c) 2015, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net.http; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.URI; +import java.net.URISyntaxException; +import static java.net.http.SettingsFrame.HEADER_TABLE_SIZE; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.function.Consumer; +import sun.net.httpclient.hpack.Decoder; +import sun.net.httpclient.hpack.DecodingCallback; +import sun.net.httpclient.hpack.Encoder; + +/** + * Represents one HTTP2 connection, either plaintext upgraded from HTTP/1.1 + * or HTTPS opened using "h2" ALPN. + */ +public class Http2TestServerConnection { + final Http2TestServer server; + @SuppressWarnings({"rawtypes","unchecked"}) + final Map streams; // input q per stream + final Queue outputQ; + int nextstream; + final Socket socket; + final InputStream is; + final OutputStream os; + Encoder hpackOut; + Decoder hpackIn; + SettingsFrame clientSettings, serverSettings; + final ExecutorService exec; + final boolean secure; + final Http2Handler handler; + volatile boolean stopping; + int nextPushStreamId = 2; + + final static ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0); + final static byte[] EMPTY_BARRAY = new byte[0]; + + final static byte[] clientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes(); + + Http2TestServerConnection(Http2TestServer server, Socket socket) throws IOException { + System.err.println("New connection from " + socket); + this.server = server; + this.streams = Collections.synchronizedMap(new HashMap<>()); + this.outputQ = new Queue<>(); + this.socket = socket; + this.clientSettings = server.clientSettings; + this.serverSettings = server.serverSettings; + this.exec = server.exec; + this.secure = server.secure; + this.handler = server.handler; + is = new BufferedInputStream(socket.getInputStream()); + os = new BufferedOutputStream(socket.getOutputStream()); + } + + void close() { + streams.forEach((i, q) -> { + q.close(); + }); + stopping = true; + try { + socket.close(); + // TODO: put a reset on each stream + } catch (IOException e) { + } + } + + private void readPreface() throws IOException { + int len = clientPreface.length; + byte[] bytes = new byte[len]; + is.readNBytes(bytes, 0, len); + if (Arrays.compare(clientPreface, bytes) != 0) { + throw new IOException("Invalid preface: " + new String(bytes, 0, len)); + } + } + + String doUpgrade() throws IOException { + String upgrade = readHttp1Request(); + String h2c = getHeader(upgrade, "Upgrade"); + if (h2c == null || !h2c.equals("h2c")) { + throw new IOException("Bad upgrade 1 " + h2c); + } + + sendHttp1Response(101, "Switching Protocols", "Connection", "Upgrade", + "Upgrade", "h2c"); + + sendSettingsFrame(); + readPreface(); + + String clientSettingsString = getHeader(upgrade, "HTTP2-Settings"); + clientSettings = getSettingsFromString(clientSettingsString); + + return upgrade; + } + + /** + * Client settings payload provided in base64 HTTP1 header. Decode it + * and add a header so we can interpret it. + * + * @param s + * @return + * @throws IOException + */ + private SettingsFrame getSettingsFromString(String s) throws IOException { + Base64.Decoder decoder = Base64.getUrlDecoder(); + byte[] payload = decoder.decode(s); + ByteBuffer bb1 = ByteBuffer.wrap(payload); + // simulate header of Settings Frame + ByteBuffer bb0 = ByteBuffer.wrap( + new byte[] {0, 0, (byte)payload.length, 4, 0, 0, 0, 0, 0}); + ByteBufferConsumer bbc = new ByteBufferConsumer( + new LinkedList(List.of(bb0, bb1)), + this::getBuffer); + Http2Frame frame = Http2Frame.readIncoming(bbc); + if (!(frame instanceof SettingsFrame)) + throw new IOException("Expected SettingsFrame"); + return (SettingsFrame)frame; + } + + void run() throws Exception { + String upgrade = null; + if (!secure) { + upgrade = doUpgrade(); + } else { + readPreface(); + sendSettingsFrame(true); + clientSettings = (SettingsFrame) readFrame(); + nextstream = 1; + } + + hpackOut = new Encoder(serverSettings.getParameter(HEADER_TABLE_SIZE)); + hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE)); + + exec.submit(() -> { + readLoop(); + }); + exec.submit(() -> { + writeLoop(); + }); + if (!secure) { + createPrimordialStream(upgrade); + nextstream = 3; + } + } + + static class BufferPool implements BufferHandler { + + public void setMinBufferSize(int size) { + } + + public ByteBuffer getBuffer(int size) { + if (size == -1) + size = 32 * 1024; + return ByteBuffer.allocate(size); + } + + public void returnBuffer(ByteBuffer buffer) { + } + } + + static BufferPool bufferpool = new BufferPool(); + + private void writeFrame(Http2Frame frame) throws IOException { + ByteBufferGenerator bg = new ByteBufferGenerator(bufferpool); + frame.computeLength(); + System.err.println("Writing frame " + frame.toString()); + frame.writeOutgoing(bg); + ByteBuffer[] bufs = bg.getBufferArray(); + int c = 0; + for (ByteBuffer buf : bufs) { + byte[] ba = buf.array(); + int start = buf.arrayOffset() + buf.position(); + c += buf.remaining(); + os.write(ba, start, buf.remaining()); + } + os.flush(); + System.err.printf("wrote %d bytes\n", c); + } + + void handleStreamReset(ResetFrame resetFrame) throws IOException { + // TODO: cleanup + throw new IOException("Stream reset"); + } + + private void handleCommonFrame(Http2Frame f) throws IOException { + if (f instanceof SettingsFrame) { + serverSettings = (SettingsFrame) f; + if (serverSettings.getFlag(SettingsFrame.ACK)) // ignore + { + return; + } + // otherwise acknowledge it + SettingsFrame frame = new SettingsFrame(); + frame.setFlag(SettingsFrame.ACK); + frame.streamid(0); + outputQ.put(frame); + return; + } + System.err.println("Received ---> " + f.toString()); + throw new UnsupportedOperationException("Not supported yet."); + } + + void sendWindowUpdates(int len, int streamid) throws IOException { + if (len == 0) + return; + WindowUpdateFrame wup = new WindowUpdateFrame(); + wup.streamid(streamid); + wup.setUpdate(len); + outputQ.put(wup); + wup = new WindowUpdateFrame(); + wup.streamid(0); + wup.setUpdate(len); + outputQ.put(wup); + } + + HttpHeadersImpl decodeHeaders(List frames) { + HttpHeadersImpl headers = new HttpHeadersImpl(); + + DecodingCallback cb = (name, value) -> { + headers.addHeader(name.toString(), value.toString()); + }; + + for (HeaderFrame frame : frames) { + ByteBuffer[] buffers = frame.getHeaderBlock(); + for (ByteBuffer buffer : buffers) { + hpackIn.decode(buffer, false, cb); + } + } + hpackIn.decode(EMPTY_BUFFER, true, cb); + return headers; + } + + String getRequestLine(String request) { + int eol = request.indexOf(CRLF); + return request.substring(0, eol); + } + + // First stream (1) comes from a plaintext HTTP/1.1 request + @SuppressWarnings({"rawtypes","unchecked"}) + void createPrimordialStream(String request) throws IOException { + HttpHeadersImpl headers = new HttpHeadersImpl(); + String requestLine = getRequestLine(request); + String[] tokens = requestLine.split(" "); + if (!tokens[2].equals("HTTP/1.1")) { + throw new IOException("bad request line"); + } + URI uri = null; + try { + uri = new URI(tokens[1]); + } catch (URISyntaxException e) { + throw new IOException(e); + } + String host = getHeader(request, "Host"); + if (host == null) { + throw new IOException("missing Host"); + } + + headers.setHeader(":method", tokens[0]); + headers.setHeader(":scheme", "http"); // always in this case + headers.setHeader(":authority", host); + headers.setHeader(":path", uri.getPath()); + Queue q = new Queue(); + String body = getRequestBody(request); + headers.setHeader("Content-length", Integer.toString(body.length())); + + addRequestBodyToQueue(body, q); + streams.put(1, q); + exec.submit(() -> { + handleRequest(headers, q, 1); + }); + } + + // all other streams created here + @SuppressWarnings({"rawtypes","unchecked"}) + void createStream(HeaderFrame frame) throws IOException { + List frames = new LinkedList<>(); + frames.add(frame); + int streamid = frame.streamid(); + if (streamid != nextstream) { + throw new IOException("unexpected stream id"); + } + nextstream += 2; + + while (!frame.getFlag(HeaderFrame.END_HEADERS)) { + Http2Frame f = readFrame(); + if (!(f instanceof HeaderFrame)) { + handleCommonFrame(f); // should only be error frames + } else { + frame = (HeaderFrame) f; + frames.add(frame); + } + } + HttpHeadersImpl headers = decodeHeaders(frames); + Queue q = new Queue(); + streams.put(streamid, q); + exec.submit(() -> { + handleRequest(headers, q, streamid); + }); + } + + // runs in own thread. Handles request from start to finish. Incoming frames + // for this stream/request delivered on Q + + @SuppressWarnings({"rawtypes","unchecked"}) + void handleRequest(HttpHeadersImpl headers, Queue queue, int streamid) { + String method = headers.firstValue(":method").orElse(""); + System.out.println("method = " + method); + String path = headers.firstValue(":path").orElse(""); + System.out.println("path = " + path); + String scheme = headers.firstValue(":scheme").orElse(""); + System.out.println("scheme = " + scheme); + String authority = headers.firstValue(":authority").orElse(""); + System.out.println("authority = " + authority); + HttpHeadersImpl rspheaders = new HttpHeadersImpl(); + int winsize = clientSettings.getParameter( + SettingsFrame.INITIAL_WINDOW_SIZE); + System.err.println ("Stream window size = " + winsize); + try ( + BodyInputStream bis = new BodyInputStream(queue, streamid, this); + BodyOutputStream bos = new BodyOutputStream(streamid, winsize, this); + ) + { + String us = scheme + "://" + authority + path; + URI uri = new URI(us); + boolean pushAllowed = clientSettings.getParameter(SettingsFrame.ENABLE_PUSH) == 1; + Http2TestExchange exchange = new Http2TestExchange(streamid, method, + headers, rspheaders, uri, bis, bos, this, pushAllowed); + + // give to user + handler.handle(exchange); + + // everything happens in the exchange from here. Hopefully will + // return though. + } catch (Throwable e) { + System.err.println("TestServer: handleRequest exception: " + e); + e.printStackTrace(); + } + } + + // Runs in own thread + + @SuppressWarnings({"rawtypes","unchecked"}) + void readLoop() { + try { + while (!stopping) { + Http2Frame frame = readFrame(); + int stream = frame.streamid(); + if (stream == 0) { + if (frame.type() == WindowUpdateFrame.TYPE) { + WindowUpdateFrame wup = (WindowUpdateFrame) frame; + updateConnectionWindow(wup.getUpdate()); + } else { + // other common frame types + handleCommonFrame(frame); + } + } else { + Queue q = streams.get(stream); + if (frame.type() == HeadersFrame.TYPE) { + if (q != null) { + System.err.println("HEADERS frame for existing stream! Error."); + // TODO: close connection + continue; + } else { + createStream((HeadersFrame) frame); + } + } else { + if (q == null) { + System.err.printf("Non Headers frame received with"+ + " non existing stream (%d) ", frame.streamid()); + System.err.println(frame); + continue; + } + if (frame.type() == WindowUpdateFrame.TYPE) { + WindowUpdateFrame wup = (WindowUpdateFrame) frame; + synchronized (updaters) { + Consumer r = updaters.get(stream); + r.accept(wup.getUpdate()); + } + } else { + q.put(frame); + } + } + } + } + } catch (Throwable e) { + close(); + if (!stopping) { + System.err.println("Http server reader thread shutdown"); + e.printStackTrace(); + } + } + } + + // set streamid outside plus other specific fields + void encodeHeaders(HttpHeadersImpl headers, HeaderFrame out) { + List buffers = new LinkedList<>(); + + ByteBuffer buf = getBuffer(); + boolean encoded; + for (Map.Entry> entry : headers.map().entrySet()) { + List values = entry.getValue(); + String key = entry.getKey().toLowerCase(); + for (String value : values) { + do { + hpackOut.header(key, value); + encoded = hpackOut.encode(buf); + if (!encoded) { + buf.flip(); + buffers.add(buf); + buf = getBuffer(); + } + } while (!encoded); + } + } + buf.flip(); + buffers.add(buf); + out.setFlags(HeaderFrame.END_HEADERS); + out.setHeaderBlock(buffers.toArray(bbarray)); + } + + static void closeIgnore(Closeable c) { + try { + c.close(); + } catch (IOException e) {} + } + + // Runs in own thread + void writeLoop() { + try { + while (!stopping) { + Http2Frame frame = outputQ.take(); + if (frame instanceof ResponseHeaders) { + ResponseHeaders rh = (ResponseHeaders)frame; + HeadersFrame hf = new HeadersFrame(); + encodeHeaders(rh.headers, hf); + hf.streamid(rh.streamid()); + writeFrame(hf); + } else if (frame instanceof OutgoingPushPromise) { + handlePush((OutgoingPushPromise)frame); + } else + writeFrame(frame); + } + System.err.println("Connection writer stopping"); + } catch (Throwable e) { + e.printStackTrace(); + /*close(); + if (!stopping) { + e.printStackTrace(); + System.err.println("TestServer: writeLoop exception: " + e); + }*/ + } + } + + private void handlePush(OutgoingPushPromise op) throws IOException { + PushPromiseFrame pp = new PushPromiseFrame(); + encodeHeaders(op.headers, pp); + int promisedStreamid = nextPushStreamId; + nextPushStreamId += 2; + pp.streamid(op.parentStream); + pp.setPromisedStream(promisedStreamid); + writeFrame(pp); + final InputStream ii = op.is; + final BodyOutputStream oo = new BodyOutputStream( + promisedStreamid, + clientSettings.getParameter( + SettingsFrame.INITIAL_WINDOW_SIZE), this); + oo.goodToGo(); + exec.submit(() -> { + try { + ResponseHeaders oh = getPushResponse(promisedStreamid); + outputQ.put(oh); + ii.transferTo(oo); + } catch (Throwable ex) { + System.err.printf("TestServer: pushing response error: %s\n", + ex.toString()); + } finally { + closeIgnore(ii); + closeIgnore(oo); + } + }); + + } + + // returns a minimal response with status 200 + // that is the response to the push promise just sent + private ResponseHeaders getPushResponse(int streamid) { + HttpHeadersImpl h = new HttpHeadersImpl(); + h.addHeader(":status", "200"); + ResponseHeaders oh = new ResponseHeaders(h); + oh.streamid(streamid); + return oh; + } + + private ByteBuffer getBuffer() { + return ByteBuffer.allocate(8 * 1024); + } + + private Http2Frame readFrame() throws IOException { + byte[] buf = new byte[9]; + if (is.readNBytes(buf, 0, 9) != 9) + throw new IOException("readFrame: connection closed"); + int len = 0; + for (int i = 0; i < 3; i++) { + int n = buf[i] & 0xff; + //System.err.println("n = " + n); + len = (len << 8) + n; + } + byte[] rest = new byte[len]; + int n = is.readNBytes(rest, 0, len); + if (n != len) + throw new IOException("Error reading frame"); + ByteBufferConsumer bc = new ByteBufferConsumer( + new LinkedList(List.of(ByteBuffer.wrap(buf), ByteBuffer.wrap(rest))), + this::getBuffer); + return Http2Frame.readIncoming(bc); + } + + void sendSettingsFrame() throws IOException { + sendSettingsFrame(false); + } + + void sendSettingsFrame(boolean now) throws IOException { + if (serverSettings == null) { + serverSettings = SettingsFrame.getDefaultSettings(); + } + if (now) { + writeFrame(serverSettings); + } else { + outputQ.put(serverSettings); + } + } + + String readUntil(String end) throws IOException { + int number = end.length(); + int found = 0; + StringBuilder sb = new StringBuilder(); + while (found < number) { + char expected = end.charAt(found); + int c = is.read(); + if (c == -1) { + throw new IOException("Connection closed"); + } + char c0 = (char) c; + sb.append(c0); + if (c0 != expected) { + found = 0; + continue; + } + found++; + } + return sb.toString(); + } + + private int getContentLength(String headers) { + return getIntHeader(headers, "Content-length"); + } + + private int getIntHeader(String headers, String name) { + String val = getHeader(headers, name); + if (val == null) { + return -1; + } + return Integer.parseInt(val); + } + + private String getHeader(String headers, String name) { + String headers1 = headers.toLowerCase(); // not efficient + name = CRLF + name.toLowerCase(); + int start = headers1.indexOf(name); + if (start == -1) { + return null; + } + start += 2; + int end = headers1.indexOf(CRLF, start); + String line = headers.substring(start, end); + start = line.indexOf(':'); + if (start == -1) { + return null; + } + return line.substring(start + 1).trim(); + } + + final static String CRLF = "\r\n"; + + String readHttp1Request() throws IOException { + String headers = readUntil(CRLF + CRLF); + int clen = getContentLength(headers); + // read the content. There shouldn't be content but .. + byte[] buf = new byte[clen]; + is.readNBytes(buf, 0, clen); + String body = new String(buf, "US-ASCII"); + return headers + body; + } + + void sendHttp1Response(int code, String msg, String... headers) throws IOException { + StringBuilder sb = new StringBuilder(); + sb.append("HTTP/1.1 ") + .append(code) + .append(' ') + .append(msg) + .append(CRLF); + int numheaders = headers.length; + for (int i = 0; i < numheaders; i += 2) { + sb.append(headers[i]) + .append(": ") + .append(headers[i + 1]) + .append(CRLF); + } + sb.append(CRLF); + String s = sb.toString(); + os.write(s.getBytes("US-ASCII")); + os.flush(); + } + + private void unexpectedFrame(Http2Frame frame) { + System.err.println("OOPS. Unexpected"); + assert false; + } + + final static ByteBuffer[] bbarray = new ByteBuffer[0]; + + // wrapper around a BlockingQueue that throws an exception when it's closed + // Each stream has one of these + + String getRequestBody(String request) { + int bodystart = request.indexOf(CRLF+CRLF); + String body; + if (bodystart == -1) + body = ""; + else + body = request.substring(bodystart+4); + return body; + } + + @SuppressWarnings({"rawtypes","unchecked"}) + void addRequestBodyToQueue(String body, Queue q) throws IOException { + ByteBuffer buf = ByteBuffer.wrap(body.getBytes(StandardCharsets.US_ASCII)); + DataFrame df = new DataFrame(); + df.streamid(1); // only used for primordial stream + df.setData(buf); + df.computeLength(); + df.setFlag(DataFrame.END_STREAM); + q.put(df); + } + + // window updates done in main reader thread because they may + // be used to unblock BodyOutputStreams waiting for WUPs + + HashMap> updaters = new HashMap<>(); + + void registerStreamWindowUpdater(int streamid, Consumer r) { + synchronized(updaters) { + updaters.put(streamid, r); + } + } + + int sendWindow = 64 * 1024 - 1; // connection level send window + + /** + * BodyOutputStreams call this to get the connection window first. + * + * @param amount + */ + synchronized void obtainConnectionWindow(int amount) throws InterruptedException { + while (amount > 0) { + int n = Math.min(amount, sendWindow); + amount -= n; + sendWindow -= n; + if (amount > 0) + wait(); + } + } + + synchronized void updateConnectionWindow(int amount) { + sendWindow += amount; + notifyAll(); + } + + // simplified output headers class. really just a type safe container + // for the hashmap. + + static class ResponseHeaders extends Http2Frame { + HttpHeadersImpl headers; + + ResponseHeaders(HttpHeadersImpl headers) { + this.headers = headers; + } + + @Override + void readIncomingImpl(ByteBufferConsumer bc) throws IOException { + throw new UnsupportedOperationException("Not supported ever!"); + } + + @Override + void computeLength() { + throw new UnsupportedOperationException("Not supported ever!"); + } + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/OutgoingPushPromise.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/OutgoingPushPromise.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,31 @@ +package java.net.http; + +import java.io.*; +import java.net.*; + +// will be converted to a PushPromiseFrame in the writeLoop +// a thread is then created to produce the DataFrames from the InputStream +class OutgoingPushPromise extends Http2Frame { + final HttpHeadersImpl headers; + final URI uri; + final InputStream is; + final int parentStream; // not the pushed streamid + + OutgoingPushPromise(int parentStream, URI uri, HttpHeadersImpl headers, InputStream is) { + this.uri = uri; + this.headers = headers; + this.is = is; + this.parentStream = parentStream; + } + + @Override + void readIncomingImpl(ByteBufferConsumer bc) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + void computeLength() { + throw new UnsupportedOperationException("Not supported yet."); + } + +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/PushHandler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/PushHandler.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2015, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net.http; + +import java.io.*; +import java.net.*; +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.net.http.*; +import java.util.*; +import java.util.concurrent.*; + +public class PushHandler implements Http2Handler { + + final Path tempFile; + final int loops; + + public PushHandler(int file_size, int loops) throws Exception { + tempFile = TestUtil.getAFile(file_size); + this.loops = loops; + } + + int invocation = 0; + + public void handle(Http2TestExchange ee) { + try { + System.err.println ("Server: handle " + ee); + invocation++; + + if (ee.serverPushAllowed()) { + for (int i=0; i task1 = pool.submit(() -> openAndCloseWatcher(dir)); + Future task2 = pool.submit(() -> deleteAndRecreateDirectory(dir)); + task1.get(); + task2.get(); + } finally { + pool.shutdown(); + deleteFileTree(dir); + } + } + + private static void openAndCloseWatcher(Path dir) { + FileSystem fs = FileSystems.getDefault(); + for (int i = 0; i < ITERATIONS_COUNT; i++) { + try (WatchService watcher = fs.newWatchService()) { + dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); + } catch (IOException ioe) { + // ignore + } + } + } + + private static void deleteAndRecreateDirectory(Path dir) { + for (int i = 0; i < ITERATIONS_COUNT; i++) { + try { + deleteFileTree(dir); + Path subdir = Files.createDirectories(dir.resolve("subdir")); + Files.createFile(subdir.resolve("test")); + } catch (IOException ioe) { + // ignore + } + } + } + + private static void deleteFileTree(Path file) { + try { + if (Files.isDirectory(file)) { + try (DirectoryStream stream = Files.newDirectoryStream(file)) { + for (Path pa : stream) { + deleteFileTree(pa); + } + } + } + Files.delete(file); + } catch (IOException ioe) { + // ignore + } + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/util/ResourceBundle/Bug6299235Test.sh --- a/jdk/test/java/util/ResourceBundle/Bug6299235Test.sh Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/util/ResourceBundle/Bug6299235Test.sh Tue May 03 12:25:20 2016 -0700 @@ -68,7 +68,7 @@ ${TESTJAVA}/bin/jar xf ${TESTSRC}/awtres.jar echo -${TESTJAVA}/bin/java ${TESTVMOPTS} -Xpatch:${PATCHDIR} \ +${TESTJAVA}/bin/java ${TESTVMOPTS} -Xpatch:java.desktop=${PATCHDIR}/java.desktop \ -cp ${TESTCLASSES} Bug6299235Test if [ $? -ne 0 ] diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/DoublePrimitiveOpsTests.java --- a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/DoublePrimitiveOpsTests.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/DoublePrimitiveOpsTests.java Tue May 03 12:25:20 2016 -0700 @@ -27,11 +27,18 @@ import java.util.Arrays; import java.util.Random; +import java.util.Spliterator; import java.util.stream.DoubleStream; import java.util.stream.LongStream; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +/** + * @test + * @bug 8153293 + */ @Test public class DoublePrimitiveOpsTests { @@ -42,6 +49,13 @@ assertEquals(sum, 1.0 + 2.0 + 3.0 + 4.0 + 5.0); } + public void testFlags() { + assertTrue(LongStream.range(1, 10).asDoubleStream().boxed().spliterator() + .hasCharacteristics(Spliterator.SORTED)); + assertFalse(DoubleStream.of(1, 10).boxed().spliterator() + .hasCharacteristics(Spliterator.SORTED)); + } + public void testToArray() { { double[] array = LongStream.range(1, 10).asDoubleStream().map(i -> i * 2).toArray(); @@ -72,6 +86,22 @@ } } + public void testSortDistinct() { + { + double[] range = LongStream.range(0, 10).asDoubleStream().toArray(); + + assertEquals(LongStream.range(0, 10).asDoubleStream().sorted().distinct().toArray(), range); + assertEquals(LongStream.range(0, 10).asDoubleStream().parallel().sorted().distinct().toArray(), range); + + double[] data = {5, 3, 1, 1, 5, Double.NaN, 3, 9, Double.POSITIVE_INFINITY, + Double.NEGATIVE_INFINITY, 2, 9, 1, 0, 8, Double.NaN, -0.0}; + double[] expected = {Double.NEGATIVE_INFINITY, -0.0, 0, 1, 2, 3, 5, 8, 9, + Double.POSITIVE_INFINITY, Double.NaN}; + assertEquals(DoubleStream.of(data).sorted().distinct().toArray(), expected); + assertEquals(DoubleStream.of(data).parallel().sorted().distinct().toArray(), expected); + } + } + public void testSortSort() { Random r = new Random(); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/IntPrimitiveOpsTests.java --- a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/IntPrimitiveOpsTests.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/IntPrimitiveOpsTests.java Tue May 03 12:25:20 2016 -0700 @@ -28,13 +28,21 @@ import java.util.Arrays; import java.util.List; import java.util.Random; +import java.util.Spliterator; +import java.util.TreeSet; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.IntConsumer; import java.util.stream.Collectors; import java.util.stream.IntStream; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +/** + * @test + * @bug 8153293 + */ @Test public class IntPrimitiveOpsTests { @@ -85,6 +93,29 @@ assertEquals(sum, 15); } + public void testFlags() { + assertTrue(IntStream.range(1, 10).boxed().spliterator() + .hasCharacteristics(Spliterator.SORTED | Spliterator.DISTINCT)); + assertFalse(IntStream.of(1, 10).boxed().spliterator() + .hasCharacteristics(Spliterator.SORTED)); + assertFalse(IntStream.of(1, 10).boxed().spliterator() + .hasCharacteristics(Spliterator.DISTINCT)); + + assertTrue(IntStream.range(1, 10).asLongStream().spliterator() + .hasCharacteristics(Spliterator.SORTED | Spliterator.DISTINCT)); + assertFalse(IntStream.of(1, 10).asLongStream().spliterator() + .hasCharacteristics(Spliterator.SORTED)); + assertFalse(IntStream.of(1, 10).asLongStream().spliterator() + .hasCharacteristics(Spliterator.DISTINCT)); + + assertTrue(IntStream.range(1, 10).asDoubleStream().spliterator() + .hasCharacteristics(Spliterator.SORTED | Spliterator.DISTINCT)); + assertFalse(IntStream.of(1, 10).asDoubleStream().spliterator() + .hasCharacteristics(Spliterator.SORTED)); + assertFalse(IntStream.of(1, 10).asDoubleStream().spliterator() + .hasCharacteristics(Spliterator.DISTINCT)); + } + public void testToArray() { { int[] array = IntStream.range(1, 10).map(i -> i * 2).toArray(); @@ -115,6 +146,35 @@ } } + public void testSortDistinct() { + { + int[] range = IntStream.range(0, 10).toArray(); + + assertEquals(IntStream.range(0, 10).sorted().distinct().toArray(), range); + assertEquals(IntStream.range(0, 10).parallel().sorted().distinct().toArray(), range); + + int[] data = {5, 3, 1, 1, 5, 3, 9, 2, 9, 1, 0, 8}; + int[] expected = {0, 1, 2, 3, 5, 8, 9}; + assertEquals(IntStream.of(data).sorted().distinct().toArray(), expected); + assertEquals(IntStream.of(data).parallel().sorted().distinct().toArray(), expected); + } + + { + int[] input = new Random().ints(100, -10, 10).map(x -> x+Integer.MAX_VALUE).toArray(); + TreeSet longs = new TreeSet<>(); + for(int i : input) longs.add((long)i); + long[] expectedLongs = longs.stream().mapToLong(Long::longValue).toArray(); + assertEquals(IntStream.of(input).sorted().asLongStream().sorted().distinct().toArray(), + expectedLongs); + + TreeSet doubles = new TreeSet<>(); + for(int i : input) doubles.add((double)i); + double[] expectedDoubles = doubles.stream().mapToDouble(Double::doubleValue).toArray(); + assertEquals(IntStream.of(input).sorted().distinct().asDoubleStream() + .sorted().distinct().toArray(), expectedDoubles); + } + } + public void testSortSort() { Random r = new Random(); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/LongPrimitiveOpsTests.java --- a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/LongPrimitiveOpsTests.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/LongPrimitiveOpsTests.java Tue May 03 12:25:20 2016 -0700 @@ -28,13 +28,21 @@ import java.util.Arrays; import java.util.List; import java.util.Random; +import java.util.Spliterator; +import java.util.TreeSet; import java.util.concurrent.atomic.AtomicLong; import java.util.function.LongConsumer; import java.util.stream.Collectors; import java.util.stream.LongStream; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +/** + * @test + * @bug 8153293 + */ @Test public class LongPrimitiveOpsTests { @@ -85,6 +93,22 @@ assertEquals(sum, 15); } + public void testFlags() { + assertTrue(LongStream.range(1, 10).boxed().spliterator() + .hasCharacteristics(Spliterator.SORTED | Spliterator.DISTINCT)); + assertFalse(LongStream.of(1, 10).boxed().spliterator() + .hasCharacteristics(Spliterator.SORTED)); + assertFalse(LongStream.of(1, 10).boxed().spliterator() + .hasCharacteristics(Spliterator.DISTINCT)); + + assertTrue(LongStream.range(1, 10).asDoubleStream().spliterator() + .hasCharacteristics(Spliterator.SORTED)); + assertFalse(LongStream.range(1, 10).asDoubleStream().spliterator() + .hasCharacteristics(Spliterator.DISTINCT)); + assertFalse(LongStream.of(1, 10).asDoubleStream().spliterator() + .hasCharacteristics(Spliterator.SORTED)); + } + public void testToArray() { { long[] array = LongStream.range(1, 10).map(i -> i * 2).toArray(); @@ -115,6 +139,30 @@ } } + public void testSortDistinct() { + { + long[] range = LongStream.range(0, 10).toArray(); + + assertEquals(LongStream.range(0, 10).sorted().distinct().toArray(), range); + assertEquals(LongStream.range(0, 10).parallel().sorted().distinct().toArray(), range); + + long[] data = {5, 3, 1, 1, 5, 3, 9, 2, 9, 1, 0, 8}; + long[] expected = {0, 1, 2, 3, 5, 8, 9}; + assertEquals(LongStream.of(data).sorted().distinct().toArray(), expected); + assertEquals(LongStream.of(data).parallel().sorted().distinct().toArray(), expected); + } + + { + long[] input = new Random().longs(100, -10, 10).map(x -> x+Long.MAX_VALUE).toArray(); + + TreeSet doubles = new TreeSet<>(); + for(long i : input) doubles.add((double)i); + double[] expectedDoubles = doubles.stream().mapToDouble(Double::doubleValue).toArray(); + assertEquals(LongStream.of(input).sorted().distinct().asDoubleStream() + .sorted().distinct().toArray(), expectedDoubles); + } + } + public void testSortSort() { Random r = new Random(); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/crypto/Cipher/CipherStreamClose.java --- a/jdk/test/javax/crypto/Cipher/CipherStreamClose.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/crypto/Cipher/CipherStreamClose.java Tue May 03 12:25:20 2016 -0700 @@ -27,6 +27,8 @@ * @summary Make sure Cipher IO streams doesn't call extra doFinal if close() * is called multiple times. Additionally, verify the input and output streams * match with encryption and decryption with non-stream crypto. + * @compile -addmods java.xml.bind CipherStreamClose.java + * @run main/othervm -addmods java.xml.bind CipherStreamClose */ import java.io.*; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/rmi/PortableRemoteObject/ConcurrentHashMapTest.java --- a/jdk/test/javax/rmi/PortableRemoteObject/ConcurrentHashMapTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/rmi/PortableRemoteObject/ConcurrentHashMapTest.java Tue May 03 12:25:20 2016 -0700 @@ -27,8 +27,10 @@ * @summary test RMI-IIOP call with ConcurrentHashMap as an argument * @library /lib/testlibrary * @build jdk.testlibrary.* - * @build Test HelloInterface HelloServer HelloClient HelloImpl _HelloImpl_Tie _HelloInterface_Stub ConcurrentHashMapTest - * @run main/othervm -Djava.naming.provider.url=iiop://localhost:1050 -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory ConcurrentHashMapTest + * @compile -addmods java.corba Test.java HelloInterface.java HelloServer.java HelloClient.java + * HelloImpl.java _HelloImpl_Tie.java _HelloInterface_Stub.java ConcurrentHashMapTest.java + * @run main/othervm -addmods java.corba -Djava.naming.provider.url=iiop://localhost:1050 + * -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory ConcurrentHashMapTest * @key intermittent */ @@ -101,6 +103,8 @@ // -Djava.naming.provider.url=iiop://localhost:1050 HelloServer List commands = new ArrayList<>(); commands.add(ConcurrentHashMapTest.JAVA); + commands.add("-addmods"); + commands.add("java.corba"); commands.add("-Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory"); commands.add("-Djava.naming.provider.url=iiop://localhost:1050"); commands.add("-cp"); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/smartcardio/CommandAPDUTest.java --- a/jdk/test/javax/smartcardio/CommandAPDUTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/smartcardio/CommandAPDUTest.java Tue May 03 12:25:20 2016 -0700 @@ -26,7 +26,8 @@ * @bug 8049021 * @summary Test different constructors for CommandAPDU and check CLA,INS,NC,NE, * P1,and P2 - * @run testng CommandAPDUTest + * @compile -addmods java.smartcardio CommandAPDUTest.java + * @run testng/othervm -addmods java.smartcardio CommandAPDUTest */ import java.nio.ByteBuffer; import javax.smartcardio.CommandAPDU; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/smartcardio/HistoricalBytes.java --- a/jdk/test/javax/smartcardio/HistoricalBytes.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/smartcardio/HistoricalBytes.java Tue May 03 12:25:20 2016 -0700 @@ -26,7 +26,9 @@ * @bug 6445367 * @summary Verify that ATR.getHistoricalBytes() works * @author Andreas Sterbenz -**/ + * @compile -addmods java.smartcardio HistoricalBytes.java + * @run main/othervm -addmods java.smartcardio HistoricalBytes + */ import java.util.Arrays; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/smartcardio/ResponseAPDUTest.java --- a/jdk/test/javax/smartcardio/ResponseAPDUTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/smartcardio/ResponseAPDUTest.java Tue May 03 12:25:20 2016 -0700 @@ -25,7 +25,8 @@ * @test * @bug 8049021 * @summary Construct ResponseAPDU from byte array and check NR< SW, SW1 and SW2 - * @run testng ResponseAPDUTest + * @compile -addmods java.smartcardio ResponseAPDUTest.java + * @run testng/othervm -addmods java.smartcardio ResponseAPDUTest */ import javax.smartcardio.ResponseAPDU; import static org.testng.Assert.*; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/smartcardio/Serialize.java --- a/jdk/test/javax/smartcardio/Serialize.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/smartcardio/Serialize.java Tue May 03 12:25:20 2016 -0700 @@ -26,6 +26,8 @@ * @bug 6445367 * @summary make sure serialization works * @author Andreas Sterbenz + * @compile -addmods java.smartcardio Serialize.java + * @run main/othervm -addmods java.smartcardio Serialize */ import java.io.*; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/smartcardio/TerminalFactorySpiTest.java --- a/jdk/test/javax/smartcardio/TerminalFactorySpiTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/smartcardio/TerminalFactorySpiTest.java Tue May 03 12:25:20 2016 -0700 @@ -25,7 +25,8 @@ * @test * @bug 8049021 * @summary Test if we can write new provider for smart card - * @run main/othervm/policy=policy TerminalFactorySpiTest + * @compile -addmods java.smartcardio TerminalFactorySpiTest.java + * @run main/othervm/policy=policy -addmods java.smartcardio TerminalFactorySpiTest */ import java.security.Provider; import java.security.Security; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/smartcardio/TestCardPermission.java --- a/jdk/test/javax/smartcardio/TestCardPermission.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/smartcardio/TestCardPermission.java Tue May 03 12:25:20 2016 -0700 @@ -26,6 +26,8 @@ * @bug 6293767 * @summary Test for the CardPermission class * @author Andreas Sterbenz + * @compile -addmods java.smartcardio TestCardPermission.java + * @run main/othervm -addmods java.smartcardio TestCardPermission */ import javax.smartcardio.*; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/smartcardio/TestCommandAPDU.java --- a/jdk/test/javax/smartcardio/TestCommandAPDU.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/smartcardio/TestCommandAPDU.java Tue May 03 12:25:20 2016 -0700 @@ -27,6 +27,8 @@ * @summary Test for the CommandAPDU class * @author Andreas Sterbenz * @key randomness + * @compile -addmods java.smartcardio TestCommandAPDU.java + * @run main/othervm -addmods java.smartcardio TestCommandAPDU */ import java.util.*; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/transaction/testng/Driver.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/transaction/testng/Driver.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,36 @@ +/* + * 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. + * + * 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 + * @compile -addmods java.transaction + * test/transaction/InvalidTransactionExceptionTests.java + * test/transaction/TransactionRequiredExceptionTests.java + * test/transaction/TransactionRolledbackExceptionTests.java + * test/transaction/XAExceptionTests.java + * util/SerializedTransactionExceptions.java + * @run testng/othervm -addmods java.transaction test.transaction.InvalidTransactionExceptionTests + * @run testng/othervm -addmods java.transaction test.transaction.TransactionRequiredExceptionTests + * @run testng/othervm -addmods java.transaction test.transaction.TransactionRolledbackExceptionTests + * @run testng/othervm -addmods java.transaction util.SerializedTransactionExceptions + */ diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/transaction/testng/TEST.properties --- a/jdk/test/javax/transaction/testng/TEST.properties Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -# JDBC unit tests uses TestNG -TestNG.dirs= . -othervm.dirs= . - diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/xml/bind/xjc/8032884/XjcOptionalPropertyTest.java --- a/jdk/test/javax/xml/bind/xjc/8032884/XjcOptionalPropertyTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/xml/bind/xjc/8032884/XjcOptionalPropertyTest.java Tue May 03 12:25:20 2016 -0700 @@ -26,6 +26,7 @@ * @bug 8032884 * @summary Globalbindings optionalProperty="primitive" does not work when minOccurs=0 * @run shell compile-schema.sh + * @compile -addmods java.xml.bind XjcOptionalPropertyTest.java * @run main/othervm XjcOptionalPropertyTest */ diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/xml/jaxp/common/8035437/run.sh --- a/jdk/test/javax/xml/jaxp/common/8035437/run.sh Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/xml/jaxp/common/8035437/run.sh Tue May 03 12:25:20 2016 -0700 @@ -34,12 +34,12 @@ -d compile/java.xml -Xmodule:java.xml $TESTSRC/Document.java $TESTSRC/Node.java || exit 1 $COMPILEJAVA/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ - -d exec/java.xml -Xpatch:compile -Xmodule:java.xml $TESTSRC/DocumentImpl.java || exit 2 + -d exec/java.xml -Xpatch:java.xml=compile/java.xml -Xmodule:java.xml $TESTSRC/DocumentImpl.java || exit 2 $COMPILEJAVA/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ $TESTSRC/AbstractMethodErrorTest.java -d exec || exit 3 -$TESTJAVA/bin/java ${TESTVMOPTS} -Xpatch:exec -cp exec AbstractMethodErrorTest || exit 4 +$TESTJAVA/bin/java ${TESTVMOPTS} -Xpatch:java.xml=exec -cp exec AbstractMethodErrorTest || exit 4 exit 0 diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/xml/soap/XmlTest.java --- a/jdk/test/javax/xml/soap/XmlTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/xml/soap/XmlTest.java Tue May 03 12:25:20 2016 -0700 @@ -38,6 +38,8 @@ /* * @test + * @compile -addmods java.xml.ws XmlTest.java + * @run main/othervm -addmods java.xml.ws XmlTest * @summary tests JAF DataHandler can be instantiated; test serialize and * deserialize SOAP message containing xml attachment */ diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/xml/soap/spi/SAAJFactoryTest.java --- a/jdk/test/javax/xml/soap/spi/SAAJFactoryTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/xml/soap/spi/SAAJFactoryTest.java Tue May 03 12:25:20 2016 -0700 @@ -49,29 +49,29 @@ * run main/othervm SAAJFactoryTest saaj.factory.Valid - * scenario14 javax.xml.soap.MessageFactory=saaj.factory.Valid saaj.factory.Valid2 - * - * @build saaj.factory.* + * @compile -addmods java.xml.ws saaj/factory/Invalid.java saaj/factory/Valid.java + * saaj/factory/Valid2.java saaj/factory/Valid3.java SAAJFactoryTest.java * - * @run main/othervm SAAJFactoryTest com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl - - * scenario2 - - - * @run main/othervm -Djavax.xml.soap.MessageFactory=saaj.factory.Valid SAAJFactoryTest saaj.factory.Valid - - * scenario5 - - - * @run main/othervm -Djavax.xml.soap.MessageFactory=saaj.factory.NonExisting SAAJFactoryTest - * - javax.xml.soap.SOAPException - * scenario6 - - - * @run main/othervm -Djavax.xml.soap.MessageFactory=saaj.factory.Invalid SAAJFactoryTest - javax.xml.soap.SOAPException - * scenario7 - - - * @run main/othervm SAAJFactoryTest saaj.factory.Valid - - * scenario8 - saaj.factory.Valid - * @run main/othervm SAAJFactoryTest saaj.factory.Valid - - * scenario9 - saaj.factory.Valid - * @run main/othervm SAAJFactoryTest - javax.xml.soap.SOAPException - * scenario10 - saaj.factory.NonExisting - * @run main/othervm SAAJFactoryTest - javax.xml.soap.SOAPException - * scenario11 - saaj.factory.Invalid - * @run main/othervm SAAJFactoryTest com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl - - * scenario12 - - - * @run main/othervm SAAJFactoryTest saaj.factory.Valid - - * scenario15 - saaj.factory.Valid + * @run main/othervm -addmods java.xml.ws + * SAAJFactoryTest com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl - scenario2 - - + * @run main/othervm -addmods java.xml.ws -Djavax.xml.soap.MessageFactory=saaj.factory.Valid + * SAAJFactoryTest saaj.factory.Valid - scenario5 - - + * @run main/othervm -addmods java.xml.ws -Djavax.xml.soap.MessageFactory=saaj.factory.NonExisting + * SAAJFactoryTest - javax.xml.soap.SOAPException scenario6 - - + * @run main/othervm -addmods java.xml.ws -Djavax.xml.soap.MessageFactory=saaj.factory.Invalid + * SAAJFactoryTest - javax.xml.soap.SOAPException scenario7 - - + * @run main/othervm -addmods java.xml.ws + * SAAJFactoryTest saaj.factory.Valid - scenario8 - saaj.factory.Valid + * @run main/othervm -addmods java.xml.ws + * SAAJFactoryTest saaj.factory.Valid - scenario9 - saaj.factory.Valid + * @run main/othervm -addmods java.xml.ws + * SAAJFactoryTest - javax.xml.soap.SOAPException scenario10 - saaj.factory.NonExisting + * @run main/othervm -addmods java.xml.ws + * SAAJFactoryTest - javax.xml.soap.SOAPException scenario11 - saaj.factory.Invalid scenario11 - saaj.factory.Invalid + * @run main/othervm -addmods java.xml.ws + * SAAJFactoryTest com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl - scenario12 - - + * @run main/othervm -addmods java.xml.ws + * SAAJFactoryTest saaj.factory.Valid - scenario15 - saaj.factory.Valid */ public class SAAJFactoryTest { diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/xml/ws/8043129/MailTest.java --- a/jdk/test/javax/xml/ws/8043129/MailTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/xml/ws/8043129/MailTest.java Tue May 03 12:25:20 2016 -0700 @@ -27,8 +27,8 @@ * @summary JAF initialisation in SAAJ clashing with the one in javax.mail * @author mkos * @library javax.mail.jar - * @build MailTest - * @run main MailTest + * @compile -addmods java.xml.ws MailTest.java + * @run main/othervm -addmods java.xml.ws MailTest */ import javax.activation.CommandMap; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/xml/ws/clientjar/TestWsImport.java --- a/jdk/test/javax/xml/ws/clientjar/TestWsImport.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/xml/ws/clientjar/TestWsImport.java Tue May 03 12:25:20 2016 -0700 @@ -25,7 +25,8 @@ * @test * @bug 8016271 8026405 * @summary wsimport -clientjar does not create portable jar on windows due to hardcoded '\' - * @run main/othervm TestWsImport + * @compile -addmods java.xml.ws TestWsImport.java + * @run main/othervm -addmods java.xml.ws TestWsImport */ import javax.xml.namespace.QName; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/xml/ws/publish/WSTest.java --- a/jdk/test/javax/xml/ws/publish/WSTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/xml/ws/publish/WSTest.java Tue May 03 12:25:20 2016 -0700 @@ -25,7 +25,8 @@ * @test * @bug 8146086 * @summary Publishing two webservices on same port fails with "java.net.BindException: Address already in use" - * @run main/othervm WSTest + * @compile -addmods java.xml.ws WSTest.java + * @run main/othervm -addmods java.xml.ws WSTest */ import javax.jws.WebMethod; import javax.jws.WebService; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/javax/xml/ws/xsanymixed/Test.java --- a/jdk/test/javax/xml/ws/xsanymixed/Test.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/javax/xml/ws/xsanymixed/Test.java Tue May 03 12:25:20 2016 -0700 @@ -27,7 +27,8 @@ * @summary the content of xs:any content:mixed should remain as is, * no white space changes and no changes to namespace prefixes * @run shell compile-wsdl.sh - * @run main/othervm Test + * @compile -addmods java.xml.ws Test.java + * @run main/othervm -addmods java.xml.ws Test */ import com.sun.net.httpserver.HttpServer; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/sun/security/pkcs11/KeyStore/SecretKeysBasic.java --- a/jdk/test/sun/security/pkcs11/KeyStore/SecretKeysBasic.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/sun/security/pkcs11/KeyStore/SecretKeysBasic.java Tue May 03 12:25:20 2016 -0700 @@ -26,7 +26,6 @@ import java.security.*; import javax.crypto.*; import javax.crypto.spec.*; -import javax.xml.bind.DatatypeConverter; public class SecretKeysBasic extends PKCS11Test { @@ -131,8 +130,11 @@ System.out.println(info + "> " + key); System.out.println("\tALGO=" + key.getAlgorithm()); if (key.getFormat() != null) { - System.out.println("\t[" + key.getFormat() + "] VALUE=" + - DatatypeConverter.printHexBinary(key.getEncoded())); + StringBuilder sb = new StringBuilder(); + for (byte b : key.getEncoded()) { + sb.append(String.format("%02x", b & 0xff)); + } + System.out.println("\t[" + key.getFormat() + "] VALUE=" + sb); } else { System.out.println("\tVALUE=n/a"); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/sun/security/provider/PolicyFile/Modules.java --- a/jdk/test/sun/security/provider/PolicyFile/Modules.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/sun/security/provider/PolicyFile/Modules.java Tue May 03 12:25:20 2016 -0700 @@ -25,7 +25,9 @@ * @test * @bug 8047771 * @summary check permissions and principals from various modules - * @run main/othervm/java.security.policy==modules.policy Modules + * @compile -addmods java.xml.ws,java.smartcardio Modules.java + * @run main/othervm/java.security.policy==modules.policy + * -addmods java.xml.ws,java.smartcardio Modules */ import java.security.AccessController; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/sun/security/tools/jarsigner/Warning.java --- a/jdk/test/sun/security/tools/jarsigner/Warning.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/sun/security/tools/jarsigner/Warning.java Tue May 03 12:25:20 2016 -0700 @@ -89,7 +89,7 @@ issueCert("c"); run("jarsigner", "a.jar c") .shouldContain("chain is not validated. " + - "Reason: algorithm constraints check failed"); + "Reason: Algorithm constraints check failed"); recreateJar(); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jar/modularJar/Basic.java --- a/jdk/test/tools/jar/modularJar/Basic.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/tools/jar/modularJar/Basic.java Tue May 03 12:25:20 2016 -0700 @@ -340,43 +340,43 @@ "--file=" + modularJar.toString()) .assertSuccess() .resultChecker(r -> { - // Expect similar output: "Name:bar, Requires: foo,... - // Conceals: jdk.test.foo, jdk.test.foo.internal" - Pattern p = Pattern.compile("\\s+Name:\\s+bar\\s+Requires:\\s+foo"); + // Expect similar output: "bar, requires mandated foo, ... + // conceals jdk.test.foo, conceals jdk.test.foo.internal" + Pattern p = Pattern.compile("\\s+bar\\s+requires\\s++foo"); assertTrue(p.matcher(r.output).find(), - "Expecting to find \"Name: bar, Requires: foo,...\"", + "Expecting to find \"bar, requires foo,...\"", "in output, but did not: [" + r.output + "]"); p = Pattern.compile( - "Conceals:\\s+jdk.test.foo\\s+jdk.test.foo.internal"); + "conceals\\s+jdk.test.foo\\s+conceals\\s+jdk.test.foo.internal"); assertTrue(p.matcher(r.output).find(), - "Expecting to find \"Conceals: jdk.test.foo,...\"", + "Expecting to find \"conceals jdk.test.foo,...\"", "in output, but did not: [" + r.output + "]"); }); } @Test - public void dependencesFooBar() throws IOException { + public void hashBarInFooModule() throws IOException { Path mp = Paths.get("dependencesFooBar"); createTestDir(mp); - Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName); - Path modularJar = mp.resolve(FOO.moduleName + ".jar"); + Path modClasses = MODULE_CLASSES.resolve(BAR.moduleName); + Path modularJar = mp.resolve(BAR.moduleName + ".jar"); + jar("--create", + "--file=" + modularJar.toString(), + "--main-class=" + BAR.mainClass, + "--module-version=" + BAR.version, + "--no-manifest", + "-C", modClasses.toString(), ".") + .assertSuccess(); + + modClasses = MODULE_CLASSES.resolve(FOO.moduleName); + modularJar = mp.resolve(FOO.moduleName + ".jar"); jar("--create", "--file=" + modularJar.toString(), "--main-class=" + FOO.mainClass, "--module-version=" + FOO.version, - "--no-manifest", - "-C", modClasses.toString(), ".") - .assertSuccess(); - - modClasses = MODULE_CLASSES.resolve(BAR.moduleName); - modularJar = mp.resolve(BAR.moduleName + ".jar"); - jar("--create", - "--file=" + modularJar.toString(), - "--main-class=" + BAR.mainClass, - "--module-version=" + BAR.version, "--modulepath=" + mp.toString(), - "--hash-dependencies=" + "foo", // dependency on foo + "--hash-modules=" + "bar", "--no-manifest", "-C", modClasses.toString(), ".") .assertSuccess(); @@ -392,49 +392,49 @@ } @Test - public void badDependencyFooBar() throws IOException { + public void invalidHashInFooModule() throws IOException { Path mp = Paths.get("badDependencyFooBar"); createTestDir(mp); - Path fooClasses = MODULE_CLASSES.resolve(FOO.moduleName); - Path fooJar = mp.resolve(FOO.moduleName + ".jar"); - jar("--create", - "--file=" + fooJar.toString(), - "--main-class=" + FOO.mainClass, - "--module-version=" + FOO.version, - "--no-manifest", - "-C", fooClasses.toString(), ".").assertSuccess(); - Path barClasses = MODULE_CLASSES.resolve(BAR.moduleName); Path barJar = mp.resolve(BAR.moduleName + ".jar"); jar("--create", "--file=" + barJar.toString(), "--main-class=" + BAR.mainClass, "--module-version=" + BAR.version, - "--modulepath=" + mp.toString(), - "--hash-dependencies=" + "foo", // dependency on foo "--no-manifest", "-C", barClasses.toString(), ".").assertSuccess(); - // Rebuild foo.jar with a change that will cause its hash to be different - FileUtils.deleteFileWithRetry(fooJar); + Path fooClasses = MODULE_CLASSES.resolve(FOO.moduleName); + Path fooJar = mp.resolve(FOO.moduleName + ".jar"); jar("--create", "--file=" + fooJar.toString(), "--main-class=" + FOO.mainClass, - "--module-version=" + FOO.version + ".1", // a newer version + "--module-version=" + FOO.version, + "--modulepath=" + mp.toString(), + "--hash-modules=" + "bar", "--no-manifest", "-C", fooClasses.toString(), ".").assertSuccess(); + // Rebuild bar.jar with a change that will cause its hash to be different + FileUtils.deleteFileWithRetry(barJar); + jar("--create", + "--file=" + barJar.toString(), + "--main-class=" + BAR.mainClass, + "--module-version=" + BAR.version + ".1", // a newer version + "--no-manifest", + "-C", barClasses.toString(), ".").assertSuccess(); + java(mp, BAR.moduleName + "/" + BAR.mainClass, "-XaddExports:java.base/jdk.internal.module=bar") .assertFailure() .resultChecker(r -> { // Expect similar output: "java.lang.module.ResolutionException: Hash - // of foo (WdktSIQSkd4+CEacpOZoeDrCosMATNrIuNub9b5yBeo=) differs to + // of bar (WdktSIQSkd4+CEacpOZoeDrCosMATNrIuNub9b5yBeo=) differs to // expected hash (iepvdv8xTeVrFgMtUhcFnmetSub6qQHCHc92lSaSEg0=)" - Pattern p = Pattern.compile(".*Hash of foo.*differs to expected hash.*"); + Pattern p = Pattern.compile(".*Hash of bar.*differs to expected hash.*"); assertTrue(p.matcher(r.output).find(), - "Expecting error message containing \"Hash of foo ... differs to" + "Expecting error message containing \"Hash of bar ... differs to" + " expected hash...\" but got: [", r.output + "]"); }); } @@ -454,7 +454,7 @@ jar("--create", "--file=" + modularJar.toString(), - "--hash-dependencies=" + ".*", // no module-info.class + "--hash-modules=" + ".*", // no module-info.class "-C", modClasses.toString(), "jdk") .assertFailure(); // TODO: expected failure message } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jar/modularJar/src/bar/jdk/test/bar/Bar.java --- a/jdk/test/tools/jar/modularJar/src/bar/jdk/test/bar/Bar.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/tools/jar/modularJar/src/bar/jdk/test/bar/Bar.java Tue May 03 12:25:20 2016 -0700 @@ -30,7 +30,7 @@ import java.util.Optional; import java.util.StringJoiner; -import jdk.internal.module.Hasher; +import jdk.internal.module.ModuleHashes; import jdk.test.bar.internal.Message; public class Bar { @@ -43,10 +43,11 @@ Method m = ModuleDescriptor.class.getDeclaredMethod("hashes"); m.setAccessible(true); - Optional optHashes = - (Optional) m.invoke(md); + ModuleDescriptor foo = jdk.test.foo.Foo.class.getModule().getDescriptor(); + Optional oHashes = + (Optional) m.invoke(foo); - System.out.println("hashes:" + optHashes.get().hashFor("foo")); + System.out.println("hashes:" + oHashes.get().hashFor("bar")); StringJoiner sj = new StringJoiner(","); md.conceals().forEach(sj::add); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jlink/ImageFileCreatorTest.java --- a/jdk/test/tools/jlink/ImageFileCreatorTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/tools/jlink/ImageFileCreatorTest.java Tue May 03 12:25:20 2016 -0700 @@ -214,14 +214,12 @@ } @Override - public void storeFiles(Pool content, String bom) { - + public void storeFiles(Pool content) { } - }; ImagePluginStack stack = new ImagePluginStack(noopBuilder, Collections.emptyList(), - null, Collections.emptyList(), ""); + null, Collections.emptyList()); ImageFileCreator.create(archives, ByteOrder.nativeOrder(), stack); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jlink/IntegrationTest.java --- a/jdk/test/tools/jlink/IntegrationTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/tools/jlink/IntegrationTest.java Tue May 03 12:25:20 2016 -0700 @@ -241,7 +241,7 @@ lst.add(new MyPostProcessor()); } // Image builder - DefaultImageBuilder builder = new DefaultImageBuilder(true, output); + DefaultImageBuilder builder = new DefaultImageBuilder(output); PluginsConfiguration plugins = new Jlink.PluginsConfiguration(lst, builder, null); @@ -254,10 +254,6 @@ if (!jimage.exists()) { throw new AssertionError("jimage not generated"); } - File bom = new File(output.toString(), "bom"); - if (!bom.exists()) { - throw new AssertionError("bom not generated"); - } File release = new File(output.toString(), "release"); if (!release.exists()) { throw new AssertionError("release not generated"); @@ -311,7 +307,7 @@ } // Image builder - DefaultImageBuilder builder = new DefaultImageBuilder(false, output); + DefaultImageBuilder builder = new DefaultImageBuilder(output); PluginsConfiguration plugins = new Jlink.PluginsConfiguration(lst, builder, null); @@ -359,7 +355,7 @@ } // Image builder - DefaultImageBuilder builder = new DefaultImageBuilder(false, output); + DefaultImageBuilder builder = new DefaultImageBuilder(output); PluginsConfiguration plugins = new Jlink.PluginsConfiguration(lst, builder, null); boolean failed = false; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jlink/JLink2Test.java --- a/jdk/test/tools/jlink/JLink2Test.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/tools/jlink/JLink2Test.java Tue May 03 12:25:20 2016 -0700 @@ -66,8 +66,6 @@ // This test case must be first one, the JlinkTask is clean // and reveals possible bug related to plugin options in defaults - // e. g.: --genbom - testBomFile(helper); testSameNames(helper); testModulePath(helper); testOptions(); @@ -136,35 +134,6 @@ validator.validate(); } - private static void testBomFile(Helper helper) throws Exception { - String[] userOptions = { - "--compress", - "2", - "--addmods", - "bomzip", - "--strip-debug", - "--genbom", - "--exclude-resources", - "*.jcov,*/META-INF/*"}; - String moduleName = "bomzip"; - helper.generateDefaultJModule(moduleName, "composite2"); - Path imgDir = helper.generateDefaultImage(userOptions, moduleName).assertSuccess(); - helper.checkImage(imgDir, moduleName, userOptions, null, null); - File bom = new File(imgDir.toFile(), "bom"); - if (!bom.exists()) { - throw new RuntimeException(bom.getAbsolutePath() + " not generated"); - } - String bomcontent = new String(Files.readAllBytes(bom.toPath())); - if (!bomcontent.contains("--strip-debug") - || !bomcontent.contains("--compress") - || !bomcontent.contains("--genbom") - || !bomcontent.contains("--exclude-resources *.jcov," - + "*/META-INF/*") - || !bomcontent.contains("--addmods bomzip")) { - throw new Exception("Not expected content in " + bom); - } - } - private static void testOptions() throws Exception { List builtInPlugins = new ArrayList<>(); builtInPlugins.addAll(PluginRepository.getPlugins(Layer.boot())); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jlink/JLinkTest.java --- a/jdk/test/tools/jlink/JLinkTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/tools/jlink/JLinkTest.java Tue May 03 12:25:20 2016 -0700 @@ -204,7 +204,7 @@ String[] userOptions = {"--compress", "invalid"}; String moduleName = "invalidCompressLevel"; helper.generateDefaultJModule(moduleName, "composite2"); - helper.generateDefaultImage(userOptions, moduleName).assertFailure("Error: Invalid level invalid"); + helper.generateDefaultImage(userOptions, moduleName).assertFailure("Error: Invalid compression level invalid"); } // @file diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jlink/hashes/HashesTest.java --- a/jdk/test/tools/jlink/hashes/HashesTest.java Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,160 +0,0 @@ -/** - * Copyright (c) 2015, 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 the recording and checking of dependency hashes - * @author Andrei Eremeev - * @library /lib/testlibrary - * @modules java.base/jdk.internal.module - * jdk.jlink/jdk.tools.jlink - * jdk.jlink/jdk.tools.jmod - * jdk.compiler - * @ignore - * @build jdk.testlibrary.ProcessTools jdk.testlibrary.OutputAnalyzer CompilerUtils - * @run main HashesTest - */ - -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -import jdk.testlibrary.OutputAnalyzer; -import jdk.testlibrary.ProcessTools; - -public class HashesTest { - - private final Path jdkHome = Paths.get(System.getProperty("test.jdk")); - private final Path stdJmods = jdkHome.resolve("jmods"); - private final Path testSrc = Paths.get(System.getProperty("test.src")); - private final Path modSrc = testSrc.resolve("src"); - private final Path newModSrc = testSrc.resolve("newsrc"); - private final Path classes = Paths.get("classes"); - private final Path jmods = Paths.get("jmods"); - - public static void main(String[] args) throws Exception { - new HashesTest().run(); - } - - private void run() throws Exception { - if (!Files.exists(stdJmods)) { - return; - } - Files.createDirectories(jmods); - Path m1Classes = classes.resolve("m1"); - Path m2Classes = classes.resolve("m2"); - Path m3Classes = classes.resolve("not_matched"); - // build the second module - compileClasses(modSrc, m2Classes); - runJmod(m2Classes.toString(), m2Classes.getFileName().toString()); - - // build the third module - compileClasses(modSrc, m3Classes); - runJmod(m3Classes.toString(), m3Classes.getFileName().toString()); - - compileClasses(modSrc, m1Classes, "-mp", jmods.toString()); - runJmod(m1Classes.toString(), m1Classes.getFileName().toString(), - "--modulepath", jmods.toString(), "--hash-dependencies", "m2"); - runJava(0, "-mp", jmods.toString(), "-m", "m1/org.m1.Main"); - - deleteDirectory(m3Classes); - Files.delete(jmods.resolve("not_matched.jmod")); - - // build the new third module - compileClasses(newModSrc, m3Classes); - runJmod(m3Classes.toString(), m3Classes.getFileName().toString()); - runJava(0, "-mp", jmods.toString(), "-m", "m1/org.m1.Main"); - - deleteDirectory(m2Classes); - Files.delete(jmods.resolve("m2.jmod")); - - compileClasses(newModSrc, m2Classes); - runJmod(m2Classes.toString(), m2Classes.getFileName().toString()); - - runJava(1, "-mp", jmods.toString(), "-m", "m1/org.m1.Main"); - - if (jdk.tools.jlink.internal.Main.run(new String[]{ - "--modulepath", stdJmods.toString() + File.pathSeparator + jmods.toString(), - "--addmods", "m1", "--output", "myimage"}, new PrintWriter(System.out)) == 0) { - throw new AssertionError("Expected failure. rc = 0"); - } - } - - private void deleteDirectory(Path dir) throws IOException { - Files.walkFileTree(dir, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - Files.delete(file); - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - Files.delete(dir); - return FileVisitResult.CONTINUE; - } - }); - } - - private void runJava(int expectedExitCode, String... args) throws Exception { - OutputAnalyzer analyzer = ProcessTools.executeTestJava(args); - if (analyzer.getExitValue() != expectedExitCode) { - throw new AssertionError("Expected exit code: " + expectedExitCode + - ", got: " + analyzer.getExitValue()); - } - } - - private void compileClasses(Path src, Path output, String... options) throws IOException { - List args = new ArrayList<>(); - Collections.addAll(args, options); - Collections.addAll(args, "-d", output.toString()); - args.add(src.toString()); - System.out.println("javac options: " + args.stream().collect(Collectors.joining(" "))); - if (!CompilerUtils.compile(src.resolve(output.getFileName()), output, options)) { - throw new AssertionError("Compilation failure. See log."); - } - } - - private void runJmod(String cp, String modName, String... options) { - List args = new ArrayList<>(); - args.add("create"); - Collections.addAll(args, options); - Collections.addAll(args, "--class-path", cp, - jmods + File.separator + modName + ".jmod"); - int rc = jdk.tools.jmod.Main.run(args.toArray(new String[args.size()]), System.out); - System.out.println("jmod options: " + args.stream().collect(Collectors.joining(" "))); - if (rc != 0) { - throw new AssertionError("Jmod failed: rc = " + rc); - } - } -} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jlink/hashes/newsrc/m2/module-info.java --- a/jdk/test/tools/jlink/hashes/newsrc/m2/module-info.java Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2015, 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 m2 { - exports org.m2; -} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jlink/hashes/newsrc/m2/org/m2/Util.java --- a/jdk/test/tools/jlink/hashes/newsrc/m2/org/m2/Util.java Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2015, 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 org.m2; - -public class Util { - private Util() { } - - public static String timeOfDay() { - return "Time for a beer"; - } -} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jlink/hashes/newsrc/not_matched/module-info.java --- a/jdk/test/tools/jlink/hashes/newsrc/not_matched/module-info.java Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2015, 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 not_matched { - exports org.not_matched; -} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jlink/hashes/newsrc/not_matched/org/not_matched/Name.java --- a/jdk/test/tools/jlink/hashes/newsrc/not_matched/org/not_matched/Name.java Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2015, 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 org.not_matched; - -public class Name { - private Name() { } - - public static String name() { - return "new_module"; - } -} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jlink/hashes/src/m1/module-info.java --- a/jdk/test/tools/jlink/hashes/src/m1/module-info.java Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2015, 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 m1 { - requires m2; - requires not_matched; -} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jlink/hashes/src/m1/org/m1/Main.java --- a/jdk/test/tools/jlink/hashes/src/m1/org/m1/Main.java Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2015, 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 org.m1; - -import org.m2.Util; -import org.not_matched.Name; - -public class Main { - public static void main(String[] args) { - System.out.println(Util.timeOfDay()); - System.out.println(Name.name()); - } -} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jlink/hashes/src/m2/module-info.java --- a/jdk/test/tools/jlink/hashes/src/m2/module-info.java Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2015, 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 m2 { - exports org.m2; -} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jlink/hashes/src/m2/org/m2/Util.java --- a/jdk/test/tools/jlink/hashes/src/m2/org/m2/Util.java Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2015, 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 org.m2; - -public class Util { - private Util() { } - - public static String timeOfDay() { - return "Time for lunch"; - } -} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jlink/hashes/src/not_matched/module-info.java --- a/jdk/test/tools/jlink/hashes/src/not_matched/module-info.java Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2015, 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 not_matched { - exports org.not_matched; -} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jlink/hashes/src/not_matched/org/not_matched/Name.java --- a/jdk/test/tools/jlink/hashes/src/not_matched/org/not_matched/Name.java Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2015, 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 org.not_matched; - -public class Name { - private Name() { } - - public static String name() { - return "old_module"; - } -} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jlink/plugins/FileCopierPluginTest.java --- a/jdk/test/tools/jlink/plugins/FileCopierPluginTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/tools/jlink/plugins/FileCopierPluginTest.java Tue May 03 12:25:20 2016 -0700 @@ -101,9 +101,8 @@ } Path root = new File(".").toPath(); - DefaultImageBuilder imgbuilder = new DefaultImageBuilder(false, - root); - imgbuilder.storeFiles(pool, ""); + DefaultImageBuilder imgbuilder = new DefaultImageBuilder(root); + imgbuilder.storeFiles(pool); if (lic.exists()) { File license = new File(root.toFile(), "LICENSE"); diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jmod/JmodNegativeTest.java --- a/jdk/test/tools/jmod/JmodNegativeTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/tools/jmod/JmodNegativeTest.java Tue May 03 12:25:20 2016 -0700 @@ -76,7 +76,7 @@ jmod() .assertFailure() .resultChecker(r -> - assertContains(r.output, "Error: one of create, list, or describe must be specified") + assertContains(r.output, "Error: one of create, list, describe, or hash must be specified") ); } @@ -85,7 +85,7 @@ jmod("badAction") .assertFailure() .resultChecker(r -> - assertContains(r.output, "Error: mode must be one of create, list, or describe") + assertContains(r.output, "Error: mode must be one of create, list, describe, or hash") ); jmod("--badOption") @@ -170,14 +170,14 @@ } @Test - public void testHashDependenciesModulePathNotSpecified() { + public void testHashModulesModulePathNotSpecified() { jmod("create", - "--hash-dependencies", "anyPattern.*", + "--hash-modules", "anyPattern.*", "output.jmod") .assertFailure() .resultChecker(r -> assertContains(r.output, "Error: --module-path must be " - +"specified when hashing dependencies") + +"specified when hashing modules") ); } @@ -317,7 +317,7 @@ } @Test - public void testDependencyNotFound() throws IOException { + public void testNoModuleHash() throws IOException { Path jmod = MODS_DIR.resolve("output.jmod"); FileUtils.deleteFileIfExistsWithRetry(jmod); Path emptyDir = Paths.get("empty"); @@ -328,13 +328,12 @@ jmod("create", "--class-path", cp, - "--hash-dependencies", ".*", + "--hash-modules", ".*", "--modulepath", emptyDir.toString(), jmod.toString()) - .assertFailure() .resultChecker(r -> - assertContains(r.output, "Hashing module foo dependencies, " - + "unable to find module java.base on module path") + assertContains(r.output, "No hashes recorded: " + + "no module specified for hashing depends on foo") ); } @@ -350,13 +349,10 @@ jmod("create", "--class-path", cp, - "--hash-dependencies", ".*", + "--hash-modules", ".*", "--modulepath", MODS_DIR.toString(), jmod.toString()) - .assertFailure() - .resultChecker(r -> - assertContains(r.output, "Error: error reading module path") - ); + .assertFailure(); } finally { FileUtils.deleteFileWithRetry(empty); } @@ -371,7 +367,7 @@ Files.createFile(file); jmod("create", - "--hash-dependencies", ".*", + "--hash-modules", ".*", "--modulepath", file.toString(), jmod.toString()) .assertFailure() @@ -388,7 +384,7 @@ List> tasks = Arrays.asList( () -> jmod("create", - "--hash-dependencies", "anyPattern", + "--hash-modules", "anyPattern", "--modulepath", "doesNotExist", "output.jmod"), () -> jmod("create", @@ -436,7 +432,7 @@ List> tasks = Arrays.asList( () -> jmod("create", - "--hash-dependencies", "anyPattern", + "--hash-modules", "anyPattern", "--modulepath","empty" + pathSeparator + "doesNotExist", "output.jmod"), () -> jmod("create", diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jmod/hashes/HashesTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/HashesTest.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,213 @@ +/** + * Copyright (c) 2015, 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 the recording and checking of module hashes + * @author Andrei Eremeev + * @library /lib/testlibrary + * @modules java.base/jdk.internal.module + * jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jmod + * jdk.compiler + * @build CompilerUtils + * @run testng HashesTest + */ + +import java.io.IOException; +import java.io.InputStream; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReader; +import java.lang.module.ModuleReference; +import java.lang.reflect.Method; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import jdk.internal.module.ConfigurableModuleFinder; +import jdk.internal.module.ModuleHashes; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +public class HashesTest { + + private final Path testSrc = Paths.get(System.getProperty("test.src")); + private final Path modSrc = testSrc.resolve("src"); + private final Path mods = Paths.get("mods"); + private final Path jmods = Paths.get("jmods"); + private final String[] modules = new String[] { "m1", "m2", "m3"}; + + private static Method hashesMethod; + @BeforeTest + private void setup() throws Exception { + if (Files.exists(jmods)) { + deleteDirectory(jmods); + } + Files.createDirectories(jmods); + + // build m2, m3 required by m1 + compileModule("m2", modSrc); + jmod("m2"); + + compileModule("m3", modSrc); + jmod("m3"); + + // build m1 + compileModule("m1", modSrc); + // no hash is recorded since m1 has outgoing edges + jmod("m1", "--modulepath", jmods.toString(), "--hash-modules", ".*"); + + // compile org.bar and org.foo + compileModule("org.bar", modSrc); + compileModule("org.foo", modSrc); + + try { + hashesMethod = ModuleDescriptor.class.getDeclaredMethod("hashes"); + hashesMethod.setAccessible(true); + } catch (ReflectiveOperationException x) { + throw new InternalError(x); + } + } + + @Test + public void test() throws Exception { + for (String mn : modules) { + assertFalse(hashes(mn).isPresent()); + } + + // hash m1 in m2 + jmod("m2", "--modulepath", jmods.toString(), "--hash-modules", "m1"); + checkHashes(hashes("m2").get(), "m1"); + + // hash m1 in m2 + jmod("m2", "--modulepath", jmods.toString(), "--hash-modules", ".*"); + checkHashes(hashes("m2").get(), "m1"); + + // create m2.jmod with no hash + jmod("m2"); + // run jmod hash command to hash m1 in m2 and m3 + runJmod(Arrays.asList("hash", "--modulepath", jmods.toString(), + "--hash-modules", ".*")); + checkHashes(hashes("m2").get(), "m1"); + checkHashes(hashes("m3").get(), "m1"); + + jmod("org.bar"); + jmod("org.foo"); + + jmod("org.bar", "--modulepath", jmods.toString(), "--hash-modules", "org.*"); + checkHashes(hashes("org.bar").get(), "org.foo"); + + jmod("m3", "--modulepath", jmods.toString(), "--hash-modules", ".*"); + checkHashes(hashes("m3").get(), "org.foo", "org.bar", "m1"); + } + + private void checkHashes(ModuleHashes hashes, String... hashModules) { + assertTrue(hashes.names().equals(Set.of(hashModules))); + } + + private Optional hashes(String name) throws Exception { + ModuleFinder finder = ModuleFinder.of(jmods.resolve(name + ".jmod")); + if (finder instanceof ConfigurableModuleFinder) { + ((ConfigurableModuleFinder) finder) + .configurePhase(ConfigurableModuleFinder.Phase.LINK_TIME); + } + ModuleReference mref = finder.find(name).orElseThrow(RuntimeException::new); + ModuleReader reader = mref.open(); + try (InputStream in = reader.open("module-info.class").get()) { + ModuleDescriptor md = ModuleDescriptor.read(in); + Optional hashes = + (Optional) hashesMethod.invoke(md); + System.out.format("hashes in module %s %s%n", name, + hashes.isPresent() ? "present" : "absent"); + if (hashes.isPresent()) { + hashes.get().names().stream() + .sorted() + .forEach(n -> System.out.format(" %s %s%n", n, hashes.get().hashFor(n))); + } + return hashes; + } finally { + reader.close(); + } + } + + private void deleteDirectory(Path dir) throws IOException { + Files.walkFileTree(dir, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException + { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) + throws IOException + { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + } + + private void compileModule(String moduleName, Path src) throws IOException { + Path msrc = src.resolve(moduleName); + assertTrue(CompilerUtils.compile(msrc, mods, "-modulesourcepath", src.toString())); + } + + private void jmod(String moduleName, String... options) throws IOException { + Path mclasses = mods.resolve(moduleName); + Path outfile = jmods.resolve(moduleName + ".jmod"); + List args = new ArrayList<>(); + args.add("create"); + Collections.addAll(args, options); + Collections.addAll(args, "--class-path", mclasses.toString(), + outfile.toString()); + + if (Files.exists(outfile)) + Files.delete(outfile); + + runJmod(args); + } + + private void runJmod(List args) { + int rc = jdk.tools.jmod.Main.run(args.toArray(new String[args.size()]), System.out); + System.out.println("jmod options: " + args.stream().collect(Collectors.joining(" "))); + if (rc != 0) { + throw new AssertionError("Jmod failed: rc = " + rc); + } + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jmod/hashes/src/m1/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/src/m1/module-info.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2015, 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 m1 { + requires m2; + requires m3; +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jmod/hashes/src/m1/org/m1/Main.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/src/m1/org/m1/Main.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015, 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 org.m1; + +import org.m2.Util; +import org.m3.Name; + +public class Main { + public static void main(String[] args) { + System.out.println(Util.timeOfDay()); + System.out.println(Name.name()); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jmod/hashes/src/m2/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/src/m2/module-info.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015, 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 m2 { + exports org.m2; +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jmod/hashes/src/m2/org/m2/Util.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/src/m2/org/m2/Util.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2015, 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 org.m2; + +public class Util { + private Util() { } + + public static String timeOfDay() { + return "Time for lunch"; + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jmod/hashes/src/m3/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/src/m3/module-info.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015, 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 { + exports org.m3; +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jmod/hashes/src/m3/org/m3/Name.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/src/m3/org/m3/Name.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2015, 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 org.m3; + +public class Name { + private Name() { } + + public static String name() { + return "m3"; + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jmod/hashes/src/org.bar/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/src/org.bar/module-info.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,26 @@ +/* + * 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. + * + * 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 org.bar { + requires public m1; +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/jmod/hashes/src/org.foo/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/src/org.foo/module-info.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,26 @@ +/* + * 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. + * + * 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 org.foo { + requires public org.bar; +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/launcher/ToolsOpts.java --- a/jdk/test/tools/launcher/ToolsOpts.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/tools/launcher/ToolsOpts.java Tue May 03 12:25:20 2016 -0700 @@ -151,29 +151,26 @@ init(); TestResult tr; int jpos = -1; + String xPatch = "-J-Xpatch:jdk.compiler=jdk.compiler"; for (String arg[] : optionPatterns) { jpos = indexOfJoption(arg); //Build a cmd string for output in results reporting. - String cmdString = javacCmd + " -J-Xpatch:."; + String cmdString = javacCmd + " " + xPatch; for (String opt : arg) { cmdString = cmdString.concat(" " + opt); } switch (arg.length) { case 1: - tr = doExec(javacCmd, "-J-Xpatch:.", - arg[0]); + tr = doExec(javacCmd, xPatch, arg[0]); break; case 2: - tr = doExec(javacCmd, "-J-Xpatch:.", - arg[0], arg[1]); + tr = doExec(javacCmd, xPatch, arg[0], arg[1]); break; case 3: - tr = doExec(javacCmd, "-J-Xpatch:.", - arg[0], arg[1], arg[2]); + tr = doExec(javacCmd, xPatch, arg[0], arg[1], arg[2]); break; case 4: - tr = doExec(javacCmd, "-J-Xpatch:.", - arg[0], arg[1], arg[2], arg[3]); + tr = doExec(javacCmd, xPatch, arg[0], arg[1], arg[2], arg[3]); break; default: tr = null; diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/launcher/modules/addmods/AddModsTest.java --- a/jdk/test/tools/launcher/modules/addmods/AddModsTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/tools/launcher/modules/addmods/AddModsTest.java Tue May 03 12:25:20 2016 -0700 @@ -47,120 +47,179 @@ private static final String TEST_SRC = System.getProperty("test.src"); private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); - private static final Path MODS_DIR = Paths.get("mods"); - - // the module name of the library module - private static final String LIB_MODULE = "lib"; + private static final Path MODS1_DIR = Paths.get("mods1"); + private static final Path MODS2_DIR = Paths.get("mods2"); - // application source directory - private static final String APP_SRC = "app"; + // test module / main class + private static final String TEST_MODULE = "test"; + private static final String TEST_MAIN_CLASS = "test.Main"; + private static final String TEST_MID = TEST_MODULE + "/" + TEST_MAIN_CLASS; - // application is compiled to classes - private static final Path CLASSES_DIR = Paths.get("classes"); - - // application main class - private static final String MAIN_CLASS = "app.Main"; + // logger module + private static final String LOGGER_MODULE = "logger"; @BeforeTest public void compile() throws Exception { - - // javac -d mods/$LIB_MODULE src/$LIB_MODULE/** + // javac -d mods1/test src/test/** boolean compiled = CompilerUtils.compile( - SRC_DIR.resolve(LIB_MODULE), - MODS_DIR.resolve(LIB_MODULE) + SRC_DIR.resolve(TEST_MODULE), + MODS1_DIR.resolve(TEST_MODULE) ); - assertTrue(compiled, "library module did not compile"); + assertTrue(compiled, "test did not compile"); - // javac -d classes -mp mods src/$APP_DIR/** - compiled = CompilerUtils.compile( - SRC_DIR.resolve(APP_SRC), - CLASSES_DIR, - "-mp", MODS_DIR.toString(), - "-addmods", LIB_MODULE + // javac -d mods1/logger src/logger/** + compiled= CompilerUtils.compile( + SRC_DIR.resolve(LOGGER_MODULE), + MODS2_DIR.resolve(LOGGER_MODULE) ); - assertTrue(compiled, "app did not compile"); + assertTrue(compiled, "test did not compile"); } /** - * Basic test of -addmods ALL-SYSTEM, using the output of -listmods to - * check that the a sample of the system modules are resolved. + * Basic test of -addmods ALL-DEFAULT. Module java.sql should be + * resolved and the types in that module should be visible. */ - public void testAddSystemModules() throws Exception { - - executeTestJava("-addmods", "ALL-SYSTEM", - "-listmods", - "-m", "java.base") - .outputTo(System.out) - .errorTo(System.out) - .shouldContain("java.sql") - .shouldContain("java.corba"); + public void testAddDefaultModules1() throws Exception { - // no exit value to check as -m java.base will likely fail - } - - - /** - * Run application on class path that makes use of module on the - * application module path. Uses {@code -addmods lib} - */ - public void testRunWithAddMods() throws Exception { - - // java -mp mods -addmods lib -cp classes app.Main + // java -addmods ALL-DEFAULT -mp mods1 -m test ... int exitValue - = executeTestJava("-mp", MODS_DIR.toString(), - "-addmods", LIB_MODULE, - "-cp", CLASSES_DIR.toString(), - MAIN_CLASS) + = executeTestJava("-mp", MODS1_DIR.toString(), + "-addmods", "ALL-DEFAULT", + "-m", TEST_MID, + "java.sql.Connection") .outputTo(System.out) .errorTo(System.out) .getExitValue(); assertTrue(exitValue == 0); - } /** - * Run application on class path that makes use of module on the - * application module path. Uses {@code -addmods ALL-MODULE-PATH}. + * Basic test of -addmods ALL-DEFAULT. Module java.annotations.common + * should not resolved and so the types in that module should not be + * visible. */ - public void testAddAllModulePath() throws Exception { + public void testAddDefaultModules2() throws Exception { + + // java -addmods ALL-DEFAULT -mp mods1 -m test ... + int exitValue + = executeTestJava("-mp", MODS1_DIR.toString(), + "-addmods", "ALL-DEFAULT", + "-m", TEST_MID, + "javax.annotation.Generated") + .outputTo(System.out) + .errorTo(System.out) + .shouldContain("ClassNotFoundException") + .getExitValue(); - // java -mp mods -addmods lib -cp classes app.Main + assertTrue(exitValue != 0); + } + + /** + * Basic test of -addmods ALL-SYSTEM. All system modules should be resolved + * and thus all types in those modules should be visible. + */ + public void testAddSystemModules() throws Exception { + + // java -addmods ALL-SYSTEM -mp mods1 -m test ... int exitValue - = executeTestJava("-mp", MODS_DIR.toString(), - "-addmods", "ALL-MODULE-PATH", - "-cp", CLASSES_DIR.toString(), - MAIN_CLASS) + = executeTestJava("-mp", MODS1_DIR.toString(), + "-addmods", "ALL-SYSTEM", + "-m", TEST_MID, + "java.sql.Connection", + "javax.annotation.Generated") .outputTo(System.out) .errorTo(System.out) .getExitValue(); assertTrue(exitValue == 0); - } /** - * Run application on class path that makes use of module on the - * application module path. Does not use -addmods and so will - * fail at run-time. + * Run test on class path to load a type in a module on the application + * module path, uses {@code -addmods logger}. */ - public void testRunMissingAddMods() throws Exception { + public void testRunWithAddMods() throws Exception { - // java -mp mods -cp classes app.Main + // java -mp mods -addmods logger -cp classes test.Main + String classpath = MODS1_DIR.resolve(TEST_MODULE).toString(); + String modulepath = MODS2_DIR.toString(); int exitValue - = executeTestJava("-mp", MODS_DIR.toString(), - "-cp", CLASSES_DIR.toString(), - MAIN_CLASS) + = executeTestJava("-mp", modulepath, + "-addmods", LOGGER_MODULE, + "-cp", classpath, + TEST_MAIN_CLASS, + "logger.Logger") .outputTo(System.out) .errorTo(System.out) .getExitValue(); - // CNFE or other error/exception - assertTrue(exitValue != 0); + assertTrue(exitValue == 0); + } + + /** + * Run application on class path that makes use of module on the + * application module path. Does not use -addmods and so should + * fail at run-time. + */ + public void testRunMissingAddMods() throws Exception { + + // java -mp mods -cp classes test.Main + String classpath = MODS1_DIR.resolve(TEST_MODULE).toString(); + String modulepath = MODS1_DIR.toString(); + int exitValue + = executeTestJava("-mp", modulepath, + "-cp", classpath, + TEST_MAIN_CLASS, + "logger.Logger") + .outputTo(System.out) + .errorTo(System.out) + .shouldContain("ClassNotFoundException") + .getExitValue(); + + assertTrue(exitValue != 0); + } + + /** + * Run test on class path to load a type in a module on the application + * module path, uses {@code -addmods ALL-MODULE-PATH}. + */ + public void testAddAllModulePath() throws Exception { + + // java -mp mods -addmods ALL-MODULE-PATH -cp classes test.Main + String classpath = MODS1_DIR.resolve(TEST_MODULE).toString(); + String modulepath = MODS1_DIR.toString(); + int exitValue + = executeTestJava("-mp", modulepath, + "-addmods", "ALL-MODULE-PATH", + "-cp", classpath, + TEST_MAIN_CLASS) + .outputTo(System.out) + .errorTo(System.out) + .getExitValue(); + + assertTrue(exitValue == 0); + } + + + /** + * Test {@code -addmods ALL-MODULE-PATH} without {@code -modulepath}. + */ + public void testAddAllModulePathWithNoModulePath() throws Exception { + + // java -addmods ALL-MODULE-PATH -version + int exitValue + = executeTestJava("-addmods", "ALL-MODULE-PATH", + "-version") + .outputTo(System.out) + .errorTo(System.out) + .getExitValue(); + + assertTrue(exitValue == 0); } @@ -169,18 +228,17 @@ */ public void testRunWithBadAddMods() throws Exception { - // java -mp mods -addmods,DoesNotExist lib -cp classes app.Main + // java -mp mods -addmods DoesNotExist -m test ... int exitValue - = executeTestJava("-mp", MODS_DIR.toString(), - "-addmods", LIB_MODULE + ",DoesNotExist", - "-cp", CLASSES_DIR.toString(), - MAIN_CLASS) + = executeTestJava("-mp", MODS1_DIR.toString(), + "-addmods", "DoesNotExist", + "-m", TEST_MID) .outputTo(System.out) .errorTo(System.out) + .shouldContain("DoesNotExist") .getExitValue(); assertTrue(exitValue != 0); - } } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/launcher/modules/addmods/src/app/Main.java --- a/jdk/test/tools/launcher/modules/addmods/src/app/Main.java Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -/* - * Copyright (c) 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. - * - * 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 app; - -import jdk.lib.Util; - -public class Main { - public static void main(String[] args) { - Object obj = Util.makeObject(); - } -} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/launcher/modules/addmods/src/lib/jdk/lib/Util.java --- a/jdk/test/tools/launcher/modules/addmods/src/lib/jdk/lib/Util.java Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -/* - * Copyright (c) 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. - * - * 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.lib; - -public class Util { - private Util() { } - - public static Object makeObject() { - return new Object(); - } -} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/launcher/modules/addmods/src/lib/module-info.java --- a/jdk/test/tools/launcher/modules/addmods/src/lib/module-info.java Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -/* - * Copyright (c) 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. - * - * 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 lib { - exports jdk.lib; -} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/launcher/modules/addmods/src/logger/logger/Logger.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/launcher/modules/addmods/src/logger/logger/Logger.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,30 @@ +/* + * 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. + * + * 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 logger; + +/** + * No-op user module for use by the {@code java -addmods} tests. + */ +public class Logger { +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/launcher/modules/addmods/src/logger/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/launcher/modules/addmods/src/logger/module-info.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,24 @@ +/** + * 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. + * + * 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 logger { } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/launcher/modules/addmods/src/test/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/launcher/modules/addmods/src/test/module-info.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,24 @@ +/** + * 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. + * + * 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 test { } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/launcher/modules/addmods/src/test/test/Main.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/launcher/modules/addmods/src/test/test/Main.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,37 @@ +/* + * 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. + * + * 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 test; + +/** + * Invoked by tests for the {@code java -addmods} option to check that types + * are visible. + */ +public class Main { + public static void main(String[] args) throws Exception { + for (String cn : args) { + Class c = Class.forName(cn); + System.out.println("Loaded: " + c); + } + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/launcher/modules/addreads/AddReadsTest.java --- a/jdk/test/tools/launcher/modules/addreads/AddReadsTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/tools/launcher/modules/addreads/AddReadsTest.java Tue May 03 12:25:20 2016 -0700 @@ -25,7 +25,7 @@ * @test * @library /lib/testlibrary * @modules jdk.compiler - * @build AddReadsTest CompilerUtils jdk.testlibrary.* + * @build AddReadsTest CompilerUtils JarUtils jdk.testlibrary.* * @run testng AddReadsTest * @summary Basic tests for java -XaddReads */ diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/launcher/modules/patch/PatchTest.java --- a/jdk/test/tools/launcher/modules/patch/PatchTest.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/tools/launcher/modules/patch/PatchTest.java Tue May 03 12:25:20 2016 -0700 @@ -25,7 +25,7 @@ * @test * @library /lib/testlibrary * @modules jdk.compiler - * @build PatchTest CompilerUtils jdk.testlibrary.* + * @build PatchTest CompilerUtils JarUtils jdk.testlibrary.* * @run testng PatchTest * @summary Basic test for -Xpatch */ @@ -72,6 +72,9 @@ private static final Path SRC2_DIR = Paths.get(TEST_SRC, "src2"); private static final Path PATCHES2_DIR = Paths.get("patches2"); + // destination directory for patches packaged as JAR files + private static final Path PATCHES_DIR = Paths.get("patches"); + // the classes overridden or added with -Xpatch private static final String[] CLASSES = { @@ -95,7 +98,7 @@ @BeforeTest - public void compile() throws Exception { + public void setup() throws Exception { // javac -d mods/test src/test/** boolean compiled= CompilerUtils.compile(SRC_DIR.resolve("test"), @@ -103,36 +106,40 @@ assertTrue(compiled, "classes did not compile"); // javac -Xmodule:$MODULE -d patches1/$MODULE patches1/$MODULE/** + // jar cf patches/$MODULE-1.jar -C patches1/$MODULE . for (Path src : Files.newDirectoryStream(SRC1_DIR)) { Path output = PATCHES1_DIR.resolve(src.getFileName()); String mn = src.getFileName().toString(); compiled = CompilerUtils.compile(src, output, "-Xmodule:" + mn); assertTrue(compiled, "classes did not compile"); + JarUtils.createJarFile(PATCHES_DIR.resolve(mn + "-1.jar"), output); } // javac -Xmodule:$MODULE -d patches2/$MODULE patches2/$MODULE/** + // jar cf patches/$MODULE-2.jar -C patches2/$MODULE . for (Path src : Files.newDirectoryStream(SRC2_DIR)) { Path output = PATCHES2_DIR.resolve(src.getFileName()); String mn = src.getFileName().toString(); compiled = CompilerUtils.compile(src, output, "-Xmodule:" + mn); assertTrue(compiled, "classes did not compile"); + JarUtils.createJarFile(PATCHES_DIR.resolve(mn + "-2.jar"), output); } } /** - * Run the test with -Xpatch + * Run test with patches to java.base, jdk.naming.dns and jdk.compiler */ - public void testRunWithXPatch() throws Exception { - - // value for -Xpatch - String patchPath = PATCHES1_DIR + File.pathSeparator + PATCHES2_DIR; - + void runTest(String basePatches, String dnsPatches, String compilerPatches) + throws Exception + { // the argument to the test is the list of classes overridden or added String arg = Stream.of(CLASSES).collect(Collectors.joining(",")); int exitValue - = executeTestJava("-Xpatch:" + patchPath, + = executeTestJava("-Xpatch:java.base=" + basePatches, + "-Xpatch:jdk.naming.dns=" + dnsPatches, + "-Xpatch:jdk.compiler=" + compilerPatches, "-XaddExports:java.base/java.lang2=test", "-XaddExports:jdk.naming.dns/com.sun.jndi.dns=test", "-XaddExports:jdk.naming.dns/com.sun.jndi.dns2=test", @@ -145,6 +152,44 @@ .getExitValue(); assertTrue(exitValue == 0); + } + + + /** + * Run test with -Xpatch and exploded patches + */ + public void testWithExplodedPatches() throws Exception { + + // patches1/java.base:patches2/java.base + String basePatches = PATCHES1_DIR.resolve("java.base") + + File.pathSeparator + PATCHES2_DIR.resolve("java.base"); + + String dnsPatches = PATCHES1_DIR.resolve("jdk.naming.dns") + + File.pathSeparator + PATCHES2_DIR.resolve("jdk.naming.dns"); + + String compilerPatches = PATCHES1_DIR.resolve("jdk.compiler") + + File.pathSeparator + PATCHES2_DIR.resolve("jdk.compiler"); + + runTest(basePatches, dnsPatches, compilerPatches); + } + + + /** + * Run test with -Xpatch and patches in JAR files + */ + public void testWitJarPatches() throws Exception { + + // patches/java.base-1.jar:patches/java-base-2.jar + String basePatches = PATCHES_DIR.resolve("java.base-1.jar") + + File.pathSeparator + PATCHES_DIR.resolve("java.base-2.jar"); + + String dnsPatches = PATCHES_DIR.resolve("jdk.naming.dns-1.jar") + + File.pathSeparator + PATCHES_DIR.resolve("jdk.naming.dns-2.jar"); + + String compilerPatches = PATCHES_DIR.resolve("jdk.compiler-1.jar") + + File.pathSeparator + PATCHES_DIR.resolve("jdk.compiler-2.jar"); + + runTest(basePatches, dnsPatches, compilerPatches); } diff -r 8ff4232c93c2 -r 74cd426ebb3d jdk/test/tools/lib/tests/JImageGenerator.java --- a/jdk/test/tools/lib/tests/JImageGenerator.java Tue May 03 09:48:02 2016 -0700 +++ b/jdk/test/tools/lib/tests/JImageGenerator.java Tue May 03 12:25:20 2016 -0700 @@ -113,7 +113,7 @@ private static final String CMDS_OPTION = "--cmds"; private static final String CONFIG_OPTION = "--config"; - private static final String HASH_DEPENDENCIES_OPTION = "--hash-dependencies"; + private static final String HASH_MODULES_OPTION = "--hash-modules"; private static final String LIBS_OPTION = "--libs"; private static final String MODULE_VERSION_OPTION = "--module-version"; @@ -347,7 +347,7 @@ private final List jmods = new ArrayList<>(); private final List options = new ArrayList<>(); private Path output; - private String hashDependencies; + private String hashModules; private String mainClass; private String moduleVersion; @@ -356,8 +356,8 @@ return this; } - public JModTask hashDependencies(String hash) { - this.hashDependencies = hash; + public JModTask hashModules(String hash) { + this.hashModules = hash; return this; } @@ -430,9 +430,9 @@ options.add(CONFIG_OPTION); options.add(toPath(config)); } - if (hashDependencies != null) { - options.add(HASH_DEPENDENCIES_OPTION); - options.add(hashDependencies); + if (hashModules != null) { + options.add(HASH_MODULES_OPTION); + options.add(hashModules); } if (mainClass != null) { options.add(MAIN_CLASS_OPTION); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/.hgtags --- a/langtools/.hgtags Tue May 03 09:48:02 2016 -0700 +++ b/langtools/.hgtags Tue May 03 12:25:20 2016 -0700 @@ -358,3 +358,4 @@ 4e87682893e662421af10a62d29ae822ce0fea04 jdk-9+113 cba09a2e6ae969b029783eb59bb01017b78f8eef jdk-9+114 31c8b18fdc5b94a2ddd5ea0694f350a2c907e9f7 jdk-9+115 +3e3553ee39d9e081573bc7c88a252214a3152763 jdk-9+116 diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/java.compiler/share/classes/javax/tools/ToolProvider.java --- a/langtools/src/java.compiler/share/classes/javax/tools/ToolProvider.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/java.compiler/share/classes/javax/tools/ToolProvider.java Tue May 03 12:25:20 2016 -0700 @@ -123,7 +123,9 @@ private static T getSystemTool(Class clazz, String moduleName, String className) { if (useLegacy) { try { - return Class.forName(className, true, ClassLoader.getSystemClassLoader()).asSubclass(clazz).newInstance(); + @SuppressWarnings("deprecation") + T result = Class.forName(className, true, ClassLoader.getSystemClassLoader()).asSubclass(clazz).newInstance(); + return result; } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java Tue May 03 12:25:20 2016 -0700 @@ -698,7 +698,7 @@ */ public boolean packageExists(ModuleSymbol msym, Name fullname) { Assert.checkNonNull(msym); - return enterPackage(msym, fullname).exists(); + return lookupPackage(msym, fullname).exists(); } /** Make a package, given its fully qualified name. diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java Tue May 03 12:25:20 2016 -0700 @@ -53,10 +53,6 @@ public class MemberEnter extends JCTree.Visitor { protected static final Context.Key memberEnterKey = new Context.Key<>(); - /** A switch to determine whether we check for package/class conflicts - */ - final static boolean checkClash = true; - private final Enter enter; private final Log log; private final Check chk; diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java Tue May 03 12:25:20 2016 -0700 @@ -826,7 +826,7 @@ } public void visitClass(Attribute.Class clazz) { databuf.appendByte('c'); - databuf.appendChar(pool.put(typeSig(clazz.classType))); + databuf.appendChar(pool.put(typeSig(types.erasure(clazz.classType)))); } public void visitCompound(Attribute.Compound compound) { databuf.appendByte('@'); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java Tue May 03 12:25:20 2016 -0700 @@ -1086,17 +1086,19 @@ genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET); code.resolve(loopEnv.info.cont); genStats(step, loopEnv); - CondItem c; - if (cond != null) { - code.statBegin(cond.pos); + if (code.isAlive()) { + CondItem c; + if (cond != null) { + code.statBegin(cond.pos); + Assert.check(code.state.stacksize == 0); + c = genCond(TreeInfo.skipParens(cond), CRT_FLOW_CONTROLLER); + } else { + c = items.makeCondItem(goto_); + } + code.resolve(c.jumpTrue(), startpc); Assert.check(code.state.stacksize == 0); - c = genCond(TreeInfo.skipParens(cond), CRT_FLOW_CONTROLLER); - } else { - c = items.makeCondItem(goto_); + code.resolve(c.falseJumps); } - code.resolve(c.jumpTrue(), startpc); - Assert.check(code.state.stacksize == 0); - code.resolve(c.falseJumps); } Chain exit = loopEnv.info.exit; if (exit != null) { @@ -1647,6 +1649,7 @@ public void visitConditional(JCConditional tree) { Chain thenExit = null; + code.statBegin(tree.cond.pos); CondItem c = genCond(tree.cond, CRT_FLOW_CONTROLLER); Chain elseChain = c.jumpFalse(); if (!c.isFalse()) { diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Tue May 03 12:25:20 2016 -0700 @@ -282,6 +282,7 @@ if (options.isSet(XPRINT)) { try { + @SuppressWarnings("deprecation") Processor processor = PrintingProcessor.class.newInstance(); processorIterator = List.of(processor).iterator(); } catch (Throwable t) { @@ -549,8 +550,9 @@ try { Class processorClass = processorCL.loadClass(processorName); ensureReadable(processorClass); - processor = - (Processor) (processorClass.newInstance()); + @SuppressWarnings("deprecation") + Object tmp = processorClass.newInstance(); + processor = (Processor) tmp; } catch (ClassNotFoundException cnfe) { log.error("proc.processor.not.found", processorName); return false; diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/options/Option.java --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/options/Option.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/options/Option.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -151,6 +151,7 @@ // Construct transformer try { Class trCls = Class.forName(classname); + @SuppressWarnings("deprecation") Transformer transformer = (Transformer) trCls.newInstance(); transformer.setExtra(extra); helper.addTransformer(suffix, transformer); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/resources/standard.properties --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/resources/standard.properties Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/resources/standard.properties Tue May 03 12:25:20 2016 -0700 @@ -1,4 +1,4 @@ -doclet.build_version=Standard Doclet version {0} +doclet.build_version=Standard Doclet (Old) version {0} doclet.Contents=Contents doclet.Overview=Overview doclet.Window_Overview=Overview List diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/taglets/TagletManager.java --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/taglets/TagletManager.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/taglets/TagletManager.java Tue May 03 12:25:20 2016 -0700 @@ -242,7 +242,6 @@ } customTagClass = tagClassLoader.loadClass(classname); - ensureReadable(customTagClass); Method meth = customTagClass.getMethod("register", Map.class); @@ -270,27 +269,6 @@ } /** - * Ensures that the module of the given class is readable to this - * module. - * @param targetClass class in module to be made readable - */ - private void ensureReadable(Class targetClass) { - try { - Method getModuleMethod = Class.class.getMethod("getModule"); - Object thisModule = getModuleMethod.invoke(this.getClass()); - Object targetModule = getModuleMethod.invoke(targetClass); - - Class moduleClass = getModuleMethod.getReturnType(); - Method addReadsMethod = moduleClass.getMethod("addReads", moduleClass); - addReadsMethod.invoke(thisModule, targetModule); - } catch (NoSuchMethodException e) { - // ignore - } catch (Exception e) { - throw new InternalError(e); - } - } - - /** * Export javadoc internal API to the unnamed module for a classloader. * This is to support continued use of existing non-standard doclets that * use the internal toolkit API and related classes. diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/DocletInvoker.java --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/DocletInvoker.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/DocletInvoker.java Tue May 03 12:25:20 2016 -0700 @@ -102,7 +102,6 @@ this.apiMode = apiMode; this.exportInternalAPI = exportInternalAPI; // for backdoor use by standard doclet for taglets - ensureReadable(docletClass); // this may not be soon enough if the class has already been loaded if (exportInternalAPI) { exportInternalAPI(docletClass.getClassLoader()); @@ -149,8 +148,6 @@ messager.exit(); } docletClass = dc; - - ensureReadable(docletClass); } /* @@ -362,27 +359,6 @@ } /** - * Ensures that the module of the given class is readable to this - * module. - * @param targetClass class in module to be made readable - */ - private void ensureReadable(Class targetClass) { - try { - Method getModuleMethod = Class.class.getMethod("getModule"); - Object thisModule = getModuleMethod.invoke(this.getClass()); - Object targetModule = getModuleMethod.invoke(targetClass); - - Class moduleClass = getModuleMethod.getReturnType(); - Method addReadsMethod = moduleClass.getMethod("addReads", moduleClass); - addReadsMethod.invoke(thisModule, targetModule); - } catch (NoSuchMethodException e) { - // ignore - } catch (Exception e) { - throw new InternalError(e); - } - } - - /** * Export javadoc internal API to the unnamed module for a classloader. * This is to support continued use of existing non-standard doclets that * use the internal toolkit API and related classes. diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java Tue May 03 12:25:20 2016 -0700 @@ -168,6 +168,21 @@ } /** + * {@inheritDoc} + */ + public void addModuleDescription(Content moduleContentTree) { + if (!utils.getBody(mdle).isEmpty()) { + Content tree = configuration.allowTag(HtmlTag.SECTION) ? HtmlTree.SECTION() : moduleContentTree; + tree.addContent(HtmlConstants.START_OF_MODULE_DESCRIPTION); + tree.addContent(getMarkerAnchor(SectionName.MODULE_DESCRIPTION)); + addInlineComment(mdle, tree); + if (configuration.allowTag(HtmlTag.SECTION)) { + moduleContentTree.addContent(tree); + } + } + } + + /** * Adds list of packages in the package summary table. Generate link to each package. * * @param packages Packages to which link is to be generated diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SectionName.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SectionName.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SectionName.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -53,6 +53,7 @@ METHOD_DETAIL("method.detail"), METHODS_INHERITANCE("methods.inherited.from.class."), METHOD_SUMMARY("method.summary"), + MODULE_DESCRIPTION("module.description"), NAVBAR_BOTTOM("navbar.bottom"), NAVBAR_BOTTOM_FIRSTROW("navbar.bottom.firstrow"), NAVBAR_TOP("navbar.top"), diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlConstants.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlConstants.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlConstants.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -64,6 +64,12 @@ new Comment("======== END OF BOTTOM NAVBAR ======="); /** + * Marker to identify start of module description. + */ + public static final Content START_OF_MODULE_DESCRIPTION = + new Comment("============ MODULE DESCRIPTION ==========="); + + /** * Marker to identify start of class data. */ public static final Content START_OF_CLASS_DATA = diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties Tue May 03 12:25:20 2016 -0700 @@ -1,4 +1,4 @@ -doclet.build_version=Standard Doclet (Next) version {0} +doclet.build_version=Standard Doclet version {0} doclet.Contents=Contents doclet.Overview=Overview doclet.Window_Overview=Overview List diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/ModuleSummaryWriter.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/ModuleSummaryWriter.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/ModuleSummaryWriter.java Tue May 03 12:25:20 2016 -0700 @@ -74,6 +74,14 @@ public abstract Content getSummaryTree(Content summaryContentTree); /** + * Adds the module description. + * + * @param moduleContentTree the content tree to which the module description + * will be added + */ + public abstract void addModuleDescription(Content moduleContentTree); + + /** * Adds the table of packages to the documentation tree. * * @param packages the set of packages that should be added. diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/ModuleSummaryBuilder.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/ModuleSummaryBuilder.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/ModuleSummaryBuilder.java Tue May 03 12:25:20 2016 -0700 @@ -185,4 +185,17 @@ packageTableSummary, summaryContentTree); } } + + /** + * Build the description for the module. + * + * @param node the XML element that specifies which components to document + * @param moduleContentTree the tree to which the module description will + * be added + */ + public void buildModuleDescription(XMLNode node, Content moduleContentTree) { + if (!configuration.nocomment) { + moduleWriter.addModuleDescription(moduleContentTree); + } + } } diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclet.xml --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclet.xml Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclet.xml Tue May 03 12:25:20 2016 -0700 @@ -30,6 +30,7 @@ + diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletManager.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletManager.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletManager.java Tue May 03 12:25:20 2016 -0700 @@ -246,7 +246,7 @@ } tagClassLoader = fileManager.getClassLoader(TAGLET_PATH); Class customTagClass = tagClassLoader.loadClass(classname); - ensureReadable(customTagClass); + @SuppressWarnings("deprecation") Object instance = customTagClass.newInstance(); Taglet newLegacy = new UserTaglet((jdk.javadoc.doclet.taglet.Taglet)instance); String tname = newLegacy.getName(); @@ -262,27 +262,6 @@ } /** - * Ensures that the module of the given class is readable to this - * module. - * @param targetClass class in module to be made readable - */ - private void ensureReadable(Class targetClass) { - try { - Method getModuleMethod = Class.class.getMethod("getModule"); - Object thisModule = getModuleMethod.invoke(this.getClass()); - Object targetModule = getModuleMethod.invoke(targetClass); - - Class moduleClass = getModuleMethod.getReturnType(); - Method addReadsMethod = moduleClass.getMethod("addReads", moduleClass); - addReadsMethod.invoke(thisModule, targetModule); - } catch (NoSuchMethodException e) { - // ignore - } catch (Exception e) { - throw new InternalError(e.toString()); - } - } - - /** * Add a new SimpleTaglet. If this tag already exists * and the header passed as an argument is null, move tag to the back of the * list. If this tag already exists and the header passed as an argument is diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/IndexBuilder.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/IndexBuilder.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/IndexBuilder.java Tue May 03 12:25:20 2016 -0700 @@ -73,6 +73,7 @@ private final Configuration configuration; private final Utils utils; + private final Comparator comparator; /** * Constructor. Build the index map. @@ -106,6 +107,9 @@ this.classesOnly = classesOnly; this.javafx = configuration.javafx; this.indexmap = new TreeMap<>(); + comparator = classesOnly + ? utils.makeAllClassesComparator() + : utils.makeIndexUseComparator(); buildIndexMap(configuration.root); } @@ -175,7 +179,7 @@ Character.toUpperCase(name.charAt(0)); Character unicode = ch; SortedSet list = indexmap.computeIfAbsent(unicode, - c -> new TreeSet<>(utils.makeIndexUseComparator())); + c -> new TreeSet<>(comparator)); list.add(element); } } diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java Tue May 03 12:25:20 2016 -0700 @@ -1628,6 +1628,7 @@ /** * Comparator for ModuleElements, simply compares the fully qualified names + * @return a Comparator */ public Comparator makeModuleComparator() { return new Utils.ElementComparator() { @@ -1639,7 +1640,28 @@ } /** - * Comparator for PackageElements, simply compares the fully qualified names + * Returns a Comparator for all classes, compares the simple names of + * TypeElement, if equal then the fully qualified names. + * + * @return Comparator + */ + public Comparator makeAllClassesComparator() { + return new Utils.ElementComparator() { + @Override + public int compare(Element e1, Element e2) { + int result = compareNames(e1, e2); + if (result == 0) + result = compareFullyQualifiedNames(e1, e2); + + return result; + } + }; + } + + /** + * Returns a Comparator for packages, by comparing the fully qualified names. + * + * @return a Comparator */ public Comparator makePackageComparator() { return new Utils.ElementComparator() { @@ -1650,6 +1672,10 @@ }; } + /** + * Returns a Comparator for SerialFieldTree. + * @return a Comparator + */ public Comparator makeSerialFieldTreeComparator() { return (SerialFieldTree o1, SerialFieldTree o2) -> { String s1 = o1.getName().toString(); @@ -1659,18 +1685,19 @@ } /** - * Comparator for General Purpose - * @return a ElementComparatorForClassUse + * Returns a general purpose comparator. + * @return a Comparator */ public Comparator makeGeneralPurposeComparator() { return makeClassUseComparator(); } /** - * A Comparator for Overrides and Implements use used on ExecutableElements - * compares the name first, then compares the SimpleName of the enclosing - * class and the FullyQualifiedName of the enclosing class. - * @return + * Returns a Comparator for overrides and implements, + * used primarily on methods, compares the name first, + * then compares the simple names of the enclosing + * TypeElement and the fully qualified name of the enclosing TypeElement. + * @return a Comparator */ public Comparator makeOverrideUseComparator() { return new Utils.ElementComparator() { @@ -1696,21 +1723,24 @@ } /** - * A comparator for index file presentations, and are sorted as follows: + * Returns a Comparator for index file presentations, and are sorted as follows. + * If comparing packages then simply compare the qualified names, otherwise * 1. sort on simple names of entities * 2. if equal, then compare the ElementKind ex: Package, Interface etc. * 3a. if equal and if the type is of ExecutableElement(Constructor, Methods), * a case insensitive comparison of parameter the type signatures * 3b. if equal, case sensitive comparison of the type signatures * 4. finally, if equal, compare the FQNs of the entities + * Iff comparing packages then simply sort on qualified names. * @return a comparator for index file use */ public Comparator makeIndexUseComparator() { return new Utils.ElementComparator() { /** - * Compare two given elements, first sort on names, then on the kinds, - * then on the parameters only if the type is an instance of ExecutableElement, - * the parameters are compared and finally the fully qualified names. + * Compare two given elements, if comparing two packages, return the + * comparison of FullyQualifiedName, first sort on names, then on the + * kinds, then on the parameters only if the type is an ExecutableElement, + * the parameters are compared and finally the qualified names. * * @param e1 - an element. * @param e2 - an element. @@ -1719,10 +1749,7 @@ */ @Override public int compare(Element e1, Element e2) { - int result = compareElementTypeKinds(e1, e2); - if (result != 0) { - return result; - } + int result = 0; if (isPackage(e1) && isPackage(e2)) { return compareFullyQualifiedNames(e1, e2); } @@ -1730,6 +1757,10 @@ if (result != 0) { return result; } + result = compareElementTypeKinds(e1, e2); + if (result != 0) { + return result; + } if (hasParameters(e1)) { List parameters1 = ((ExecutableElement)e1).getParameters(); List parameters2 = ((ExecutableElement)e2).getParameters(); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/DocEnv.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/DocEnv.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/DocEnv.java Tue May 03 12:25:20 2016 -0700 @@ -550,7 +550,7 @@ // Messager should be replaced by a more general // compilation environment. This can probably // subsume DocEnv as well. - messager.exit(); + throw new Messager.ExitJavadoc(); } /** diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Main.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Main.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Main.java Tue May 03 12:25:20 2016 -0700 @@ -59,13 +59,6 @@ * @return The return code. */ public static int execute(String... args) { - // NOTE: the following should be removed when the old doclet - // is removed. - if (args != null && args.length > 0 && "-Xold".equals(args[0])) { - String[] nargs = new String[args.length - 1]; - System.arraycopy(args, 1, nargs, 0, nargs.length); - return com.sun.tools.javadoc.Main.execute(nargs); - } Start jdoc = new Start(); return jdoc.begin(args); } diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Messager.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Messager.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Messager.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -139,7 +139,7 @@ } } - public class ExitJavadoc extends Error { + public static class ExitJavadoc extends Error { private static final long serialVersionUID = 0; } @@ -416,15 +416,6 @@ } } - /** - * Force program exit, e.g., from a fatal error. - *

    - * TODO: This method does not really belong here. - */ - public void exit() { - throw new ExitJavadoc(); - } - private void report(DiagnosticType type, String pos, String msg) { switch (type) { case ERROR: diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java Tue May 03 12:25:20 2016 -0700 @@ -66,6 +66,7 @@ import jdk.javadoc.doclet.DocletEnvironment; import static com.sun.tools.javac.main.Option.*; + /** * Main program of Javadoc. * Previously named "Main". @@ -79,6 +80,12 @@ * @author Neal Gafter (rewrite) */ public class Start extends ToolOption.Helper { + + private static final Class OldStdDoclet = + com.sun.tools.doclets.standard.Standard.class; + + private static final Class StdDoclet = + jdk.javadoc.internal.doclets.standard.Standard.class; /** Context for this invocation. */ private final Context context; @@ -193,18 +200,26 @@ if (foot != null) messager.notice(foot); - if (exit) exit(); + if (exit) + throw new Messager.ExitJavadoc(); } + /** - * Exit - */ - private void exit() { - messager.exit(); - } - - /** - * Main program - external wrapper + * Main program - external wrapper. In order to maintain backward + * CLI compatibility, we dispatch to the old tool or the old doclet's + * Start mechanism, based on the options present on the command line + * with the following precedence: + * 1. presence of -Xold, dispatch to old tool + * 2. doclet variant, if old, dispatch to old Start + * 3. taglet variant, if old, dispatch to old Start + * + * Thus the presence of -Xold switches the tool, soon after command files + * if any, are expanded, this is performed here, noting that the messager + * is available at this point in time. + * The doclet/taglet tests are performed in the begin method, further on, + * this is to minimize argument processing and most importantly the impact + * of class loader creation, needed to detect the doclet/taglet class variants. */ int begin(String... argv) { // Preprocess @file arguments @@ -212,14 +227,18 @@ argv = CommandLine.parse(argv); } catch (FileNotFoundException e) { messager.error("main.cant.read", e.getMessage()); - exit(); + throw new Messager.ExitJavadoc(); } catch (IOException e) { e.printStackTrace(System.err); - exit(); + throw new Messager.ExitJavadoc(); } - List argList = Arrays.asList(argv); - boolean ok = begin(argList, Collections. emptySet()); + if (argv.length > 0 && "-Xold".equals(argv[0])) { + messager.warning("main.legacy_api"); + String[] nargv = Arrays.copyOfRange(argv, 1, argv.length); + return com.sun.tools.javadoc.Main.execute(nargv); + } + boolean ok = begin(Arrays.asList(argv), Collections. emptySet()); return ok ? 0 : 1; } @@ -231,11 +250,11 @@ List opts = new ArrayList<>(); for (String opt: options) opts.add(opt); + return begin(opts, fileObjects); } private boolean begin(List options, Iterable fileObjects) { - fileManager = context.get(JavaFileManager.class); if (fileManager == null) { JavacFileManager.preRegister(context); @@ -244,20 +263,21 @@ ((BaseFileManager) fileManager).autoClose = true; } } - // locale and doclet needs to be determined first + // locale, doclet and maybe taglet, needs to be determined first docletClass = preProcess(fileManager, options); - if (jdk.javadoc.doclet.Doclet.class.isAssignableFrom(docletClass)) { // no need to dispatch to old, safe to init now initMessager(); messager.setLocale(locale); try { - doclet = (Doclet) docletClass.newInstance(); + @SuppressWarnings("deprecation") + Object o = docletClass.newInstance(); + doclet = (Doclet) o; } catch (InstantiationException | IllegalAccessException exc) { exc.printStackTrace(); if (!apiMode) { error("main.could_not_instantiate_class", docletClass); - messager.exit(); + throw new Messager.ExitJavadoc(); } throw new ClientCodeException(exc); } @@ -267,6 +287,7 @@ = new com.sun.tools.javadoc.Start(context); return ostart.begin(docletClass, options, fileObjects); } + warn("main.legacy_api"); String[] array = options.toArray(new String[options.size()]); return com.sun.tools.javadoc.Main.execute(array) == 0; } @@ -312,27 +333,6 @@ } /** - * Ensures that the module of the given class is readable to this - * module. - * @param targetClass class in module to be made readable - */ - private void ensureReadable(Class targetClass) { - try { - Method getModuleMethod = Class.class.getMethod("getModule"); - Object thisModule = getModuleMethod.invoke(this.getClass()); - Object targetModule = getModuleMethod.invoke(targetClass); - - Class moduleClass = getModuleMethod.getReturnType(); - Method addReadsMethod = moduleClass.getMethod("addReads", moduleClass); - addReadsMethod.invoke(thisModule, targetModule); - } catch (NoSuchMethodException e) { - // ignore - } catch (Exception e) { - throw new InternalError(e); - } - } - - /** * Main program - internal */ private boolean parseAndExecute(List argList, @@ -459,6 +459,11 @@ String userDocletPath = null; String userDocletName = null; + // taglet specifying arguments, since tagletpath is a doclet + // functionality, assume they are repeated and inspect all. + List userTagletPath = new ArrayList<>(); + List userTagletNames = new ArrayList<>(); + // Step 1: loop through the args, set locale early on, if found. for (int i = 0 ; i < argv.size() ; i++) { String arg = argv.get(i); @@ -470,7 +475,7 @@ oneArg(argv, i++); if (userDocletName != null) { usageError("main.more_than_one_doclet_specified_0_and_1", - userDocletName, argv.get(i)); + userDocletName, argv.get(i)); } if (docletName != null) { usageError("main.more_than_one_doclet_specified_0_and_1", @@ -484,13 +489,20 @@ } else { userDocletPath += File.pathSeparator + argv.get(i); } + } else if ("-taglet".equals(arg)) { + userTagletNames.add(argv.get(i + 1)); + } else if ("-tagletpath".equals(arg)) { + for (String pathname : argv.get(i + 1).split(File.pathSeparator)) { + userTagletPath.add(new File(pathname)); + } } } - // Step 2: a doclet has already been provided, - // nothing more to do. + + // Step 2: a doclet is provided, nothing more to do. if (docletClass != null) { return docletClass; } + // Step 3: doclet name specified ? if so find a ClassLoader, // and load it. if (userDocletName != null) { @@ -506,38 +518,78 @@ try { ((StandardJavaFileManager)fileManager).setLocation(DOCLET_PATH, paths); } catch (IOException ioe) { - panic("main.doclet_no_classloader_found", ioe); - return null; // keep compiler happy + error("main.doclet_could_not_set_location", paths); + throw new Messager.ExitJavadoc(); } } cl = fileManager.getClassLoader(DOCLET_PATH); if (cl == null) { // despite doclet specified on cmdline no classloader found! - panic("main.doclet_no_classloader_found", userDocletName); - return null; // keep compiler happy - } - try { - Class klass = cl.loadClass(userDocletName); - ensureReadable(klass); - return klass; - } catch (ClassNotFoundException cnfe) { - panic("main.doclet_class_not_found", userDocletName); - return null; // keep compiler happy + error("main.doclet_no_classloader_found", userDocletName); + throw new Messager.ExitJavadoc(); } } + try { + Class klass = cl.loadClass(userDocletName); + return klass; + } catch (ClassNotFoundException cnfe) { + error("main.doclet_class_not_found", userDocletName); + throw new Messager.ExitJavadoc(); + } } - // Step 4: we have a doclet, try loading it, otherwise - // return back the standard doclet + + // Step 4: we have a doclet, try loading it if (docletName != null) { try { return Class.forName(docletName, true, getClass().getClassLoader()); } catch (ClassNotFoundException cnfe) { - panic("main.doclet_class_not_found", userDocletName); - return null; // happy compiler, should not happen + error("main.doclet_class_not_found", userDocletName); + throw new Messager.ExitJavadoc(); } - } else { - return jdk.javadoc.internal.doclets.standard.Standard.class; + } + + // Step 5: we don't have a doclet specified, do we have taglets ? + if (!userTagletNames.isEmpty() && hasOldTaglet(userTagletNames, userTagletPath)) { + // found a bogey, return the old doclet + return OldStdDoclet; } + + // finally + return StdDoclet; + } + + /* + * This method returns true iff it finds a legacy taglet, but for + * all other conditions including errors it returns false, allowing + * nature to take its own course. + */ + private boolean hasOldTaglet(List tagletNames, List tagletPaths) { + if (!fileManager.hasLocation(TAGLET_PATH)) { + try { + ((StandardJavaFileManager) fileManager).setLocation(TAGLET_PATH, tagletPaths); + } catch (IOException ioe) { + error("main.doclet_could_not_set_location", tagletPaths); + throw new Messager.ExitJavadoc(); + } + } + ClassLoader cl = fileManager.getClassLoader(TAGLET_PATH); + if (cl == null) { + // no classloader found! + error("main.doclet_no_classloader_found", tagletNames.get(0)); + throw new Messager.ExitJavadoc(); + } + for (String tagletName : tagletNames) { + try { + Class klass = cl.loadClass(tagletName); + if (com.sun.tools.doclets.Taglet.class.isAssignableFrom(klass)) { + return true; + } + } catch (ClassNotFoundException cnfe) { + error("main.doclet_class_not_found", tagletName); + throw new Messager.ExitJavadoc(); + } + } + return false; } private void parseArgs(List args, List javaNames) { @@ -595,14 +647,12 @@ usage(true); } - // a terminal call, will not return - void panic(String key, Object... args) { - error(key, args); - messager.exit(); + void error(String key, Object... args) { + messager.error(key, args); } - void error(String key, Object... args) { - messager.error(key, args); + void warn(String key, Object... args) { + messager.warning(key, args); } /** diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/resources/javadoc.properties --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/resources/javadoc.properties Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/resources/javadoc.properties Tue May 03 12:25:20 2016 -0700 @@ -73,7 +73,8 @@ \ given module. may be ALL-UNNAMED to require\n\ \ the unnamed module.\n\ \ -Xmodule: Specify a module to which the classes being compiled belong.\n\ -\ -Xpatch: Specify location of module class files to patch\n +\ -Xpatch: Specify location of module class files to patch\n\ +\ -Xold Invoke the legacy javadoc tool\n main.Xusage.foot=\ These options are non-standard and subject to change without notice. @@ -96,6 +97,7 @@ such as -J-Xmx32m. main.done_in=[done in {0} ms] main.more_than_one_doclet_specified_0_and_1=More than one doclet specified ({0} and {1}). +main.doclet_could_not_set_location=Could not set location for {0} main.doclet_no_classloader_found=Could not obtain classloader to load {0} main.could_not_instantiate_class=Could not instantiate class {0} main.doclet_class_not_found=Cannot find doclet class {0} @@ -109,10 +111,15 @@ main.unsupported.release.version=release version {0} not supported main.release.not.standard.file.manager=-release option specified, but the provided JavaFileManager is not a StandardJavaFileManager. main.unknown.error=an unknown error has occurred +main.legacy_api=The old Doclet and Taglet APIs in the packages\n\ + com.sun.javadoc, com.sun.tools.doclets and their implementations\n\ + are planned to be removed in a future JDK release. These\n\ + components have been superseded by the new APIs in jdk.javadoc.doclet.\n\ + Users are strongly recommended to migrate to the new APIs.\n + javadoc.class_not_found=Class {0} not found. javadoc.error=error javadoc.warning=warning - javadoc.error.msg={0}: error - {1} javadoc.warning.msg={0}: warning - {1} javadoc.note.msg = {1} diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ArgTokenizer.java --- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ArgTokenizer.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ArgTokenizer.java Tue May 03 12:25:20 2016 -0700 @@ -35,6 +35,7 @@ class ArgTokenizer { private final String str; + private final String prefix; private final int length; private int next = 0; private char buf[] = new char[20]; @@ -49,7 +50,12 @@ private boolean isQuoted = false; ArgTokenizer(String arg) { + this("", arg); + } + + ArgTokenizer(String prefix, String arg) { this.str = arg; + this.prefix = prefix; this.length = arg.length(); quoteChar('"'); quoteChar('\''); @@ -88,7 +94,7 @@ } String whole() { - return str; + return prefix + str; } void mark() { diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/Feedback.java --- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/Feedback.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/Feedback.java Tue May 03 12:25:20 2016 -0700 @@ -95,20 +95,20 @@ return mode.getContinuationPrompt(nextId); } - public boolean setFeedback(JShellTool tool, ArgTokenizer at) { - return new Setter(tool, at).setFeedback(); + public boolean setFeedback(MessageHandler messageHandler, ArgTokenizer at) { + return new Setter(messageHandler, at).setFeedback(); } - public boolean setFormat(JShellTool tool, ArgTokenizer at) { - return new Setter(tool, at).setFormat(); + public boolean setFormat(MessageHandler messageHandler, ArgTokenizer at) { + return new Setter(messageHandler, at).setFormat(); } - public boolean setNewMode(JShellTool tool, ArgTokenizer at) { - return new Setter(tool, at).setNewMode(); + public boolean setNewMode(MessageHandler messageHandler, ArgTokenizer at) { + return new Setter(messageHandler, at).setNewMode(); } - public boolean setPrompt(JShellTool tool, ArgTokenizer at) { - return new Setter(tool, at).setPrompt(); + public boolean setPrompt(MessageHandler messageHandler, ArgTokenizer at) { + return new Setter(messageHandler, at).setPrompt(); } { @@ -529,26 +529,26 @@ private class Setter { private final ArgTokenizer at; - private final JShellTool tool; + private final MessageHandler messageHandler; boolean valid = true; - Setter(JShellTool tool, ArgTokenizer at) { - this.tool = tool; + Setter(MessageHandler messageHandler, ArgTokenizer at) { + this.messageHandler = messageHandler; this.at = at; } void fluff(String format, Object... args) { - tool.fluff(format, args); + messageHandler.fluff(format, args); } - void fluffmsg(String format, Object... args) { - tool.fluffmsg(format, args); + void fluffmsg(String messageKey, Object... args) { + messageHandler.fluffmsg(messageKey, args); } void errorat(String messageKey, Object... args) { Object[] a2 = Arrays.copyOf(args, args.length + 2); - a2[args.length] = "/set " + at.whole(); - tool.errormsg(messageKey, a2); + a2[args.length] = at.whole(); + messageHandler.errormsg(messageKey, a2); } // For /set prompt "" "" diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java --- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java Tue May 03 12:25:20 2016 -0700 @@ -108,7 +108,7 @@ * Command line REPL tool for Java using the JShell API. * @author Robert Field */ -public class JShellTool { +public class JShellTool implements MessageHandler { private static final String LINE_SEP = System.getProperty("line.separator"); private static final Pattern LINEBREAK = Pattern.compile("\\R"); @@ -166,6 +166,8 @@ private boolean regenerateOnDeath = true; private boolean live = false; private boolean feedbackInitialized = false; + private String initialMode = null; + private List remoteVMOptions = new ArrayList<>(); SourceCodeAnalysis analysis; JShell state = null; @@ -256,7 +258,8 @@ * @param format printf format * @param args printf args */ - void fluff(String format, Object... args) { + @Override + public void fluff(String format, Object... args) { if (feedback.shouldDisplayCommandFluff() && interactive()) { hard(format, args); } @@ -362,7 +365,8 @@ * @param key the resource key * @param args */ - void errormsg(String key, Object... args) { + @Override + public void errormsg(String key, Object... args) { cmdout.println(prefix(messageFormat(key, args), feedback.getErrorPre())); } @@ -383,7 +387,8 @@ * @param key the resource key * @param args */ - void fluffmsg(String key, Object... args) { + @Override + public void fluffmsg(String key, Object... args) { if (feedback.shouldDisplayCommandFluff() && interactive()) { hardmsg(key, args); } @@ -512,6 +517,23 @@ case "-fullversion": cmdout.printf("jshell %s\n", fullVersion()); return null; + case "-feedback": + if (ai.hasNext()) { + initialMode = ai.next(); + } else { + startmsg("jshell.err.opt.feedback.arg"); + return null; + } + break; + case "-q": + initialMode = "concise"; + break; + case "-qq": + initialMode = "silent"; + break; + case "-v": + initialMode = "verbose"; + break; case "-startup": if (cmdlineStartup != null) { startmsg("jshell.err.opt.startup.conflict"); @@ -530,6 +552,10 @@ cmdlineStartup = ""; break; default: + if (arg.startsWith("-R")) { + remoteVMOptions.add(arg.substring(2)); + break; + } startmsg("jshell.err.opt.unknown", arg); printUsage(); return null; @@ -567,6 +593,7 @@ .idGenerator((sn, i) -> (currentNameSpace == startNamespace || state.status(sn).isActive) ? currentNameSpace.tid(sn) : errorNamespace.tid(sn)) + .remoteVMOptions(remoteVMOptions.toArray(new String[remoteVMOptions.size()])) .build(); shutdownSubscription = state.onShutdown((JShell deadState) -> { if (deadState == state) { @@ -596,6 +623,26 @@ start = cmdlineStartup; } startUpRun(start); + if (initialMode != null) { + MessageHandler mh = new MessageHandler() { + @Override + public void fluff(String format, Object... args) { + } + + @Override + public void fluffmsg(String messageKey, Object... args) { + } + + @Override + public void errormsg(String messageKey, Object... args) { + startmsg(messageKey, args); + } + }; + if (!feedback.setFeedback(mh, new ArgTokenizer("-feedback ", initialMode))) { + regenerateOnDeath = false; + } + initialMode = null; + } currentNameSpace = mainNamespace; } //where @@ -1050,7 +1097,7 @@ "format", "feedback", "newmode", "prompt", "editor", "start"}; final boolean cmdSet(String arg) { - ArgTokenizer at = new ArgTokenizer(arg.trim()); + ArgTokenizer at = new ArgTokenizer("/set ", arg.trim()); String which = setSubCommand(at); if (which == null) { return false; diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/MessageHandler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/MessageHandler.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,41 @@ +/* + * 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.jshell.tool; + + +/** + * User message reporting support + * + * @author Robert Field + */ +public interface MessageHandler { + + void fluff(String format, Object... args); + + void fluffmsg(String messageKey, Object... args); + + void errormsg(String messageKey, Object... args); +} diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties --- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties Tue May 03 12:25:20 2016 -0700 @@ -28,6 +28,7 @@ For an introduction type: /help intro\n jshell.err.opt.classpath.conflict = Conflicting -classpath option. jshell.err.opt.classpath.arg = Argument to -classpath missing. +jshell.err.opt.feedback.arg = Argument to -feedback missing. Mode required. jshell.err.opt.startup.conflict = Conflicting -startup or -nostartup option. jshell.err.opt.unknown = Unknown option: {0} @@ -128,13 +129,23 @@ help.usage = \ Usage: jshell \n\ -where possible options include:\n\t\ - -classpath Specify where to find user class files\n\t\ - -cp Specify where to find user class files\n\t\ - -startup One run replacement for the start-up definitions\n\t\ - -nostartup Do not run the start-up definitions\n\t\ - -help Print a synopsis of standard options\n\t\ - -version Version information\n +where possible options include:\n\ +\ -classpath Specify where to find user class files\n\ +\ -cp Specify where to find user class files\n\ +\ -startup One run replacement for the start-up definitions\n\ +\ -nostartup Do not run the start-up definitions\n\ +\ -feedback Specify the initial feedback mode. The mode may be\n\ +\ predefined (silent, concise, normal, or verbose) or\n\ +\ previously user-defined\n\ +\ -q Quiet feedback. Same as: -feedback concise\n\ +\ -qq Really quiet feedback. Same as: -feedback silent\n\ +\ -v Verbose feedback. Same as: -feedback verbose\n\ +\ -J Pass directly to the runtime system.\n\ +\ Use one -J for each runtime flag or flag argument\n\ +\ -R Pass to the remote runtime system.\n\ +\ Use one -R for each remote flag or flag argument\n\ +\ -help Print this synopsis of standard options\n\ +\ -version Version information\n help.list.summary = list the source you have typed help.list.args = [all|start|] diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.jshell/share/classes/jdk/jshell/Diag.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/Diag.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Diag.java Tue May 03 12:25:20 2016 -0700 @@ -36,12 +36,18 @@ // Simplified view on compiler Diagnostic. /** + * In-package creation only. + */ + Diag() { + } + + /** * Used to signal that no position is available. */ public final static long NOPOS = Diagnostic.NOPOS; /** - * Is this diagnostic and error (as opposed to a warning or note) + * Is this diagnostic an error (as opposed to a warning or note) * @return true if this diagnostic is an error */ public abstract boolean isError(); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java Tue May 03 12:25:20 2016 -0700 @@ -452,7 +452,7 @@ // If appropriate, execute the snippet String value = null; - Exception exception = null; + JShellException exception = null; if (si.status().isDefined) { if (si.isExecutable()) { try { @@ -462,7 +462,8 @@ : ""; } catch (EvalException ex) { exception = translateExecutionException(ex); - } catch (UnresolvedReferenceException ex) { + } catch (JShellException ex) { + // UnresolvedReferenceException exception = ex; } } else if (si.subKind() == SubKind.VAR_DECLARATION_SUBKIND) { @@ -499,7 +500,7 @@ || e.exception() != null; } - private List events(Unit c, Collection outs, String value, Exception exception) { + private List events(Unit c, Collection outs, String value, JShellException exception) { List events = new ArrayList<>(); events.add(c.event(value, exception)); events.addAll(outs.stream() diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.jshell/share/classes/jdk/jshell/EvalException.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/EvalException.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/EvalException.java Tue May 03 12:25:20 2016 -0700 @@ -40,7 +40,7 @@ * empty string. */ @SuppressWarnings("serial") // serialVersionUID intentionally omitted -public class EvalException extends Exception { +public class EvalException extends JShellException { private final String exceptionClass; EvalException(String message, String exceptionClass, StackTraceElement[] stackElements) { diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.jshell/share/classes/jdk/jshell/ExecutionControl.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/ExecutionControl.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/ExecutionControl.java Tue May 03 12:25:20 2016 -0700 @@ -57,11 +57,19 @@ private ObjectInputStream in; private ObjectOutputStream out; private final JShell proc; + private final String remoteVMOptions; - ExecutionControl(JDIEnv env, SnippetMaps maps, JShell proc) { + ExecutionControl(JDIEnv env, SnippetMaps maps, JShell proc, List extraRemoteVMOptions) { this.env = env; this.maps = maps; this.proc = proc; + StringBuilder sb = new StringBuilder(); + extraRemoteVMOptions.stream() + .forEach(s -> { + sb.append(" "); + sb.append(s); + }); + this.remoteVMOptions = sb.toString(); } void launch() throws IOException { @@ -111,7 +119,7 @@ } } - String commandInvoke(String classname) throws EvalException, UnresolvedReferenceException { + String commandInvoke(String classname) throws JShellException { try { synchronized (STOP_LOCK) { userCodeRunning = true; @@ -205,7 +213,7 @@ } } - private boolean readAndReportExecutionResult() throws IOException, EvalException, UnresolvedReferenceException { + private boolean readAndReportExecutionResult() throws IOException, JShellException { int ok = in.readInt(); switch (ok) { case RESULT_SUCCESS: @@ -257,11 +265,9 @@ // Locale.getDefault()); String connectorName = "com.sun.jdi.CommandLineLaunch"; - String classPath = System.getProperty("java.class.path"); - String javaArgs = "-classpath " + classPath; Map argumentName2Value = new HashMap<>(); argumentName2Value.put("main", "jdk.internal.jshell.remote.RemoteAgent " + port); - argumentName2Value.put("options", javaArgs); + argumentName2Value.put("options", remoteVMOptions); boolean launchImmediately = true; int traceFlags = 0;// VirtualMachine.TRACE_SENDS | VirtualMachine.TRACE_EVENTS; diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java Tue May 03 12:25:20 2016 -0700 @@ -26,10 +26,11 @@ package jdk.jshell; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -84,6 +85,7 @@ final PrintStream err; final Supplier tempVariableNameGenerator; final BiFunction idGenerator; + final List extraRemoteVMOptions; private int nextKeyIndex = 1; @@ -105,6 +107,7 @@ this.err = b.err; this.tempVariableNameGenerator = b.tempVariableNameGenerator; this.idGenerator = b.idGenerator; + this.extraRemoteVMOptions = b.extraRemoteVMOptions; this.maps = new SnippetMaps(this); this.keyMap = new KeyMap(this); @@ -139,6 +142,7 @@ PrintStream err = System.err; Supplier tempVariableNameGenerator = null; BiFunction idGenerator = null; + List extraRemoteVMOptions = new ArrayList<>(); Builder() { } @@ -264,6 +268,18 @@ } /** + * Set additional VM options for launching the VM. + * + * @param options The options for the remote VM. + * @return the Builder instance (for use in chained + * initialization). + */ + public Builder remoteVMOptions(String... options) { + this.extraRemoteVMOptions.addAll(Arrays.asList(options)); + return this; + } + + /** * Build a JShell state engine. This is the entry-point to all JShell * functionality. This creates a remote process for execution. It is * thus important to close the returned instance. @@ -621,10 +637,10 @@ ExecutionControl executionControl() { if (executionControl == null) { - this.executionControl = new ExecutionControl(new JDIEnv(this), maps, this); + this.executionControl = new ExecutionControl(new JDIEnv(this), maps, this, extraRemoteVMOptions); try { executionControl.launch(); - } catch (IOException ex) { + } catch (Throwable ex) { throw new InternalError("Launching JDI execution engine threw: " + ex.getMessage(), ex); } } diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.jshell/share/classes/jdk/jshell/JShellException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/JShellException.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,37 @@ +/* + * 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.jshell; + +/** + * The superclass of JShell generated exceptions + */ +@SuppressWarnings("serial") // serialVersionUID intentionally omitted +public class JShellException extends Exception { + + JShellException(String message) { + super(message); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.jshell/share/classes/jdk/jshell/SnippetEvent.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/SnippetEvent.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/SnippetEvent.java Tue May 03 12:25:20 2016 -0700 @@ -44,7 +44,7 @@ SnippetEvent(Snippet snippet, Status previousStatus, Status status, boolean isSignatureChange, Snippet causeSnippet, - String value, Exception exception) { + String value, JShellException exception) { this.snippet = snippet; this.previousStatus = previousStatus; this.status = status; @@ -60,7 +60,7 @@ private final boolean isSignatureChange; private final Snippet causeSnippet; private final String value; - private final Exception exception; + private final JShellException exception; /** * The Snippet which has changed @@ -121,7 +121,7 @@ * during execution, otherwise null. * @return the exception or null. */ - public Exception exception() { + public JShellException exception() { return exception; } diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.jshell/share/classes/jdk/jshell/Unit.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/Unit.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Unit.java Tue May 03 12:25:20 2016 -0700 @@ -440,7 +440,7 @@ : msi.parameterTypes(); } - SnippetEvent event(String value, Exception exception) { + SnippetEvent event(String value, JShellException exception) { boolean wasSignatureChanged = sigChanged(); state.debug(DBG_EVNT, "Snippet: %s id: %s before: %s status: %s sig: %b cause: %s\n", si, si.id(), prevStatus, si.status(), wasSignatureChanged, causalSnippet); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/src/jdk.jshell/share/classes/jdk/jshell/UnresolvedReferenceException.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/UnresolvedReferenceException.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/UnresolvedReferenceException.java Tue May 03 12:25:20 2016 -0700 @@ -38,7 +38,7 @@ * empty string. */ @SuppressWarnings("serial") // serialVersionUID intentionally omitted -public class UnresolvedReferenceException extends Exception { +public class UnresolvedReferenceException extends JShellException { final DeclarationSnippet snippet; diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/jdk/javadoc/doclet/testModules/TestModules.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/javadoc/doclet/testModules/TestModules.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,145 @@ +/* + * 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. + * + * 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 8154119 + * @summary Test modules support in javadoc. + * @author bpatel + * @library ../lib + * @modules jdk.javadoc/jdk.javadoc.internal.tool + * @build JavadocTester + * @run main TestModules + */ + +public class TestModules extends JavadocTester { + + public static void main(String... args) throws Exception { + TestModules tester = new TestModules(); + tester.runTests(); + } + + @Test + void test1() { + javadoc("-d", "out", + "-modulesourcepath", testSrc, + "-addmods", "module1,module2", + "testpkgmdl1", "testpkgmdl2"); + checkExit(Exit.OK); + testDescription(true); + testNoDescription(false); + } + + @Test + void test2() { + javadoc("-d", "out-html5", "-html5", + "-modulesourcepath", testSrc, + "-addmods", "module1,module2", + "testpkgmdl1", "testpkgmdl2"); + checkExit(Exit.OK); + testHtml5Description(true); + testHtml5NoDescription(false); + } + + @Test + void test3() { + javadoc("-d", "out-nocomment", "-nocomment", + "-modulesourcepath", testSrc, + "-addmods", "module1,module2", + "testpkgmdl1", "testpkgmdl2"); + checkExit(Exit.OK); + testDescription(false); + testNoDescription(true); + } + + @Test + void test4() { + javadoc("-d", "out-html5-nocomment", "-nocomment", "-html5", + "-modulesourcepath", testSrc, + "-addmods", "module1,module2", + "testpkgmdl1", "testpkgmdl2"); + checkExit(Exit.OK); + testHtml5Description(false); + testHtml5NoDescription(true); + } + + void testDescription(boolean found) { + checkOutput("module1-summary.html", found, + "\n" + + "\n" + + "\n" + + "\n" + + "

    This is a test description for the module1 module.
    "); + checkOutput("module2-summary.html", found, + "\n" + + "\n" + + "\n" + + "\n" + + "
    This is a test description for the module2 module.
    "); + } + + void testNoDescription(boolean found) { + checkOutput("module1-summary.html", found, + "
    \n" + + "
      \n" + + "
    • \n" + + ""); + checkOutput("module2-summary.html", found, + "
      \n" + + "
        \n" + + "
      • \n" + + "
      "); + } + + void testHtml5Description(boolean found) { + checkOutput("module1-summary.html", found, + "
      \n" + + "\n" + + "\n" + + "\n" + + "\n" + + "
      This is a test description for the module1 module.
      \n" + + "
      "); + checkOutput("module2-summary.html", found, + "
      \n" + + "\n" + + "\n" + + "\n" + + "\n" + + "
      This is a test description for the module2 module.
      \n" + + "
      "); + } + + void testHtml5NoDescription(boolean found) { + checkOutput("module1-summary.html", found, + "
      \n" + + "
        \n" + + "
      • \n" + + "
      "); + checkOutput("module2-summary.html", found, + "
      \n" + + "
        \n" + + "
      • \n" + + "
      "); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/jdk/javadoc/doclet/testModules/module1/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/javadoc/doclet/testModules/module1/module-info.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,33 @@ +/* + * 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. + */ + +/** + * This is a test description for the module1 module. + */ +module module1 { + requires module2; + + exports testpkgmdl1; +} diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/jdk/javadoc/doclet/testModules/module1/testpkgmdl1/TestClassInModule1.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/javadoc/doclet/testModules/module1/testpkgmdl1/TestClassInModule1.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,28 @@ +/* + * 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 testpkgmdl1; + +public class TestClassInModule1 { +} diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/jdk/javadoc/doclet/testModules/module2/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/javadoc/doclet/testModules/module2/module-info.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,31 @@ +/* + * 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. + */ + +/** + * This is a test description for the module2 module. + */ +module module2 { + exports testpkgmdl2; +} diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/jdk/javadoc/doclet/testModules/module2/testpkgmdl2/TestClassInModule2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/javadoc/doclet/testModules/module2/testpkgmdl2/TestClassInModule2.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,28 @@ +/* + * 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 testpkgmdl2; + +public class TestClassInModule2 { +} diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/jdk/javadoc/doclet/testOrdering/TestOrdering.java --- a/langtools/test/jdk/javadoc/doclet/testOrdering/TestOrdering.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/jdk/javadoc/doclet/testOrdering/TestOrdering.java Tue May 03 12:25:20 2016 -0700 @@ -23,7 +23,7 @@ /* * @test - * @bug 8039410 8042601 8042829 8049393 8050031 + * @bug 8039410 8042601 8042829 8049393 8050031 8155061 * @summary test to determine if members are ordered correctly * @author ksrini * @library ../lib/ @@ -72,6 +72,8 @@ checkOrder("pkg1/class-use/UsedClass.html", expectedInnerClassContructors); checkOrder("pkg1/ImplementsOrdering.html", expectedImplementsOrdering); checkOrder("pkg1/OverrideOrdering.html", expectedOverrideOrdering); + checkOrder("allclasses-noframe.html", expectedAllClasses); + checkOrder("allclasses-frame.html", expectedAllClasses); } enum ListOrder { NONE, REVERSE, SHUFFLE }; @@ -179,6 +181,22 @@ }; String[] composeTestVectors() { List testList = new ArrayList<>(); + + testList.addAll(Arrays.asList(expectedPackageOrdering)); + + for (String x : expectedMethodOrdering) { + testList.add(x); + for (int i = 0; i < MAX_PACKAGES; i++) { + String wpkg = "add" + i; + testList.add(wpkg + "/" + x); + String dpkg = wpkg; + for (int j = 1; j < MAX_SUBPACKAGES_DEPTH; j++) { + dpkg = dpkg + "/" + "add"; + testList.add(dpkg + "/" + x); + } + } + } + for (String x : expectedEnumOrdering) { testList.add(x.replace("REPLACE_ME", "<Unnamed>")); for (int i = 0; i < MAX_PACKAGES; i++) { @@ -194,20 +212,9 @@ testList.addAll(Arrays.asList(expectedFieldOrdering)); - for (String x : expectedMethodOrdering) { - testList.add(x); - for (int i = 0; i < MAX_PACKAGES; i++) { - String wpkg = "add" + i; - testList.add(wpkg + "/" + x); - String dpkg = wpkg; - for (int j = 1; j < MAX_SUBPACKAGES_DEPTH; j++) { - dpkg = dpkg + "/" + "add"; - testList.add(dpkg + "/" + x); - } - } - } return testList.toArray(new String[testList.size()]); } + void checkExecutableMemberOrdering(String usePage) { String contents = readFile(usePage); // check constructors @@ -309,6 +316,22 @@ return in.replace("/", "."); } + final String expectedAllClasses[] = { + "pkg1/A.html\" title=\"class in pkg1", + "pkg1/A.C.html\" title=\"class in pkg1", + "pkg1/B.html\" title=\"class in pkg1", + "pkg1/B.A.html\" title=\"class in pkg1", + "pkg1/C1.html\" title=\"class in pkg1", + "pkg1/C2.html\" title=\"class in pkg1", + "pkg1/C3.html\" title=\"class in pkg1", + "pkg1/C4.html\" title=\"class in pkg1", + "pkg1/ImplementsOrdering.html\" title=\"interface in pkg1", + "pkg1/MethodOrder.html\" title=\"class in pkg1", + "pkg1/OverrideOrdering.html\" title=\"class in pkg1", + "pkg1/UsedClass.html\" title=\"class in pkg1" + + }; + final String expectedInnerClassContructors[] = { "../../pkg1/A.html#A-pkg1.UsedClass-", "../../pkg1/B.A.html#A-pkg1.UsedClass-", @@ -342,12 +365,33 @@ "../../pkg1/MethodOrder.html#m-java.util.Collection-", "../../pkg1/MethodOrder.html#m-java.util.List-" }; + final String expectedClassUseWithTypeParams[] = { "../../pkg1/MethodOrder.html#tpm-pkg1.UsedClass-", "../../pkg1/MethodOrder.html#tpm-pkg1.UsedClass-pkg1.UsedClass-", "../../pkg1/MethodOrder.html#tpm-pkg1.UsedClass-pkg1.UsedClass:A-", "../../pkg1/MethodOrder.html#tpm-pkg1.UsedClass-java.lang.String-" }; + + final String expectedPackageOrdering[] = { + "\"add0/package-summary.html\">add0 - package add0", + "\"add0/add/package-summary.html\">add0.add - package add0.add", + "\"add0/add/add/package-summary.html\">add0.add.add - package add0.add.add", + "\"add0/add/add/add/package-summary.html\">add0.add.add.add - package add0.add.add.add", + "\"add1/package-summary.html\">add1 - package add1", + "\"add1/add/package-summary.html\">add1.add - package add1.add", + "\"add1/add/add/package-summary.html\">add1.add.add - package add1.add.add", + "\"add1/add/add/add/package-summary.html\">add1.add.add.add - package add1.add.add.add", + "\"add2/package-summary.html\">add2 - package add2", + "\"add2/add/package-summary.html\">add2.add - package add2.add", + "\"add2/add/add/package-summary.html\">add2.add.add - package add2.add.add", + "\"add2/add/add/add/package-summary.html\">add2.add.add.add - package add2.add.add.add", + "\"add3/package-summary.html\">add3 - package add3", + "\"add3/add/package-summary.html\">add3.add - package add3.add", + "\"add3/add/add/package-summary.html\">add3.add.add - package add3.add.add", + "\"add3/add/add/add/package-summary.html\">add3.add.add.add - package add3.add.add.add" + }; + final String expectedMethodOrdering[] = { "Add.html#add--", "Add.html#add-double-", @@ -361,10 +405,12 @@ "Add.html#add-java.lang.Double-", "Add.html#add-java.lang.Integer-" }; + final String expectedEnumOrdering[] = { "Add.add.html\" title=\"enum in REPLACE_ME\"", "Add.ADD.html\" title=\"enum in REPLACE_ME\"" }; + final String expectedFieldOrdering[] = { "Add.html#addadd\"", "add0/add/add/add/Add.html#addadd\"", @@ -418,10 +464,12 @@ "add3/add/Add.html#ADDADD\"", "add3/Add.html#ADDADD\"" }; + final String expectedPackageTreeOrdering[] = { "", "" }; + final String expectedOverviewOrdering[] = { "", "", @@ -458,6 +506,7 @@ "", "", }; + final static String expectedOverviewFrameOrdering[] = { "<unnamed package>", "add0", @@ -477,11 +526,13 @@ "add3.add.add", "add3.add.add.add" }; + final static String expectedImplementsOrdering[] = { "
      close in interface java.lang.AutoCloseable
      ", "
      close in interface java.nio.channels.Channel
      ", "
      close in interface java.io.Closeable
      " }; + final static String expectedOverrideOrdering[] = { "
      iterator in interface java.util.Collection<", "
      iterator in interface java.lang.Iterable<" diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/jdk/javadoc/tool/EnsureNewOldDoclet.java --- a/langtools/test/jdk/javadoc/tool/EnsureNewOldDoclet.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/jdk/javadoc/tool/EnsureNewOldDoclet.java Tue May 03 12:25:20 2016 -0700 @@ -23,96 +23,349 @@ /* * @test - * @bug 8035473 - * @summary make sure the new doclet is invoked by default, and -Xold + * @bug 8035473 8154482 + * @summary make sure the javadoc tool responds correctly to Xold, + * old doclets and taglets. + * @library /tools/lib + * @build toolbox.ToolBox toolbox.TestRunner + * @run main EnsureNewOldDoclet */ import java.io.*; -import java.util.ArrayList; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import com.sun.javadoc.Tag; +import com.sun.source.doctree.DocTree; + +import toolbox.*; + /** - * Dummy javadoc comment. + * This test ensures the doclet responds correctly when given + * various conditions that force a fall back to the old javadoc + * tool. The following condition in the order described will + * force a dispatch to the old tool, -Xold, old doclet and old taglet. + * */ -public class EnsureNewOldDoclet { +public class EnsureNewOldDoclet extends TestRunner { + + final ToolBox tb; + final File testSrc; + final Path javadocPath; + final ExecTask task; + final String testClasses; + final PrintStream ostream; - final File javadoc; - final File testSrc; - final String thisClassName; + final static String CLASS_NAME = "EnsureNewOldDoclet"; + final static String OLD_DOCLET_CLASS_NAME = CLASS_NAME + "$OldDoclet"; + final static String NEW_DOCLET_CLASS_NAME = CLASS_NAME + "$NewDoclet"; //unused + final static String OLD_TAGLET_CLASS_NAME = CLASS_NAME + "$OldTaglet"; + final static String NEW_TAGLET_CLASS_NAME = CLASS_NAME + "$NewTaglet"; + + final static Pattern OLD_HEADER = Pattern.compile("^Standard Doclet \\(Old\\) version.*"); + final static Pattern NEW_HEADER = Pattern.compile("^Standard Doclet version.*"); + + + final static String OLD_DOCLET_MARKER = "OLD_DOCLET_MARKER"; + final static String OLD_TAGLET_MARKER = "Registered: OldTaglet"; + + final static String NEW_DOCLET_MARKER = "NEW_DOCLET_MARKER"; + final static String NEW_TAGLET_MARKER = "Registered Taglet " + CLASS_NAME + "\\$NewTaglet"; - final static Pattern Expected1 = Pattern.compile("^Standard Doclet \\(Next\\) version.*"); - final static Pattern Expected2 = Pattern.compile("^Standard Doclet version.*"); + final static Pattern WARN_TEXT = Pattern.compile("Users are strongly recommended to migrate" + + " to the new APIs."); + final static String OLD_DOCLET_ERROR = "java.lang.NoSuchMethodException: " + + CLASS_NAME +"\\$NewTaglet"; + final static Pattern NEW_DOCLET_ERROR = Pattern.compile(".*java.lang.ClassCastException.*Taglet " + + CLASS_NAME + "\\$OldTaglet.*"); + + final static String OLD_STDDOCLET = "com.sun.tools.doclets.standard.Standard"; + final static String NEW_STDDOCLET = "jdk.javadoc.internal.doclets.standard.Standard"; + - public EnsureNewOldDoclet() { - File javaHome = new File(System.getProperty("java.home")); - if (javaHome.getName().endsWith("jre")) - javaHome = javaHome.getParentFile(); - javadoc = new File(new File(javaHome, "bin"), "javadoc"); - testSrc = new File(System.getProperty("test.src")); - thisClassName = EnsureNewOldDoclet.class.getName(); + public EnsureNewOldDoclet() throws Exception { + super(System.err); + ostream = System.err; + testClasses = System.getProperty("test.classes"); + tb = new ToolBox(); + javadocPath = tb.getJDKTool("javadoc"); + task = new ExecTask(tb, javadocPath); + testSrc = new File("Foo.java"); + generateSample(testSrc); + } + + void generateSample(File testSrc) throws Exception { + String nl = System.getProperty("line.separator"); + String src = Arrays.asList( + "/**", + " * A test class to test javadoc. Nothing more nothing less.", + " */", + " public class Foo{}").stream().collect(Collectors.joining(nl)); + tb.writeFile(testSrc.getPath(), src); } public static void main(String... args) throws Exception { - EnsureNewOldDoclet test = new EnsureNewOldDoclet(); - test.run1(); - test.run2(); + new EnsureNewOldDoclet().runTests(); + } + + // input: nothing, default mode + // outcome: new tool and new doclet + @Test + public void testDefault() throws Exception { + setArgs("-classpath", ".", // insulates us from ambient classpath + testSrc.toString()); + Task.Result tr = task.run(Task.Expect.SUCCESS); + List out = tr.getOutputLines(Task.OutputKind.STDOUT); + checkOutput(testName, out, NEW_HEADER); + } + + // input: -Xold + // outcome: old tool + @Test + public void testXold() throws Exception { + setArgs("-Xold", + "-classpath", ".", // ambient classpath insulation + testSrc.toString()); + Task.Result tr = task.run(Task.Expect.SUCCESS); + List out = tr.getOutputLines(Task.OutputKind.STDOUT); + List err = tr.getOutputLines(Task.OutputKind.STDERR); + checkOutput(testName, out, OLD_HEADER); + checkOutput(testName, err, WARN_TEXT); + } + + // input: old doclet + // outcome: old tool + @Test + public void testOldDoclet() throws Exception { + setArgs("-classpath", ".", // ambient classpath insulation + "-doclet", + OLD_DOCLET_CLASS_NAME, + "-docletpath", + testClasses, + testSrc.toString()); + Task.Result tr = task.run(Task.Expect.SUCCESS); + List out = tr.getOutputLines(Task.OutputKind.STDOUT); + List err = tr.getOutputLines(Task.OutputKind.STDERR); + checkOutput(testName, out, OLD_DOCLET_MARKER); + checkOutput(testName, err, WARN_TEXT); + } + + // input: old taglet + // outcome: old tool + @Test + public void testOldTaglet() throws Exception { + setArgs("-classpath", ".", // ambient classpath insulation + "-taglet", + OLD_TAGLET_CLASS_NAME, + "-tagletpath", + testClasses, + testSrc.toString()); + Task.Result tr = task.run(Task.Expect.SUCCESS); + List out = tr.getOutputLines(Task.OutputKind.STDOUT); + List err = tr.getOutputLines(Task.OutputKind.STDERR); + checkOutput(testName, out, OLD_TAGLET_MARKER); + checkOutput(testName, err, WARN_TEXT); } - // make sure new doclet is invoked by default - void run1() throws Exception { - List output = doTest(javadoc.getPath(), - "-classpath", ".", // insulates us from ambient classpath - "-Xdoclint:none", - "-package", - new File(testSrc, thisClassName + ".java").getPath()); - System.out.println(output); - for (String x : output) { - if (Expected1.matcher(x).matches()) { + // input: new doclet and old taglet + // outcome: new doclet with failure + @Test + public void testNewDocletOldTaglet() throws Exception { + setArgs("-classpath", ".", // ambient classpath insulation + "-doclet", + NEW_STDDOCLET, + "-taglet", + OLD_TAGLET_CLASS_NAME, + "-tagletpath", + testClasses, + testSrc.toString()); + Task.Result tr = task.run(Task.Expect.FAIL, 1); + //Task.Result tr = task.run(); + List out = tr.getOutputLines(Task.OutputKind.STDOUT); + List err = tr.getOutputLines(Task.OutputKind.STDERR); + checkOutput(testName, out, NEW_HEADER); + checkOutput(testName, err, NEW_DOCLET_ERROR); + } + + // input: old doclet and old taglet + // outcome: old doclet and old taglet should register + @Test + public void testOldDocletOldTaglet() throws Exception { + setArgs("-classpath", ".", // ambient classpath insulation + "-doclet", + OLD_STDDOCLET, + "-taglet", + OLD_TAGLET_CLASS_NAME, + "-tagletpath", + testClasses, + testSrc.toString()); + Task.Result tr = task.run(Task.Expect.SUCCESS); + List out = tr.getOutputLines(Task.OutputKind.STDOUT); + List err = tr.getOutputLines(Task.OutputKind.STDERR); + checkOutput(testName, out, OLD_HEADER); + checkOutput(testName, out, OLD_TAGLET_MARKER); + checkOutput(testName, err, WARN_TEXT); + } + + // input: new doclet and new taglet + // outcome: new doclet and new taglet should register + @Test + public void testNewDocletNewTaglet() throws Exception { + setArgs("-classpath", ".", // ambient classpath insulation + "-doclet", + NEW_STDDOCLET, + "-taglet", + NEW_TAGLET_CLASS_NAME, + "-tagletpath", + testClasses, + testSrc.toString()); + Task.Result tr = task.run(Task.Expect.SUCCESS); + List out = tr.getOutputLines(Task.OutputKind.STDOUT); + List err = tr.getOutputLines(Task.OutputKind.STDERR); + checkOutput(testName, out, NEW_HEADER); + checkOutput(testName, out, NEW_TAGLET_MARKER); + } + + // input: old doclet and new taglet + // outcome: old doclet and error + @Test + public void testOldDocletNewTaglet() throws Exception { + setArgs("-classpath", ".", // ambient classpath insulation + "-doclet", + OLD_STDDOCLET, + "-taglet", + NEW_TAGLET_CLASS_NAME, + "-tagletpath", + testClasses, + testSrc.toString()); + Task.Result tr = task.run(Task.Expect.FAIL, 1); + List out = tr.getOutputLines(Task.OutputKind.STDOUT); + List err = tr.getOutputLines(Task.OutputKind.STDERR); + checkOutput(testName, out, OLD_HEADER); + checkOutput(testName, err, WARN_TEXT); + checkOutput(testName, err, OLD_DOCLET_ERROR); + } + + void setArgs(String... args) { + ostream.println("cmds: " + Arrays.asList(args)); + task.args(args); + } + + void checkOutput(String testCase, List content, String toFind) throws Exception { + checkOutput(testCase, content, Pattern.compile(".*" + toFind + ".*")); + } + + void checkOutput(String testCase, List content, Pattern toFind) throws Exception { + ostream.println("---" + testCase + "---"); + content.stream().forEach(x -> System.out.println(x)); + for (String x : content) { + ostream.println(x); + if (toFind.matcher(x).matches()) { return; } } - throw new Exception("run1: Expected string not found:"); + throw new Exception(testCase + ": Expected string not found: " + toFind); } - // make sure the old doclet is invoked with -Xold - void run2() throws Exception { - List output = doTest(javadoc.getPath(), - "-Xold", - "-classpath", ".", // insulates us from ambient classpath - "-Xdoclint:none", - "-package", - new File(testSrc, thisClassName + ".java").getPath()); - - for (String x : output) { - if (Expected2.matcher(x).matches()) { - throw new Exception("run2: Expected string not found"); - } - return; + public static class OldDoclet extends com.sun.javadoc.Doclet { + public static boolean start(com.sun.javadoc.RootDoc root) { + System.out.println(OLD_DOCLET_MARKER); + return true; } } - /** - * More dummy comments. - */ - List doTest(String... args) throws Exception { - List output = new ArrayList<>(); - // run javadoc in separate process to ensure doclet executed under - // normal user conditions w.r.t. classloader - Process p = new ProcessBuilder() - .command(args) - .redirectErrorStream(true) - .start(); - try (BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()))) { - String line = in.readLine(); - while (line != null) { - output.add(line.trim()); - line = in.readLine(); - } + public static class OldTaglet implements com.sun.tools.doclets.Taglet { + + public static void register(Map map) { + EnsureNewOldDoclet.OldTaglet tag = new OldTaglet(); + com.sun.tools.doclets.Taglet t = (com.sun.tools.doclets.Taglet) map.get(tag.getName()); + System.out.println(OLD_TAGLET_MARKER); + } + + @Override + public boolean inField() { + return true; + } + + @Override + public boolean inConstructor() { + return true; + } + + @Override + public boolean inMethod() { + return true; + } + + @Override + public boolean inOverview() { + return true; + } + + @Override + public boolean inPackage() { + return true; + } + + @Override + public boolean inType() { + return true; + } + + @Override + public boolean isInlineTag() { + return true; } - int rc = p.waitFor(); - if (rc != 0) - throw new Exception("javadoc failed, rc:" + rc); - return output; + + @Override + public String getName() { + return "OldTaglet"; + } + + @Override + public String toString(Tag tag) { + return getName(); + } + + @Override + public String toString(Tag[] tags) { + return getName(); + } + } + + public static class NewTaglet implements jdk.javadoc.doclet.taglet.Taglet { + + @Override + public Set getAllowedLocations() { + return Collections.emptySet(); + } + + @Override + public boolean isInlineTag() { + return true; + } + + @Override + public String getName() { + return "NewTaglet"; + } + + @Override + public String toString(DocTree tag) { + return tag.toString(); + } + + @Override + public String toString(List tags) { + return tags.toString(); + } + } } diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/jdk/jshell/StartOptionTest.java --- a/langtools/test/jdk/jshell/StartOptionTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/jdk/jshell/StartOptionTest.java Tue May 03 12:25:20 2016 -0700 @@ -22,7 +22,7 @@ */ /* - * @test + * @test 8151754 * @summary Testing start-up options. * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main @@ -34,6 +34,7 @@ */ import java.io.ByteArrayOutputStream; +import java.io.OutputStream; import java.io.PrintStream; import java.nio.charset.StandardCharsets; import java.nio.file.Path; @@ -47,6 +48,7 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; @Test public class StartOptionTest { @@ -55,9 +57,24 @@ private ByteArrayOutputStream err; private JShellTool getShellTool() { - return new JShellTool(null, new PrintStream(out), new PrintStream(err), null, null, null, - null, new ReplToolTesting.MemoryPreferences(), - Locale.ROOT); + class NoOutputAllowedStream extends OutputStream { + private final String label; + NoOutputAllowedStream(String label) { + this.label = label; + } + @Override + public void write(int b) { fail("Unexpected output to: " + label); } + } + return new JShellTool( + new TestingInputStream(), + new PrintStream(out), + new PrintStream(err), + new PrintStream(new NoOutputAllowedStream("console")), + new TestingInputStream(), + new PrintStream(new NoOutputAllowedStream("userout")), + new PrintStream(new NoOutputAllowedStream("usererr")), + new ReplToolTesting.MemoryPreferences(), + Locale.ROOT); } private String getOutput() { @@ -133,6 +150,12 @@ } @Test + public void testNegFeedbackOption() throws Exception { + start("", "Argument to -feedback missing. Mode required.", "-feedback"); + start("", "Does not match any current feedback mode: blorp -- -feedback blorp", "-feedback", "blorp"); + } + + @Test public void testVersion() throws Exception { start(s -> assertTrue(s.startsWith("jshell")), null, "-version"); } diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/jdk/jshell/ToolSimpleTest.java --- a/langtools/test/jdk/jshell/ToolSimpleTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/jdk/jshell/ToolSimpleTest.java Tue May 03 12:25:20 2016 -0700 @@ -23,7 +23,7 @@ /* * @test - * @bug 8153716 8143955 + * @bug 8153716 8143955 8151754 8150382 * @summary Simple jshell tool tests * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main @@ -349,4 +349,39 @@ System.setProperty("java.awt.headless", prevHeadless==null? "false" : prevHeadless); } } + + public void testOptionQ() { + test(new String[]{"-q", "-nostartup"}, + (a) -> assertCommand(a, "1+1", "$1 ==> 2"), + (a) -> assertCommand(a, "int x = 5", "") + ); + } + + public void testOptionQq() { + test(new String[]{"-qq", "-nostartup"}, + (a) -> assertCommand(a, "1+1", "") + ); + } + + public void testOptionV() { + test(new String[]{"-v", "-nostartup"}, + (a) -> assertCommand(a, "1+1", + "$1 ==> 2\n" + + "| created scratch variable $1 : int") + ); + } + + public void testOptionFeedback() { + test(new String[]{"-feedback", "concise", "-nostartup"}, + (a) -> assertCommand(a, "1+1", "$1 ==> 2"), + (a) -> assertCommand(a, "int x = 5", "") + ); + } + + public void testOptionR() { + test(new String[]{"-R-Dthe.sound=blorp", "-nostartup"}, + (a) -> assertCommand(a, "System.getProperty(\"the.sound\")", + "$1 ==> \"blorp\"") + ); + } } diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/UnreachableLoopCond.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/UnreachableLoopCond.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,85 @@ +/* + * 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. + * + * 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 8155028 + * @summary javac crashes in silly do-while loop + * @compile UnreachableLoopCond.java + */ + +class UnreachableLoopCond { + + public void foo() { + Integer i = 100; + do { + return; + } while (i++ < 10); + } + + public void goo() { + Integer i = 100; + do { + break; + } while (i++ < 10); + } + + public void zoo() { + Integer i = 100; + do { + throw new RuntimeException(); + } while (i++ < 10); + } + + public void loo() { + Integer i = 100; + Integer j = 100; + do { + do { + return; + } while (i++ < 10); + } while (j++ < 10); + } + + public void moo() { + Integer i = 100; + do { + if (true) { + return; + } else { + return; + } + } while (i++ < 10); + } + + public void moo(boolean cond) { + Integer i = 100; + do { + if (cond) { + return; + } else { + return; + } + } while (i++ < 10); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/annotations/T8154270/EraseClassInfoAnnotationValueTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/annotations/T8154270/EraseClassInfoAnnotationValueTest.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 Google Inc. 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.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; + +/* + * @test + * @bug 8154270 + * @summary javac wrongly rejects some class literals as annotation element values + * @compile EraseClassInfoAnnotationValueTest.java + * @compile -implicit:none Other.java + * @run main EraseClassInfoAnnotationValueTest + */ +public class EraseClassInfoAnnotationValueTest { + + @Retention(RetentionPolicy.RUNTIME) + public @interface A { + Class value(); + } + + static class ParametricType { + + @A(Inner.class) + public static class Nested {} + + public class Inner {} + } + + public static void main(String[] args) { + Class clazz = ParametricType.Nested.class.getAnnotation(A.class).value(); + if (!clazz.equals(ParametricType.Inner.class)) { + throw new AssertionError(clazz); + } + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/annotations/T8154270/Other.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/annotations/T8154270/Other.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2016 Google Inc. 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. + */ + +/** + * Tests separate compilation. + */ +public class Other { + Class clazz = EraseClassInfoAnnotationValueTest.ParametricType.Nested.class; +} diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/classfiles/attributes/LineNumberTable/LineNumberTestBase.java --- a/langtools/test/tools/javac/classfiles/attributes/LineNumberTable/LineNumberTestBase.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/classfiles/attributes/LineNumberTable/LineNumberTestBase.java Tue May 03 12:25:20 2016 -0700 @@ -191,7 +191,7 @@ CONDITION("int res = \n" + "testField == 2 ?\n" + "10\n" + - ":9;", 1, 3, 4), // see issue https://bugs.openjdk.java.net/browse/JDK-8050993 + ":9;", 2, 3, 4), TRY("try{\n" + " --testField;\n" + "}\n" + diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/classfiles/attributes/LineNumberTable/T8050993.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/classfiles/attributes/LineNumberTable/T8050993.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,53 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8050993 + * @summary Verify that the condition in the conditional lexpression gets a LineNumberTable entry + * @modules jdk.jdeps/com.sun.tools.classfile + * @compile -g T8050993.java + * @run main T8050993 + */ + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import com.sun.tools.classfile.*; + +public class T8050993 { + public static void main(String[] args) throws IOException, ConstantPoolException { + ClassFile someTestIn = ClassFile.read(T8050993.class.getResourceAsStream("T8050993.class")); + Set expectedLineNumbers = new HashSet<>(Arrays.asList(49, 50, 47, 48)); + for (Method m : someTestIn.methods) { + if ("method".equals(m.getName(someTestIn.constant_pool))) { + Code_attribute code_attribute = (Code_attribute) m.attributes.get(Attribute.Code); + for (Attribute at : code_attribute.attributes) { + if (Attribute.LineNumberTable.equals(at.getName(someTestIn.constant_pool))) { + LineNumberTable_attribute att = (LineNumberTable_attribute) at; + Set actualLinesNumbers = Arrays.stream(att.line_number_table) + .map(e -> e.line_number) + .collect(Collectors.toSet()); + if (!Objects.equals(expectedLineNumbers, actualLinesNumbers)) { + throw new AssertionError("Expected LineNumber entries not found;" + + "actual=" + actualLinesNumbers + ";" + + "expected=" + expectedLineNumbers); + } + } + } + } + } + } + + public static int field; + + public static String method() { + String s = + field % 2 == 0 ? + "true" + field : + "false" + field; + return s; + } + +} diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/AbstractOrInnerClassServiceImplTest.java --- a/langtools/test/tools/javac/modules/AbstractOrInnerClassServiceImplTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/AbstractOrInnerClassServiceImplTest.java Tue May 03 12:25:20 2016 -0700 @@ -47,7 +47,7 @@ } @Test - void testAbstractServiceImpl(Path base) throws Exception { + public void testAbstractServiceImpl(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p1.Service with p2.Impl; }", @@ -68,7 +68,7 @@ } @Test - void testInnerClassServiceImpl(Path base) throws Exception { + public void testInnerClassServiceImpl(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p1.Service with p2.Outer.Inner; }", @@ -89,7 +89,7 @@ } @Test - void testInnerInterfaceServiceImpl(Path base) throws Exception { + public void testInnerInterfaceServiceImpl(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p1.Service with p2.Outer.Inner; }", diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/AddLimitMods.java --- a/langtools/test/tools/javac/modules/AddLimitMods.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/AddLimitMods.java Tue May 03 12:25:20 2016 -0700 @@ -79,7 +79,7 @@ } @Test - void testManual(Path base) throws Exception { + public void testManual(Path base) throws Exception { Path moduleSrc = base.resolve("module-src"); Path m1 = moduleSrc.resolve("m1"); @@ -176,7 +176,7 @@ } @Test - void testAllModulePath(Path base) throws Exception { + public void testAllModulePath(Path base) throws Exception { if (Files.isDirectory(base)) tb.cleanDirectory(base); @@ -284,7 +284,7 @@ } @Test - void testRuntime2Compile(Path base) throws Exception { + public void testRuntime2Compile(Path base) throws Exception { Path classpathSrc = base.resolve("classpath-src"); Path classpathOut = base.resolve("classpath-out"); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/AddReadsTest.java --- a/langtools/test/tools/javac/modules/AddReadsTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/AddReadsTest.java Tue May 03 12:25:20 2016 -0700 @@ -58,7 +58,7 @@ } @Test - void testAddReads(Path base) throws Exception { + public void testAddReads(Path base) throws Exception { Path src = base.resolve("src"); Path src_m1 = src.resolve("m1"); tb.writeJavaFiles(src_m1, @@ -150,7 +150,7 @@ } @Test - void testAddReadsUnnamedModule(Path base) throws Exception { + public void testAddReadsUnnamedModule(Path base) throws Exception { Path jar = prepareTestJar(base); Path moduleSrc = base.resolve("module-src"); @@ -175,7 +175,7 @@ } @Test - void testAddReadsUnnamedModulePackageConflict(Path base) throws Exception { + public void testAddReadsUnnamedModulePackageConflict(Path base) throws Exception { Path jar = prepareTestJar(base); Path moduleSrc = base.resolve("module-src"); @@ -202,7 +202,7 @@ } @Test - void testAddReadsUnnamedToJavaBase(Path base) throws Exception { + public void testAddReadsUnnamedToJavaBase(Path base) throws Exception { Path jar = prepareTestJar(base); Path src = base.resolve("src"); Path classes = base.resolve("classes"); @@ -223,7 +223,7 @@ } @Test - void testAddReadsToJavaBase(Path base) throws Exception { + public void testAddReadsToJavaBase(Path base) throws Exception { Path src = base.resolve("src"); Path classes = base.resolve("classes"); @@ -275,7 +275,7 @@ } @Test - void testX(Path base) throws Exception { + public void testX(Path base) throws Exception { Path src = base.resolve("src"); Path src_m1 = src.resolve("m1"); tb.writeJavaFiles(src_m1, diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/AnnotationProcessing.java --- a/langtools/test/tools/javac/modules/AnnotationProcessing.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/AnnotationProcessing.java Tue May 03 12:25:20 2016 -0700 @@ -68,7 +68,7 @@ } @Test - void testAPSingleModule(Path base) throws Exception { + public void testAPSingleModule(Path base) throws Exception { Path moduleSrc = base.resolve("module-src"); Path m1 = moduleSrc.resolve("m1"); @@ -95,7 +95,7 @@ } @Test - void testAPMultiModule(Path base) throws Exception { + public void testAPMultiModule(Path base) throws Exception { Path moduleSrc = base.resolve("module-src"); Path m1 = moduleSrc.resolve("m1"); Path m2 = moduleSrc.resolve("m2"); @@ -196,7 +196,7 @@ } @Test - void testVerifyUsesProvides(Path base) throws Exception { + public void testVerifyUsesProvides(Path base) throws Exception { Path moduleSrc = base.resolve("module-src"); Path m1 = moduleSrc.resolve("m1"); @@ -254,7 +254,7 @@ } @Test - void testPackageNoModule(Path base) throws Exception { + public void testPackageNoModule(Path base) throws Exception { Path src = base.resolve("src"); Path classes = base.resolve("classes"); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/AnnotationProcessorsInModulesTest.java --- a/langtools/test/tools/javac/modules/AnnotationProcessorsInModulesTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/AnnotationProcessorsInModulesTest.java Tue May 03 12:25:20 2016 -0700 @@ -156,7 +156,7 @@ Path classes; @Test - void testUseOnlyOneProcessor(Path base) throws Exception { + public void testUseOnlyOneProcessor(Path base) throws Exception { initialization(base); String log = new JavacTask(tb) .options("-processormodulepath", processorCompiledModules.toString(), @@ -172,7 +172,7 @@ } @Test - void testAnnotationProcessorExecutionOrder(Path base) throws Exception { + public void testAnnotationProcessorExecutionOrder(Path base) throws Exception { initialization(base); List log = new JavacTask(tb) .options("-processormodulepath", processorCompiledModules.toString(), @@ -202,7 +202,7 @@ } @Test - void testErrorOutputIfOneProcessorNameIsIncorrect(Path base) throws Exception { + public void testErrorOutputIfOneProcessorNameIsIncorrect(Path base) throws Exception { initialization(base); String log = new JavacTask(tb) .options("-XDrawDiagnostics", "-processormodulepath", processorCompiledModules.toString(), @@ -218,7 +218,7 @@ } @Test - void testOptionsExclusion(Path base) throws Exception { + public void testOptionsExclusion(Path base) throws Exception { initialization(base); List log = new JavacTask(tb) .options("-XDrawDiagnostics", "-processormodulepath", processorCompiledModules.toString(), diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/AutomaticModules.java --- a/langtools/test/tools/javac/modules/AutomaticModules.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/AutomaticModules.java Tue May 03 12:25:20 2016 -0700 @@ -48,7 +48,7 @@ } @Test - void testSimple(Path base) throws Exception { + public void testSimple(Path base) throws Exception { Path legacySrc = base.resolve("legacy-src"); tb.writeJavaFiles(legacySrc, "package api; import java.awt.event.ActionListener; public abstract class Api implements ActionListener {}"); @@ -98,7 +98,7 @@ } @Test - void testUnnamedModule(Path base) throws Exception { + public void testUnnamedModule(Path base) throws Exception { Path legacySrc = base.resolve("legacy-src"); tb.writeJavaFiles(legacySrc, "package api; public abstract class Api { public void run(CharSequence str) { } private void run(base.Base base) { } }", @@ -156,7 +156,7 @@ } @Test - void testModuleInfoFromClassFileDependsOnAutomatic(Path base) throws Exception { + public void testModuleInfoFromClassFileDependsOnAutomatic(Path base) throws Exception { Path automaticSrc = base.resolve("automaticSrc"); tb.writeJavaFiles(automaticSrc, "package api; public class Api {}"); Path automaticClasses = base.resolve("automaticClasses"); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/DoclintOtherModules.java --- a/langtools/test/tools/javac/modules/DoclintOtherModules.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/DoclintOtherModules.java Tue May 03 12:25:20 2016 -0700 @@ -47,7 +47,7 @@ } @Test - void testSimple(Path base) throws Exception { + public void testSimple(Path base) throws Exception { Path src = base.resolve("src"); Path m1 = src.resolve("m1"); Path m2 = src.resolve("m2"); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/DuplicateClassTest.java --- a/langtools/test/tools/javac/modules/DuplicateClassTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/DuplicateClassTest.java Tue May 03 12:25:20 2016 -0700 @@ -47,7 +47,7 @@ } @Test - void testSimple(Path base) throws Exception { + public void testSimple(Path base) throws Exception { Path m1 = base.resolve("m1"); Path m2 = base.resolve("m2"); tb.writeJavaFiles(m1, diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/EdgeCases.java --- a/langtools/test/tools/javac/modules/EdgeCases.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/EdgeCases.java Tue May 03 12:25:20 2016 -0700 @@ -23,6 +23,7 @@ /* * @test + * @bug 8154283 * @summary tests for multi-module mode compilation * @library /tools/lib * @modules @@ -57,7 +58,6 @@ import toolbox.JarTask; import toolbox.JavacTask; import toolbox.Task; -import toolbox.ToolBox; public class EdgeCases extends ModuleTestBase { @@ -66,7 +66,7 @@ } @Test - void testAddExportUndefinedModule(Path base) throws Exception { + public void testAddExportUndefinedModule(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "package test; import undef.Any; public class Test {}"); Path classes = base.resolve("classes"); @@ -89,7 +89,7 @@ } @Test - void testModuleSymbolOutterMostClass(Path base) throws Exception { + public void testModuleSymbolOutterMostClass(Path base) throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) { Path moduleSrc = base.resolve("module-src"); @@ -110,7 +110,7 @@ } @Test - void testParseEnterAnalyze(Path base) throws Exception { + public void testParseEnterAnalyze(Path base) throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) { Path moduleSrc = base.resolve("module-src"); @@ -148,7 +148,7 @@ } @Test - void testModuleImplicitModuleBoundaries(Path base) throws Exception { + public void testModuleImplicitModuleBoundaries(Path base) throws Exception { Path src = base.resolve("src"); Path src_m1 = src.resolve("m1"); tb.writeJavaFiles(src_m1, @@ -180,7 +180,7 @@ } @Test - void testAssignClassToAutomaticModule(Path base) throws Exception { + public void testAssignClassToAutomaticModule(Path base) throws Exception { //check that if a ClassSymbol belongs to an automatic module, it is properly assigned and not //duplicated when being accessed through a classfile. Path automaticSrc = base.resolve("automaticSrc"); @@ -239,7 +239,7 @@ } @Test - void testEmptyImplicitModuleInfo(Path base) throws Exception { + public void testEmptyImplicitModuleInfo(Path base) throws Exception { Path src = base.resolve("src"); Path src_m1 = src.resolve("m1"); Files.createDirectories(src_m1); @@ -269,4 +269,39 @@ } + @Test + public void testClassPackageClash(Path base) throws Exception { + Path src = base.resolve("src"); + Path src_m1 = src.resolve("m1"); + tb.writeJavaFiles(src_m1, + "module m1 { exports test.m1; }", + "package test.m1;\n" + + "public class Test {}\n"); + Path src_m2 = src.resolve("m2"); + tb.writeJavaFiles(src_m2, + "module m2 { requires m1; }", + "package test;\n" + + "public class m1 {}\n"); + Path classes = base.resolve("classes"); + tb.createDirectories(classes); + + List log = new JavacTask(tb) + .options("-modulesourcepath", src.toString(), + "-XDrawDiagnostics") + .outdir(classes) + .files(findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = Arrays.asList( + "m1.java:2:8: compiler.err.clash.with.pkg.of.same.name: kindname.class, test.m1", + "1 error" + ); + + if (!expected.equals(log)) { + throw new IllegalStateException(log.toString()); + } + } + } diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/GraphsTest.java --- a/langtools/test/tools/javac/modules/GraphsTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/GraphsTest.java Tue May 03 12:25:20 2016 -0700 @@ -28,7 +28,8 @@ * @modules * jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main - * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask ModuleTestBase + * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask toolbox.ModuleBuilder + * ModuleTestBase * @run main GraphsTest */ @@ -41,6 +42,7 @@ import toolbox.JarTask; import toolbox.JavacTask; +import toolbox.ModuleBuilder; import toolbox.Task; import toolbox.ToolBox; @@ -69,11 +71,11 @@ * */ @Test - void diamond(Path base) throws Exception { + public void diamond(Path base) throws Exception { Path modules = Files.createDirectories(base.resolve("modules")); - new ModuleBuilder("J") + new ModuleBuilder(tb, "J") .exports("openJ") .classes("package openJ; public class J { }") .classes("package closedJ; public class J { }") @@ -87,25 +89,25 @@ .run() .writeAll(); - new ModuleBuilder("O") + new ModuleBuilder(tb, "O") .exports("openO") .requiresPublic("J", jarModules) .classes("package openO; public class O { openJ.J j; }") .classes("package closedO; public class O { }") .build(modules); - new ModuleBuilder("N") + new ModuleBuilder(tb, "N") .requiresPublic("O", modules, jarModules) .exports("openN") .classes("package openN; public class N { }") .classes("package closedN; public class N { }") .build(modules); - new ModuleBuilder("L") + new ModuleBuilder(tb, "L") .requiresPublic("O", modules, jarModules) .exports("openL") .classes("package openL; public class L { }") .classes("package closedL; public class L { }") .build(modules); - ModuleBuilder m = new ModuleBuilder("M"); + ModuleBuilder m = new ModuleBuilder(tb, "M"); //positive case Path positiveSrc = m .requires("N", modules) @@ -178,14 +180,14 @@ @Test public void reexportOfQualifiedExport(Path base) throws Exception { Path modules = base.resolve("modules"); - new ModuleBuilder("M") + new ModuleBuilder(tb, "M") .requiresPublic("N") .write(modules); - new ModuleBuilder("N") + new ModuleBuilder(tb, "N") .exportsTo("pack", "M") .classes("package pack; public class Clazz { }") .write(modules); - new ModuleBuilder("L") + new ModuleBuilder(tb, "L") .requires("M") .classes("package p; public class A { A(pack.Clazz cl){} } ") .write(modules); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/HelloWorldTest.java --- a/langtools/test/tools/javac/modules/HelloWorldTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/HelloWorldTest.java Tue May 03 12:25:20 2016 -0700 @@ -58,7 +58,7 @@ + HELLO_WORLD; @Test - void testLegacyMode(Path base) throws Exception { + public void testLegacyMode(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, HELLO_WORLD); @@ -85,7 +85,7 @@ } @Test - void testUnnamedModule(Path base) throws Exception { + public void testUnnamedModule(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, HELLO_WORLD); @@ -101,7 +101,7 @@ } @Test - void testSingleModule(Path base) throws Exception { + public void testSingleModule(Path base) throws Exception { Path src = base.resolve("src"); tb.writeFile(src.resolve("module-info.java"), "module m { }"); tb.writeJavaFiles(src, PKG_HELLO_WORLD); @@ -121,7 +121,7 @@ } @Test - void testModuleSourcePath(Path base) throws Exception { + public void testModuleSourcePath(Path base) throws Exception { Path src = base.resolve("src"); Path src_m1 = src.resolve("m1"); tb.writeFile(src_m1.resolve("module-info.java"), "module m1 { }"); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/MOptionTest.java --- a/langtools/test/tools/javac/modules/MOptionTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/MOptionTest.java Tue May 03 12:25:20 2016 -0700 @@ -47,7 +47,7 @@ } @Test - void testOneModule(Path base) throws Exception { + public void testOneModule(Path base) throws Exception { Path src = base.resolve("src"); Path m1 = src.resolve("m1"); Path build = base.resolve("build"); @@ -112,7 +112,7 @@ } @Test - void testNoOutputDir(Path base) throws Exception { + public void testNoOutputDir(Path base) throws Exception { Path src = base.resolve("src"); Path m1 = src.resolve("m1"); Path build = base.resolve("build"); @@ -135,7 +135,7 @@ } @Test - void testNoModuleSourcePath(Path base) throws Exception { + public void testNoModuleSourcePath(Path base) throws Exception { Path src = base.resolve("src"); Path m1 = src.resolve("m1"); Path build = base.resolve("build"); @@ -158,7 +158,7 @@ } @Test - void testMultiModule(Path base) throws Exception { + public void testMultiModule(Path base) throws Exception { Path src = base.resolve("src"); Path m1 = src.resolve("m1"); Path m2 = src.resolve("m2"); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/ModuleFinderTest.java --- a/langtools/test/tools/javac/modules/ModuleFinderTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/ModuleFinderTest.java Tue May 03 12:25:20 2016 -0700 @@ -48,7 +48,7 @@ } @Test - void testDuplicateModulesOnPath(Path base) throws Exception { + public void testDuplicateModulesOnPath(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m1 { }"); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/ModuleInfoTest.java --- a/langtools/test/tools/javac/modules/ModuleInfoTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/ModuleInfoTest.java Tue May 03 12:25:20 2016 -0700 @@ -51,7 +51,7 @@ * Check error message if module declaration not in module-info.java. */ @Test - void testModuleDeclNotInModuleJava(Path base) throws Exception { + public void testModuleDeclNotInModuleJava(Path base) throws Exception { Path src = base.resolve("src"); tb.writeFile(src.resolve("M.java"), "module M { }"); String log = new JavacTask(tb) @@ -69,7 +69,7 @@ * Verify that a package private class can be put in module-info.java. */ @Test - void testNotModuleDeclInModuleJava_1(Path base) throws Exception { + public void testNotModuleDeclInModuleJava_1(Path base) throws Exception { Path src = base.resolve("src"); tb.writeFile(src.resolve("module-info.java"), "class C { }"); new JavacTask(tb) @@ -83,7 +83,7 @@ * Verify that a public class cannot be put in module-info.java. */ @Test - void testNotModuleDeclInModuleJava_2(Path base) throws Exception { + public void testNotModuleDeclInModuleJava_2(Path base) throws Exception { Path src = base.resolve("src"); tb.writeFile(src.resolve("module-info.java"), "public class C { }"); String log = new JavacTask(tb) @@ -101,7 +101,7 @@ * Verify that only one module decl can be put in module-info.java. */ @Test - void testSingleModuleDecl(Path base) throws Exception { + public void testSingleModuleDecl(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module M1 { } /*...*/ module M2 { }"); String log = new JavacTask(tb) @@ -119,7 +119,7 @@ * Verify that missing requires are reported. */ @Test - void testRequiresNotFound(Path base) throws Exception { + public void testRequiresNotFound(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module M1 { requires M2; }"); String log = new JavacTask(tb) @@ -137,7 +137,7 @@ * Verify that missing exports are reported. */ @Test - void testExportsNotFound(Path base) throws Exception { + public void testExportsNotFound(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module M1 { exports p to M2; }"); String log = new JavacTask(tb) @@ -155,7 +155,7 @@ * Verify that a simple loop is detected. */ @Test - void testRequiresSelf(Path base) throws Exception { + public void testRequiresSelf(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module M { requires M; }"); String log = new JavacTask(tb) @@ -173,7 +173,7 @@ * Verify that a multi-module loop is detected. */ @Test - void testRequiresLoop(Path base) throws Exception { + public void testRequiresLoop(Path base) throws Exception { Path src = base.resolve("src"); Path src_m1 = src.resolve("m1"); tb.writeFile(src_m1.resolve("module-info.java"), "module m1 { requires m2; }"); @@ -201,7 +201,7 @@ * Verify that a multi-module loop is detected. */ @Test - void testRequiresPublicLoop(Path base) throws Exception { + public void testRequiresPublicLoop(Path base) throws Exception { Path src = base.resolve("src"); Path src_m1 = src.resolve("m1"); tb.writeFile(src_m1.resolve("module-info.java"), "module m1 { requires m2; }"); @@ -229,7 +229,7 @@ * Verify that duplicate requires are detected. */ @Test - void testDuplicateRequires(Path base) throws Exception { + public void testDuplicateRequires(Path base) throws Exception { Path src = base.resolve("src"); Path src_m1 = src.resolve("m1"); tb.writeFile(src_m1.resolve("module-info.java"), "module m1 { }"); @@ -255,7 +255,7 @@ * Verify that duplicate exported packages are detected. */ @Test - void testDuplicateExports_packages(Path base) throws Exception { + public void testDuplicateExports_packages(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m1 { exports p; exports p; }"); @@ -278,7 +278,7 @@ * Verify that duplicate exported packages are detected. */ @Test - void testDuplicateExports_packages2(Path base) throws Exception { + public void testDuplicateExports_packages2(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m1"), "module m1 { exports p; exports p to m2; }"); tb.writeJavaFiles(src.resolve("m2"), "module m2 { }"); @@ -302,7 +302,7 @@ * Verify that duplicate exported packages are detected. */ @Test - void testDuplicateExports_modules(Path base) throws Exception { + public void testDuplicateExports_modules(Path base) throws Exception { Path src = base.resolve("src"); Path src_m1 = src.resolve("m1"); tb.writeFile(src_m1.resolve("module-info.java"), "module m1 { }"); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/ModuleInfoTreeAccess.java --- a/langtools/test/tools/javac/modules/ModuleInfoTreeAccess.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/ModuleInfoTreeAccess.java Tue May 03 12:25:20 2016 -0700 @@ -61,7 +61,7 @@ } @Test - void testTreePathForModuleDecl(Path base) throws Exception { + public void testTreePathForModuleDecl(Path base) throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) { diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/ModulePathTest.java --- a/langtools/test/tools/javac/modules/ModulePathTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/ModulePathTest.java Tue May 03 12:25:20 2016 -0700 @@ -30,7 +30,8 @@ * jdk.compiler/com.sun.tools.javac.main * jdk.jdeps/com.sun.tools.javap * jdk.jlink/jdk.tools.jmod - * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask ModuleTestBase + * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask toolbox.ModuleBuilder + * ModuleTestBase * @run main ModulePathTest */ @@ -41,6 +42,7 @@ import toolbox.JarTask; import toolbox.JavacTask; +import toolbox.ModuleBuilder; import toolbox.Task; import toolbox.ToolBox; @@ -54,7 +56,7 @@ } @Test - void testNotExistsOnPath(Path base) throws Exception { + public void testNotExistsOnPath(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "class C { }"); @@ -71,7 +73,7 @@ } @Test - void testNotADirOnPath_1(Path base) throws Exception { + public void testNotADirOnPath_1(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "class C { }"); tb.writeFile("dummy.txt", ""); @@ -89,7 +91,7 @@ } @Test - void testNotADirOnPath_2(Path base) throws Exception { + public void testNotADirOnPath_2(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "class C { }"); tb.writeFile("dummy.jimage", ""); @@ -107,7 +109,7 @@ } @Test - void testExplodedModuleOnPath(Path base) throws Exception { + public void testExplodedModuleOnPath(Path base) throws Exception { Path modSrc = base.resolve("modSrc"); tb.writeJavaFiles(modSrc, "module m1 { exports p; }", @@ -137,7 +139,7 @@ } @Test - void testBadExplodedModuleOnPath(Path base) throws Exception { + public void testBadExplodedModuleOnPath(Path base) throws Exception { Path modClasses = base.resolve("modClasses"); tb.writeFile(modClasses.resolve("module-info.class"), "module m1 { }"); @@ -162,7 +164,7 @@ } @Test - void testAutoJarOnPath(Path base) throws Exception { + public void testAutoJarOnPath(Path base) throws Exception { Path jarSrc = base.resolve("jarSrc"); tb.writeJavaFiles(jarSrc, "package p; public class CC { }"); @@ -195,7 +197,7 @@ } @Test - void testModJarOnPath(Path base) throws Exception { + public void testModJarOnPath(Path base) throws Exception { Path jarSrc = base.resolve("jarSrc"); tb.writeJavaFiles(jarSrc, "module m1 { exports p; }", @@ -231,7 +233,7 @@ } @Test - void testBadJarOnPath(Path base) throws Exception { + public void testBadJarOnPath(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "class C { }"); tb.writeFile("dummy.jar", ""); @@ -249,7 +251,7 @@ } @Test - void testJModOnPath(Path base) throws Exception { + public void testJModOnPath(Path base) throws Exception { Path jmodSrc = base.resolve("jmodSrc"); tb.writeJavaFiles(jmodSrc, "module m1 { exports p; }", @@ -282,7 +284,7 @@ } @Test - void testBadJModOnPath(Path base) throws Exception { + public void testBadJModOnPath(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "class C { }"); tb.writeFile("dummy.jmod", ""); @@ -300,9 +302,9 @@ } @Test - void relativePath(Path base) throws Exception { + public void relativePath(Path base) throws Exception { final Path modules = base.resolve("modules"); - new ModuleBuilder("m1").build(modules); + new ModuleBuilder(tb, "m1").build(modules); Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m2 { requires m1; }", "class A { }"); @@ -316,9 +318,9 @@ } @Test - void duplicatePaths_1(Path base) throws Exception { + public void duplicatePaths_1(Path base) throws Exception { final Path modules = base.resolve("modules"); - new ModuleBuilder("m1").build(modules); + new ModuleBuilder(tb, "m1").build(modules); Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m2 { requires m1; }", "class A { }"); @@ -332,9 +334,9 @@ } @Test - void duplicatePaths_2(Path base) throws Exception { + public void duplicatePaths_2(Path base) throws Exception { final Path modules = base.resolve("modules"); - new ModuleBuilder("m1").build(modules); + new ModuleBuilder(tb, "m1").build(modules); Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m2 { requires m1; }", "class A { }"); @@ -349,15 +351,15 @@ } @Test - void oneModuleHidesAnother(Path base) throws Exception { + public void oneModuleHidesAnother(Path base) throws Exception { final Path module = base.resolve("modules"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("pkg1") .classes("package pkg1; public class E { }") .build(module); final Path deepModuleDir = module.resolve("deepModuleDir"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("pkg2") .classes("package pkg2; public class E { }") .build(deepModuleDir); @@ -374,19 +376,19 @@ } @Test - void modulesInDifferentContainers(Path base) throws Exception { + public void modulesInDifferentContainers(Path base) throws Exception { final Path modules = base.resolve("modules"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("one") .classes("package one; public class A { }") .build(modules); - new ModuleBuilder("m2") + new ModuleBuilder(tb, "m2") .requires("m1", modules) .build(base.resolve("tmp")); jar(base.resolve("tmp/m2"), modules.resolve("m2.jar")); - new ModuleBuilder("m3") + new ModuleBuilder(tb, "m3") .requires("m2", modules) .build(base.resolve("tmp")); jmod(base.resolve("tmp/m3"), modules.resolve("m3.jmod")); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/ModuleSourcePathTest.java --- a/langtools/test/tools/javac/modules/ModuleSourcePathTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/ModuleSourcePathTest.java Tue May 03 12:25:20 2016 -0700 @@ -56,7 +56,7 @@ } @Test - void testSourcePathConflict(Path base) throws Exception { + public void testSourcePathConflict(Path base) throws Exception { Path sp = base.resolve("src"); Path msp = base.resolve("srcmodules"); @@ -74,7 +74,7 @@ } @Test - void testUnnormalizedPath1(Path base) throws Exception { + public void testUnnormalizedPath1(Path base) throws Exception { Path src = base.resolve("src"); Path src_m1 = src.resolve("m1"); tb.writeJavaFiles(src_m1, "module m1 { }"); @@ -91,7 +91,7 @@ } @Test - void testUnnormalizedPath2(Path base) throws Exception { + public void testUnnormalizedPath2(Path base) throws Exception { Path src = base.resolve("src"); Path src_m1 = src.resolve("m1"); tb.writeJavaFiles(src_m1, "module m1 { }"); @@ -115,7 +115,7 @@ } @Test - void regularBraces(Path base) throws Exception { + public void regularBraces(Path base) throws Exception { generateModules(base, "src1", "src2/inner_dir"); final Path modules = base.resolve("modules"); @@ -136,7 +136,7 @@ } @Test - void mismatchedBraces(Path base) throws Exception { + public void mismatchedBraces(Path base) throws Exception { final List sourcePaths = Arrays.asList( "{", "}", @@ -165,7 +165,7 @@ } @Test - void deepBraces(Path base) throws Exception { + public void deepBraces(Path base) throws Exception { String[] modulePaths = {"src/src1", "src/src2", "src/src3", @@ -197,7 +197,7 @@ } @Test - void fileInPath(Path base) throws Exception { + public void fileInPath(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("kettle$"), "module kettle$ { }", "package electric; class Heater { }"); tb.writeFile(base.resolve("dummy.txt"), ""); @@ -218,7 +218,7 @@ } @Test - void noAlternative(Path base) throws Exception { + public void noAlternative(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("kettle$"), "module kettle$ { }", "package electric; class Heater { }"); @@ -238,7 +238,7 @@ } @Test - void noChoice(Path base) throws Exception { + public void noChoice(Path base) throws Exception { tb.writeJavaFiles(base.resolve("kettle$"), "module kettle$ { }", "package electric; class Heater { }"); final Path modules = base.resolve("modules"); @@ -257,7 +257,7 @@ } @Test - void nestedModules(Path src) throws Exception { + public void nestedModules(Path src) throws Exception { Path carModule = src.resolve("car"); tb.writeJavaFiles(carModule, "module car { }", "package light; class Headlight { }"); tb.writeJavaFiles(carModule.resolve("engine"), "module engine { }", "package flat; class Piston { }"); @@ -277,7 +277,7 @@ } @Test - void relativePaths(Path base) throws Exception { + public void relativePaths(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("kettle"), "module kettle { }", "package electric; class Heater { }"); @@ -296,7 +296,7 @@ } @Test - void duplicatePaths(Path base) throws Exception { + public void duplicatePaths(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m1"), "module m1 { }", "package a; class A { }"); @@ -315,7 +315,7 @@ } @Test - void notExistentPaths(Path base) throws Exception { + public void notExistentPaths(Path base) throws Exception { tb.writeJavaFiles(base.resolve("m1"), "module m1 { requires m0; }", "package a; class A { }"); final Path modules = base.resolve("modules"); @@ -334,7 +334,7 @@ } @Test - void notExistentPathShouldBeSkipped(Path base) throws Exception { + public void notExistentPathShouldBeSkipped(Path base) throws Exception { tb.writeJavaFiles(base.resolve("m1"), "module m1 { }", "package a; class A { }"); final Path modules = base.resolve("modules"); @@ -352,7 +352,7 @@ } @Test - void commas(Path base) throws Exception { + public void commas(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m1"), "module m1 { }", "package a; class A { }"); @@ -371,7 +371,7 @@ } @Test - void asterisk(Path base) throws Exception { + public void asterisk(Path base) throws Exception { tb.writeJavaFiles(base.resolve("kettle").resolve("classes"), "module kettle { }", "package electric; class Heater { }"); @@ -391,7 +391,7 @@ } @Test - void asteriskInDifferentSets(Path base) throws Exception { + public void asteriskInDifferentSets(Path base) throws Exception { Path src = base.resolve("src"); final Path module = src.resolve("kettle"); tb.writeJavaFiles(module.resolve("classes"), "module kettle { }", "package electric; class Heater { }"); @@ -417,7 +417,7 @@ } @Test - void asteriskIllegalUse(Path base) throws Exception { + public void asteriskIllegalUse(Path base) throws Exception { final List sourcePaths = Arrays.asList( "*", "**", diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/ModuleTestBase.java --- a/langtools/test/tools/javac/modules/ModuleTestBase.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/ModuleTestBase.java Tue May 03 12:25:20 2016 -0700 @@ -43,19 +43,20 @@ import java.util.stream.Collectors; import toolbox.JavacTask; +import toolbox.TestRunner; import toolbox.ToolBox; /** * Base class for module tests. */ -public class ModuleTestBase { +public class ModuleTestBase extends TestRunner { protected ToolBox tb; - protected PrintStream out; private int errors; - /** Marker annotation for test methods to be invoked by runTests. */ - @Retention(RetentionPolicy.RUNTIME) - @interface Test { } + ModuleTestBase() { + super(System.err); + tb = new ToolBox(); + } /** * Run all methods annotated with @Test, and throw an exception if any @@ -63,47 +64,12 @@ * * @throws Exception if any errors occurred */ - void runTests() throws Exception { - if (tb == null) - tb = new ToolBox(); - out = System.err; - - for (Method m: getClass().getDeclaredMethods()) { - Annotation a = m.getAnnotation(Test.class); - if (a != null) { - try { - out.println("Running test " + m.getName()); - Path baseDir = Paths.get(m.getName()); - m.invoke(this, new Object[] { baseDir }); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - error("Exception: " + e.getCause()); - cause.printStackTrace(out); - } - out.println(); - } - } - if (errors > 0) - throw new Exception(errors + " errors occurred"); + protected void runTests() throws Exception { + runTests(m -> new Object[] { Paths.get(m.getName()) }); } - // move to ToolBox? - // change returntyp to List -- means updating ToolBox methods Path[] findJavaFiles(Path... paths) throws IOException { - Set files = new TreeSet<>(); - for (Path p : paths) { - Files.walkFileTree(p, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) - throws IOException { - if (file.getFileName().toString().endsWith(".java")) { - files.add(file); - } - return FileVisitResult.CONTINUE; - } - }); - } - return files.toArray(new Path[files.size()]); + return tb.findJavaFiles(paths); } void error(String message) { @@ -111,79 +77,4 @@ errors++; } - public class ModuleBuilder { - - private final String name; - private String requires = ""; - private String exports = ""; - private String uses = ""; - private String provides = ""; - private String modulePath = ""; - private List content = new ArrayList<>(); - - public ModuleBuilder(String name) { - this.name = name; - } - - public ModuleBuilder requiresPublic(String requires, Path... modulePath) { - return requires("public " + requires, modulePath); - } - - public ModuleBuilder requires(String requires, Path... modulePath) { - this.requires += " requires " + requires + ";\n"; - this.modulePath += Arrays.stream(modulePath) - .map(Path::toString) - .collect(Collectors.joining(File.pathSeparator)); - return this; - } - - public ModuleBuilder exportsTo(String pkg, String module) { - return exports(pkg + " to " + module); - } - - public ModuleBuilder exports(String pkg) { - this.exports += " exports " + pkg + ";\n"; - return this; - } - - public ModuleBuilder uses(String uses) { - this.uses += " uses " + uses + ";\n"; - return this; - } - - public ModuleBuilder provides(String service, String implementation) { - this.provides += " provides " + service + " with " + implementation + ";\n"; - return this; - } - - public ModuleBuilder classes(String... content) { - this.content.addAll(Arrays.asList(content)); - return this; - } - - public Path write(Path where) throws IOException { - Files.createDirectories(where); - List sources = new ArrayList<>(); - sources.add("module " + name + "{" - + requires - + exports - + uses - + provides - + "}"); - sources.addAll(content); - Path moduleSrc = where.resolve(name + "/src"); - tb.writeJavaFiles(moduleSrc, sources.toArray(new String[]{})); - return moduleSrc; - } - - public void build(Path where) throws IOException { - Path moduleSrc = write(where); - new JavacTask(tb) - .outdir(where.resolve(name)) - .options("-mp", modulePath) - .files(findJavaFiles(moduleSrc)) - .run() - .writeAll(); - } - } } diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/ModulesAndClassPathTest.java --- a/langtools/test/tools/javac/modules/ModulesAndClassPathTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/ModulesAndClassPathTest.java Tue May 03 12:25:20 2016 -0700 @@ -55,7 +55,7 @@ } @Test - void testModulesAndClassPath(Path base) throws Exception { + public void testModulesAndClassPath(Path base) throws Exception { Path jar = prepareTestJar(base); Path moduleSrc = base.resolve("module-src"); @@ -106,7 +106,7 @@ } @Test - void testImplicitSourcePathModuleInfo(Path base) throws Exception { + public void testImplicitSourcePathModuleInfo(Path base) throws Exception { Path jar = prepareTestJar(base); Path moduleSrc = base.resolve("module-src"); @@ -139,7 +139,7 @@ } @Test - void testModuleInfoFromOutput(Path base) throws Exception { + public void testModuleInfoFromOutput(Path base) throws Exception { Path jar = prepareTestJar(base); Path moduleSrc = base.resolve("module-src"); @@ -221,7 +221,7 @@ } @Test - void testClassOutputVisibleForIncrementalCompilation(Path base) throws Exception { + public void testClassOutputVisibleForIncrementalCompilation(Path base) throws Exception { Path moduleSrc = base.resolve("module-src"); Path m1 = moduleSrc.resolve("m1"); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/MultiModuleModeTest.java --- a/langtools/test/tools/javac/modules/MultiModuleModeTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/MultiModuleModeTest.java Tue May 03 12:25:20 2016 -0700 @@ -49,7 +49,7 @@ } @Test - void testDuplicateModules(Path base) throws Exception { + public void testDuplicateModules(Path base) throws Exception { Path src = base.resolve("src"); Path src_m1 = src.resolve("m1"); tb.writeJavaFiles(src_m1, "module m1 { }"); @@ -72,7 +72,7 @@ } @Test - void testCantFindModule(Path base) throws Exception { + public void testCantFindModule(Path base) throws Exception { Path src = base.resolve("src"); Path src_m1 = src.resolve("m1"); tb.writeJavaFiles(src_m1, "module m1 { }"); @@ -95,7 +95,7 @@ } @Test - void testModuleNameMismatch(Path base) throws Exception { + public void testModuleNameMismatch(Path base) throws Exception { Path src = base.resolve("src"); Path src_m1 = src.resolve("m1"); tb.writeJavaFiles(src_m1, "module m2 { }"); @@ -116,7 +116,7 @@ } @Test - void testImplicitModuleSource(Path base) throws Exception { + public void testImplicitModuleSource(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m1"), "module m1 { }"); tb.writeJavaFiles(src.resolve("m2"), "module m2 { requires m1; }"); @@ -132,7 +132,7 @@ } @Test - void testImplicitModuleClass(Path base) throws Exception { + public void testImplicitModuleClass(Path base) throws Exception { Path src1 = base.resolve("src1"); tb.writeJavaFiles(src1.resolve("m1"), "module m1 { }"); Path modules1 = base.resolve("modules1"); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/NPECompilingModuleInfoTest.java --- a/langtools/test/tools/javac/modules/NPECompilingModuleInfoTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/NPECompilingModuleInfoTest.java Tue May 03 12:25:20 2016 -0700 @@ -45,7 +45,7 @@ } @Test - void testCompileNoError(Path base) throws Exception { + public void testCompileNoError(Path base) throws Exception { Path mod = base.resolve("mod"); tb.writeJavaFiles(mod, "module mod { exports pkg; }"); Path pkg = mod.resolve("pkg"); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/NPEEmptyFileTest.java --- a/langtools/test/tools/javac/modules/NPEEmptyFileTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/NPEEmptyFileTest.java Tue May 03 12:25:20 2016 -0700 @@ -45,7 +45,7 @@ } @Test - void compileEmptyFile(Path base) throws Exception { + public void compileEmptyFile(Path base) throws Exception { Path modules = base.resolve("modules"); Files.createDirectories(modules); Path emptyJavaFile = base.resolve("Test.java"); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/OutputDirTest.java --- a/langtools/test/tools/javac/modules/OutputDirTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/OutputDirTest.java Tue May 03 12:25:20 2016 -0700 @@ -60,7 +60,7 @@ } @Test - void testError(Path base) throws Exception { + public void testError(Path base) throws Exception { String log = new JavacTask(tb) .options("-XDrawDiagnostics", "-modulesourcepath", src.toString()) @@ -74,7 +74,7 @@ } @Test - void testProcOnly(Path base) throws IOException { + public void testProcOnly(Path base) throws IOException { new JavacTask(tb) .options("-XDrawDiagnostics", "-proc:only", @@ -85,7 +85,7 @@ } @Test - void testClassOutDir(Path base) throws IOException { + public void testClassOutDir(Path base) throws IOException { Path classes = base.resolve("classes"); new JavacTask(tb) .options("-XDrawDiagnostics", @@ -97,7 +97,7 @@ } @Test - void testExplodedOutDir(Path base) throws Exception { + public void testExplodedOutDir(Path base) throws Exception { Path modSrc = base.resolve("modSrc"); tb.writeJavaFiles(modSrc, "module m1 { exports p; }", @@ -131,7 +131,7 @@ } @Test - void testInExplodedOutDir(Path base) throws Exception { + public void testInExplodedOutDir(Path base) throws Exception { Path modSrc = base.resolve("modSrc"); tb.writeJavaFiles(modSrc, "module m1 { exports p; }", diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/PackageConflictTest.java --- a/langtools/test/tools/javac/modules/PackageConflictTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/PackageConflictTest.java Tue May 03 12:25:20 2016 -0700 @@ -28,7 +28,7 @@ * @modules * jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main - * @build toolbox.ToolBox toolbox.JavacTask ModuleTestBase + * @build toolbox.ToolBox toolbox.JavacTask toolbox.ModuleBuilder ModuleTestBase * @run main PackageConflictTest */ @@ -38,6 +38,7 @@ import java.util.List; import toolbox.JavacTask; +import toolbox.ModuleBuilder; import toolbox.Task; import toolbox.ToolBox; @@ -48,7 +49,7 @@ } @Test - void testSimple(Path base) throws Exception { + public void testSimple(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "package java.util; public class MyList { }"); @@ -68,7 +69,7 @@ } @Test - void testDisjoint(Path base) throws Exception { + public void testDisjoint(Path base) throws Exception { Path m1 = base.resolve("m1"); Path m2 = base.resolve("m2"); tb.writeJavaFiles(m1, @@ -89,7 +90,7 @@ } @Test - void testConflictInDependencies(Path base) throws Exception { + public void testConflictInDependencies(Path base) throws Exception { Path m1 = base.resolve("m1"); Path m2 = base.resolve("m2"); Path m3 = base.resolve("m3"); @@ -123,13 +124,13 @@ } @Test - void testSimple2(Path base) throws Exception { + public void testSimple2(Path base) throws Exception { Path modules = base.resolve("modules"); - new ModuleBuilder("N") + new ModuleBuilder(tb, "N") .exports("pack") .classes("package pack; public class A { }") .build(modules); - new ModuleBuilder("M") + new ModuleBuilder(tb, "M") .requires("N") .classes("package pack; public class B { pack.A f; }") .write(modules); @@ -147,14 +148,14 @@ } @Test - void testPrivateConflict(Path base) throws Exception { + public void testPrivateConflict(Path base) throws Exception { Path modules = base.resolve("modules"); - new ModuleBuilder("N") + new ModuleBuilder(tb, "N") .exports("publ") .classes("package pack; public class A { }") .classes("package publ; public class B { }") .write(modules); - new ModuleBuilder("M") + new ModuleBuilder(tb, "M") .requires("N") .classes("package pack; public class C { publ.B b; }") .write(modules); @@ -173,14 +174,14 @@ } @Test - void testPrivateConflictOnModulePath(Path base) throws Exception { + public void testPrivateConflictOnModulePath(Path base) throws Exception { Path modules = base.resolve("modules"); - new ModuleBuilder("N") + new ModuleBuilder(tb, "N") .exports("publ") .classes("package pack; public class A { }") .classes("package publ; public class B { }") .build(modules); - new ModuleBuilder("M") + new ModuleBuilder(tb, "M") .requires("N") .classes("package pack; public class C { publ.B b; }") .write(modules); @@ -199,17 +200,17 @@ } @Test - void testRequiresConflictExports(Path base) throws Exception { + public void testRequiresConflictExports(Path base) throws Exception { Path modules = base.resolve("modules"); - new ModuleBuilder("M") + new ModuleBuilder(tb, "M") .exports("pack") .classes("package pack; public class A { }") .build(modules); - new ModuleBuilder("N") + new ModuleBuilder(tb, "N") .exports("pack") .classes("package pack; public class B { }") .build(modules); - new ModuleBuilder("K") + new ModuleBuilder(tb, "K") .requires("M") .requires("N") .classes("package pkg; public class C { pack.A a; pack.B b; }") @@ -231,18 +232,18 @@ } @Test - void testQulifiedExportsToDifferentModules(Path base) throws Exception { + public void testQulifiedExportsToDifferentModules(Path base) throws Exception { Path modules = base.resolve("modules"); - new ModuleBuilder("U").write(modules); - new ModuleBuilder("M") + new ModuleBuilder(tb, "U").write(modules); + new ModuleBuilder(tb, "M") .exports("pkg to U") .classes("package pkg; public class A { public static boolean flagM; }") .write(modules); - new ModuleBuilder("N") + new ModuleBuilder(tb, "N") .exports("pkg to K") .classes("package pkg; public class A { public static boolean flagN; }") .write(modules); - ModuleBuilder moduleK = new ModuleBuilder("K"); + ModuleBuilder moduleK = new ModuleBuilder(tb, "K"); moduleK.requires("M") .requires("N") .classes("package p; public class DependsOnN { boolean f = pkg.A.flagN; } ") diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/PackageMultipleModules.java --- a/langtools/test/tools/javac/modules/PackageMultipleModules.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/PackageMultipleModules.java Tue May 03 12:25:20 2016 -0700 @@ -49,7 +49,7 @@ } @Test - void testSimple(Path base) throws Exception { + public void testSimple(Path base) throws Exception { Path m1 = base.resolve("m1"); Path m2 = base.resolve("m2"); tb.writeJavaFiles(m1, diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/PluginsInModulesTest.java --- a/langtools/test/tools/javac/modules/PluginsInModulesTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/PluginsInModulesTest.java Tue May 03 12:25:20 2016 -0700 @@ -127,7 +127,7 @@ Path classes; @Test - void testUseOnlyOneProcessor(Path base) throws Exception { + public void testUseOnlyOneProcessor(Path base) throws Exception { initialization(base); List log = new JavacTask(tb) .options("-processormodulepath", processorCompiledModules.toString(), diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/ProvidesTest.java --- a/langtools/test/tools/javac/modules/ProvidesTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/ProvidesTest.java Tue May 03 12:25:20 2016 -0700 @@ -48,7 +48,7 @@ } @Test - void testSimple(Path base) throws Exception { + public void testSimple(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p1.C1 with p2.C2; }", @@ -65,7 +65,7 @@ } @Test - void testMulti(Path base) throws Exception { + public void testMulti(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m1"), "module m1 { exports p1; }", @@ -86,7 +86,7 @@ } @Test - void testMissingWith(Path base) throws Exception { + public void testMissingWith(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p.C; }", @@ -108,7 +108,7 @@ } @Test - void testDuplicateProvides(Path base) throws Exception { + public void testDuplicateProvides(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p1.C1 with p2.C2; provides p1.C1 with p2.C2; }", @@ -126,7 +126,7 @@ } @Test - void testMissingService(Path base) throws Exception { + public void testMissingService(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p.Missing with p.C; }", @@ -151,7 +151,7 @@ } @Test - void testProvidesFromAnotherModule(Path base) throws Exception { + public void testProvidesFromAnotherModule(Path base) throws Exception { Path modules = base.resolve("modules"); tb.writeJavaFiles(modules.resolve("M"), "module M { exports p; }", @@ -177,7 +177,7 @@ } @Test - void testServiceIsNotImplemented(Path base) throws Exception { + public void testServiceIsNotImplemented(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p.A with p.B; }", @@ -200,7 +200,7 @@ } @Test - void testMissingImplementation(Path base) throws Exception { + public void testMissingImplementation(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p.C with p.Impl; }", @@ -222,7 +222,7 @@ } @Test - void testSeveralImplementations(Path base) throws Exception { + public void testSeveralImplementations(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p.C with p.Impl1; provides p.C with p.Impl2; }", @@ -238,7 +238,7 @@ } @Test - void testOneImplementationsForServices(Path base) throws Exception { + public void testOneImplementationsForServices(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p.Service1 with p.Impl; provides p.Service2 with p.Impl; }", @@ -254,7 +254,7 @@ } @Test - void testAbstractImplementation(Path base) throws Exception { + public void testAbstractImplementation(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p1.C1 with p2.C2; }", @@ -277,7 +277,7 @@ } @Test - void testInterfaceImplementation(Path base) throws Exception { + public void testInterfaceImplementation(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p1.Service with p2.Impl; }", @@ -300,7 +300,7 @@ } @Test - void testProtectedImplementation(Path base) throws Exception { + public void testProtectedImplementation(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p1.C1 with p2.C2; }", @@ -323,7 +323,7 @@ } @Test - void testNoNoArgConstructor(Path base) throws Exception { + public void testNoNoArgConstructor(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { uses p1.C1; provides p1.C1 with p2.C2; }", @@ -346,7 +346,7 @@ } @Test - void testPrivateNoArgConstructor(Path base) throws Exception { + public void testPrivateNoArgConstructor(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { uses p1.C1; provides p1.C1 with p2.C2; }", @@ -369,7 +369,7 @@ } @Test - void testServiceIndirectlyImplemented(Path base) throws Exception { + public void testServiceIndirectlyImplemented(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p1.C1 with p2.C3; }", @@ -385,7 +385,7 @@ } @Test - void testServiceImplementationInnerClass(Path base) throws Exception { + public void testServiceImplementationInnerClass(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p1.C1 with p2.C2.Inner; }", @@ -408,7 +408,7 @@ } @Test - void testServiceDefinitionInnerClass(Path base) throws Exception { + public void testServiceDefinitionInnerClass(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p1.C1.InnerDefinition with p2.C2; }", diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/QueryBeforeEnter.java --- a/langtools/test/tools/javac/modules/QueryBeforeEnter.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/QueryBeforeEnter.java Tue May 03 12:25:20 2016 -0700 @@ -65,7 +65,7 @@ } @Test - void testEmpty(Path base) throws Exception { + public void testEmpty(Path base) throws Exception { JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); com.sun.source.util.JavacTask task = (com.sun.source.util.JavacTask) javaCompiler.getTask(null, null, null, null, null, null); @@ -75,7 +75,7 @@ } @Test - void testUnnamed(Path base) throws Exception { + public void testUnnamed(Path base) throws Exception { Path moduleSrc = base.resolve("module-src"); Path m1 = moduleSrc.resolve("m1"); @@ -151,7 +151,7 @@ } @Test - void testSingleNamed(Path base) throws Exception { + public void testSingleNamed(Path base) throws Exception { Path moduleSrc = base.resolve("module-src"); Path m1 = moduleSrc.resolve("m1"); @@ -226,7 +226,7 @@ } @Test - void testMultiModule(Path base) throws Exception { + public void testMultiModule(Path base) throws Exception { Path modulePathSrc = base.resolve("module-path-src"); Path m1 = modulePathSrc.resolve("m1"); @@ -311,7 +311,7 @@ } @Test - void testTooSoon(Path base) throws Exception { + public void testTooSoon(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/RepeatedUsesAndProvidesTest.java --- a/langtools/test/tools/javac/modules/RepeatedUsesAndProvidesTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/RepeatedUsesAndProvidesTest.java Tue May 03 12:25:20 2016 -0700 @@ -46,7 +46,7 @@ } @Test - void testDuplicateUses(Path base) throws Exception { + public void testDuplicateUses(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { uses p1.C1; uses p1.C1; }", @@ -66,7 +66,7 @@ } @Test - void testDuplicateProvides(Path base) throws Exception { + public void testDuplicateProvides(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p1.C1 with p2.C2; provides p1.C1 with p2.C2; }", diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/ReportNonExistentPackageTest.java --- a/langtools/test/tools/javac/modules/ReportNonExistentPackageTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/ReportNonExistentPackageTest.java Tue May 03 12:25:20 2016 -0700 @@ -46,7 +46,7 @@ } @Test - void testExportUnknownPackage(Path base) throws Exception { + public void testExportUnknownPackage(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { exports p1; }"); Path classes = base.resolve("classes"); @@ -64,7 +64,7 @@ } @Test - void testExportEmptyPackage(Path base) throws Exception { + public void testExportEmptyPackage(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { exports p1; }", @@ -84,7 +84,7 @@ } @Test - void testPackageWithMemberWOPackageDeclaration(Path base) throws Exception { + public void testPackageWithMemberWOPackageDeclaration(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { exports p1; }"); Path p1 = src.resolve("p1"); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/RequiresPublicTest.java --- a/langtools/test/tools/javac/modules/RequiresPublicTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/RequiresPublicTest.java Tue May 03 12:25:20 2016 -0700 @@ -47,7 +47,7 @@ } @Test - void testJavaSE_OK(Path base) throws Exception { + public void testJavaSE_OK(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { requires java.se; }", @@ -66,7 +66,7 @@ } @Test - void testJavaSE_Fail(Path base) throws Exception { + public void testJavaSE_Fail(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { requires java.se; }", @@ -90,7 +90,7 @@ } @Test - void testComplex_OK(Path base) throws Exception { + public void testComplex_OK(Path base) throws Exception { Path src = getComplexSrc(base, "", ""); Path classes = base.resolve("classes"); Files.createDirectories(classes); @@ -104,7 +104,7 @@ } @Test - void testComplex_Fail(Path base) throws Exception { + public void testComplex_Fail(Path base) throws Exception { Path src = getComplexSrc(base, "import p5.C5; import p6.C6; import p7.C7;\n", "C5 c5; C6 c6; C7 c7;\n"); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/ResolveTest.java --- a/langtools/test/tools/javac/modules/ResolveTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/ResolveTest.java Tue May 03 12:25:20 2016 -0700 @@ -45,7 +45,7 @@ } @Test - void testMissingSimpleTypeUnnamedModule(Path base) throws Exception { + public void testMissingSimpleTypeUnnamedModule(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "class C { D d; }"); @@ -62,7 +62,7 @@ } @Test - void testMissingSimpleTypeNamedModule(Path base) throws Exception { + public void testMissingSimpleTypeNamedModule(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { }", @@ -81,7 +81,7 @@ } @Test - void testUnexportedTypeUnreadableModule(Path base) throws Exception { + public void testUnexportedTypeUnreadableModule(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m1"), "module m1 { }", @@ -105,7 +105,7 @@ } @Test - void testUnexportedTypeReadableModule(Path base) throws Exception { + public void testUnexportedTypeReadableModule(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m1"), "module m1 { }", @@ -129,7 +129,7 @@ } @Test - void testQualifiedExportedTypeReadableModule(Path base) throws Exception { + public void testQualifiedExportedTypeReadableModule(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m1"), "module m1 { exports p1 to m3; }", @@ -155,7 +155,7 @@ } @Test - void testExportedTypeUnreadableModule(Path base) throws Exception { + public void testExportedTypeUnreadableModule(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m1"), "module m1 { exports p1; }", @@ -179,7 +179,7 @@ } @Test - void testExportedTypeReadableModule(Path base) throws Exception { + public void testExportedTypeReadableModule(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m1"), "module m1 { exports p1; }", @@ -199,7 +199,7 @@ } @Test - void testExportedTypeReadableModule2(Path base) throws Exception { + public void testExportedTypeReadableModule2(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m1"), "module m1 { exports p1 to m2; }", diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/ServiceInStaticClassErrorTest.java --- a/langtools/test/tools/javac/modules/ServiceInStaticClassErrorTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/ServiceInStaticClassErrorTest.java Tue May 03 12:25:20 2016 -0700 @@ -48,7 +48,7 @@ } @Test - void testError(Path base) throws Exception { + public void testError(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p1.I with p1.Outer.A; }", diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/ServiceProvidedButNotExportedOrUsedTest.java --- a/langtools/test/tools/javac/modules/ServiceProvidedButNotExportedOrUsedTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/ServiceProvidedButNotExportedOrUsedTest.java Tue May 03 12:25:20 2016 -0700 @@ -49,7 +49,7 @@ } @Test - void testWarning(Path base) throws Exception { + public void testWarning(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { provides p1.C1 with p2.C2; }", @@ -76,7 +76,7 @@ } @Test - void testImplementationMustBeInSameModuleAsProvidesDirective(Path base) throws Exception { + public void testImplementationMustBeInSameModuleAsProvidesDirective(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m1"), "module m1 { exports p1; }", diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/SingleModuleModeTest.java --- a/langtools/test/tools/javac/modules/SingleModuleModeTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/SingleModuleModeTest.java Tue May 03 12:25:20 2016 -0700 @@ -59,7 +59,7 @@ } @Test - void testTooManyModules(Path base) throws Exception { + public void testTooManyModules(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m1"), "module m1 { }"); tb.writeJavaFiles(src.resolve("m2"), "module m2 { }"); @@ -76,7 +76,7 @@ } @Test - void testImplicitModuleSource(Path base) throws Exception { + public void testImplicitModuleSource(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { }", @@ -90,7 +90,7 @@ } @Test - void testImplicitModuleClass(Path base) throws Exception { + public void testImplicitModuleClass(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { }", @@ -112,7 +112,7 @@ } @Test - void testImplicitModuleClassAP(Path base) throws Exception { + public void testImplicitModuleClassAP(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { uses java.lang.Runnable; }", @@ -137,7 +137,7 @@ } @Test - void testImplicitModuleSourceAP(Path base) throws Exception { + public void testImplicitModuleSourceAP(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { uses java.lang.Runnable; }", diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/SubpackageTest.java --- a/langtools/test/tools/javac/modules/SubpackageTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/SubpackageTest.java Tue May 03 12:25:20 2016 -0700 @@ -52,7 +52,7 @@ } @Test // based on JDK-8075435 - void testUnnamedModule(Path base) throws Exception { + public void testUnnamedModule(Path base) throws Exception { Path libsrc = base.resolve("lib/src"); tb.writeJavaFiles(libsrc, "package p; public class E extends Error { }"); @@ -83,7 +83,7 @@ } @Test - void testSimpleMulti(Path base) throws Exception { + public void testSimpleMulti(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("mp"), "module mp { exports p; }", diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/UpgradeModulePathTest.java --- a/langtools/test/tools/javac/modules/UpgradeModulePathTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/UpgradeModulePathTest.java Tue May 03 12:25:20 2016 -0700 @@ -28,7 +28,7 @@ * @modules * jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main - * @build toolbox.ToolBox toolbox.JavacTask ModuleTestBase + * @build toolbox.ToolBox toolbox.JavacTask toolbox.ModuleBuilder ModuleTestBase * @run main UpgradeModulePathTest */ @@ -36,6 +36,7 @@ import java.nio.file.Path; import toolbox.JavacTask; +import toolbox.ModuleBuilder; import toolbox.Task; import toolbox.ToolBox; @@ -47,15 +48,15 @@ } @Test - void simpleUsage(Path base) throws Exception { + public void simpleUsage(Path base) throws Exception { final Path module = base.resolve("modules"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("pkg1") .classes("package pkg1; public class E { }") .build(module); final Path upgradeModule = base.resolve("upgradeModule"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("pkg2") .classes("package pkg2; public class E { }") .build(upgradeModule); @@ -73,15 +74,15 @@ } @Test - void onlyUpgradeModulePath(Path base) throws Exception { + public void onlyUpgradeModulePath(Path base) throws Exception { final Path module = base.resolve("modules"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("pkg1") .classes("package pkg1; public class E { }") .build(module); final Path upgradeModule = base.resolve("upgradeModule"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("pkg2") .classes("package pkg2; public class E { }") .build(upgradeModule); @@ -98,15 +99,15 @@ } @Test - void withModuleSourcePath(Path base) throws Exception { + public void withModuleSourcePath(Path base) throws Exception { final Path module = base.resolve("modules"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("pkg1") .classes("package pkg1; public class E { }") .build(module); final Path upgradeModule = base.resolve("upgradeModule"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("pkg2") .classes("package pkg2; public class E { }") .build(upgradeModule); @@ -115,7 +116,7 @@ tb.writeJavaFiles(s.resolve("m3"), "module m3 { }"); final Path upgradeModule3 = base.resolve("upgradeModule"); - new ModuleBuilder("m3") + new ModuleBuilder(tb, "m3") .exports("pkg3") .classes("package pkg3; public class E { }") .build(upgradeModule); @@ -135,15 +136,15 @@ } @Test - void sameUpgradeAndModulePath(Path base) throws Exception { + public void sameUpgradeAndModulePath(Path base) throws Exception { final Path module = base.resolve("modules"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("pkg1") .classes("package pkg1; public class E { }") .build(module); final Path upgradeModule = base.resolve("upgradeModule"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("pkg2") .classes("package pkg2; public class E { }") .build(upgradeModule); @@ -161,9 +162,9 @@ } @Test - void dummyFileInUpgradeModulePath(Path base) throws Exception { + public void dummyFileInUpgradeModulePath(Path base) throws Exception { final Path module = base.resolve("modules"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("pkg1") .classes("package pkg1; public class E { }") .build(module); @@ -189,24 +190,24 @@ } @Test - void severalUpgradeModules(Path base) throws Exception { + public void severalUpgradeModules(Path base) throws Exception { final Path module = base.resolve("modules"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("pkg1") .classes("package pkg1; public class A { }") .build(module); - new ModuleBuilder("m2") + new ModuleBuilder(tb, "m2") .exports("pkg2") .classes("package pkg2; public class B { }") .build(module); Path upgradeModule = base.resolve("upgradeModule"); - new ModuleBuilder("m2") + new ModuleBuilder(tb, "m2") .exports("pkg2") .classes("package pkg2; public class BC { }") .build(upgradeModule); - new ModuleBuilder("m3") + new ModuleBuilder(tb, "m3") .exports("pkg3") .classes("package pkg3; public class DC { }") .build(upgradeModule); @@ -240,21 +241,21 @@ } @Test - void severalUpgradeModulePathsLastWin(Path base) throws Exception { + public void severalUpgradeModulePathsLastWin(Path base) throws Exception { final Path module = base.resolve("modules"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("pkg1") .classes("package pkg1; public class E { }") .build(module); final Path upgradeModule1 = base.resolve("upgradeModule1"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("pkg2") .classes("package pkg2; public class EC1 { }") .build(upgradeModule1); final Path upgradeModule2 = base.resolve("upgradeModule2"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("pkg2") .classes("package pkg2; public class EC2 { }") .build(upgradeModule2); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/UsesTest.java --- a/langtools/test/tools/javac/modules/UsesTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/UsesTest.java Tue May 03 12:25:20 2016 -0700 @@ -28,7 +28,7 @@ * @modules * jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main - * @build toolbox.ToolBox toolbox.JavacTask ModuleTestBase + * @build toolbox.ToolBox toolbox.JavacTask toolbox.ModuleBuilder ModuleTestBase * @run main UsesTest */ @@ -39,6 +39,7 @@ import java.util.List; import toolbox.JavacTask; +import toolbox.ModuleBuilder; import toolbox.Task; import toolbox.ToolBox; @@ -49,7 +50,7 @@ } @Test - void testSimple(Path base) throws Exception { + public void testSimple(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { uses p.C; }", @@ -65,7 +66,7 @@ } @Test - void testSimpleInner(Path base) throws Exception { + public void testSimpleInner(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { uses p.C.Inner; }", @@ -81,7 +82,7 @@ } @Test - void testSimpleAnnotation(Path base) throws Exception { + public void testSimpleAnnotation(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { uses p.C; }", @@ -97,7 +98,7 @@ } @Test - void testPrivateService(Path base) throws Exception { + public void testPrivateService(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { uses p.C.A; uses p.C; }", @@ -119,7 +120,7 @@ } @Test - void testMulti(Path base) throws Exception { + public void testMulti(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m1"), "module m1 { exports p; }", @@ -138,13 +139,13 @@ } @Test - void testMultiOnModulePath(Path base) throws Exception { + public void testMultiOnModulePath(Path base) throws Exception { Path modules = base.resolve("modules"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("p") .classes("package p; public class C { }") .build(modules); - new ModuleBuilder("m2") + new ModuleBuilder(tb, "m2") .requires("m1") .uses("p.C") .write(modules); @@ -158,13 +159,13 @@ } @Test - void testMultiOnModulePathInner(Path base) throws Exception { + public void testMultiOnModulePathInner(Path base) throws Exception { Path modules = base.resolve("modules"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .exports("p") .classes("package p; public class C { public class Inner { } }") .build(modules); - new ModuleBuilder("m2") + new ModuleBuilder(tb, "m2") .requires("m1") .uses("p.C.Inner") .write(modules); @@ -178,7 +179,7 @@ } @Test - void testDuplicateUses(Path base) throws Exception { + public void testDuplicateUses(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m"), "module m { uses p.C; uses p.C; }", @@ -199,7 +200,7 @@ } @Test - void testServiceNotExist(Path base) throws Exception { + public void testServiceNotExist(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src, "module m { uses p.NotExist; }", @@ -220,7 +221,7 @@ } @Test - void testUsesUnexportedService(Path base) throws Exception { + public void testUsesUnexportedService(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m1"), "module m1 { }", @@ -244,7 +245,7 @@ } @Test - void testUsesUnexportedButProvidedService(Path base) throws Exception { + public void testUsesUnexportedButProvidedService(Path base) throws Exception { Path src = base.resolve("src"); tb.writeJavaFiles(src.resolve("m1"), "module m1 { provides p.C with p.C; }", diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/javac/modules/XModuleTest.java --- a/langtools/test/tools/javac/modules/XModuleTest.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/javac/modules/XModuleTest.java Tue May 03 12:25:20 2016 -0700 @@ -28,7 +28,7 @@ * @modules * jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main - * @build toolbox.ToolBox toolbox.JavacTask ModuleTestBase + * @build toolbox.ToolBox toolbox.JavacTask toolbox.ModuleBuilder ModuleTestBase * @run main XModuleTest */ @@ -37,7 +37,9 @@ import java.util.List; import toolbox.JavacTask; +import toolbox.ModuleBuilder; import toolbox.Task; +import toolbox.TestRunner; import toolbox.ToolBox; public class XModuleTest extends ModuleTestBase { @@ -47,7 +49,7 @@ } @Test - void testCorrectXModule(Path base) throws Exception { + public void testCorrectXModule(Path base) throws Exception { //note: avoiding use of java.base, as that gets special handling on some places: Path src = base.resolve("src"); tb.writeJavaFiles(src, "package javax.lang.model.element; public interface Extra extends Element { }"); @@ -67,7 +69,7 @@ } @Test - void testSourcePath(Path base) throws Exception { + public void testSourcePath(Path base) throws Exception { //note: avoiding use of java.base, as that gets special handling on some places: Path src = base.resolve("src"); tb.writeJavaFiles(src, "package javax.lang.model.element; public interface Extra extends Element, Other { }", "package javax.lang.model.element; interface Other { }"); @@ -87,7 +89,7 @@ } @Test - void testClassPath(Path base) throws Exception { + public void testClassPath(Path base) throws Exception { Path cpSrc = base.resolve("cpSrc"); tb.writeJavaFiles(cpSrc, "package p; public interface Other { }"); Path cpClasses = base.resolve("cpClasses"); @@ -122,7 +124,7 @@ } @Test - void testNoModuleInfoOnSourcePath(Path base) throws Exception { + public void testNoModuleInfoOnSourcePath(Path base) throws Exception { //note: avoiding use of java.base, as that gets special handling on some places: Path src = base.resolve("src"); tb.writeJavaFiles(src, @@ -147,7 +149,7 @@ } @Test - void testNoModuleInfoInClassOutput(Path base) throws Exception { + public void testNoModuleInfoInClassOutput(Path base) throws Exception { //note: avoiding use of java.base, as that gets special handling on some places: Path srcMod = base.resolve("src-mod"); tb.writeJavaFiles(srcMod, @@ -187,7 +189,7 @@ } @Test - void testModuleSourcePathXModule(Path base) throws Exception { + public void testModuleSourcePathXModule(Path base) throws Exception { //note: avoiding use of java.base, as that gets special handling on some places: Path src = base.resolve("src"); tb.writeJavaFiles(src, "package javax.lang.model.element; public interface Extra extends Element { }"); @@ -210,7 +212,7 @@ } @Test - void testXModuleTooMany(Path base) throws Exception { + public void testXModuleTooMany(Path base) throws Exception { //note: avoiding use of java.base, as that gets special handling on some places: Path src = base.resolve("src"); tb.writeJavaFiles(src, "package javax.lang.model.element; public interface Extra extends Element { }"); @@ -234,9 +236,9 @@ } @Test - void testWithModulePath(Path base) throws Exception { + public void testWithModulePath(Path base) throws Exception { Path module = base.resolve("modules"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .classes("package pkg1; public interface E { }") .build(module); @@ -251,7 +253,7 @@ .writeAll(); //checks module bounds still exist - new ModuleBuilder("m2") + new ModuleBuilder(tb, "m2") .classes("package pkg2; public interface D { }") .build(module); @@ -275,14 +277,14 @@ } @Test - void testWithUpgradeModulePath(Path base) throws Exception { + public void testWithUpgradeModulePath(Path base) throws Exception { Path module = base.resolve("modules"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .classes("package pkg1; public interface E { }") .build(module); Path upgrade = base.resolve("upgrade"); - new ModuleBuilder("m1") + new ModuleBuilder(tb, "m1") .classes("package pkg1; public interface D { }") .build(upgrade); diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/lib/toolbox/JavacTask.java --- a/langtools/test/tools/lib/toolbox/JavacTask.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/lib/toolbox/JavacTask.java Tue May 03 12:25:20 2016 -0700 @@ -103,6 +103,16 @@ } /** + * Sets the classpath. + * @param classpath the classpath + * @return this task object + */ + public JavacTask classpath(List classpath) { + this.classpath = classpath; + return this; + } + + /** * Sets the sourcepath. * @param sourcepath the sourcepath * @return this task object @@ -126,6 +136,16 @@ } /** + * Sets the sourcepath. + * @param sourcepath the sourcepath + * @return this task object + */ + public JavacTask sourcepath(List sourcepath) { + this.sourcepath = sourcepath; + return this; + } + + /** * Sets the output directory. * @param outdir the output directory * @return this task object @@ -188,6 +208,18 @@ } /** + * Sets the files to be compiled or analyzed. + * @param files the files + * @return this task object + */ + public JavacTask files(List files) { + this.files = files.stream() + .map(Path::toString) + .collect(Collectors.toList()); + return this; + } + + /** * Sets the sources to be compiled or analyzed. * Each source string is converted into an in-memory object that * can be passed directly to the compiler. diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/lib/toolbox/ModuleBuilder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/lib/toolbox/ModuleBuilder.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2015, 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. + * + * 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 toolbox; + +import java.io.File; +import java.io.IOException; +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.stream.Collectors; + +public class ModuleBuilder { + + private final ToolBox tb; + private final String name; + private String requires = ""; + private String exports = ""; + private String uses = ""; + private String provides = ""; + private String modulePath = ""; + private List content = new ArrayList<>(); + + public ModuleBuilder(ToolBox tb, String name) { + this.tb = tb; + this.name = name; + } + + public ModuleBuilder requiresPublic(String requires, Path... modulePath) { + return requires("public " + requires, modulePath); + } + + public ModuleBuilder requires(String requires, Path... modulePath) { + this.requires += " requires " + requires + ";\n"; + this.modulePath += Arrays.stream(modulePath) + .map(Path::toString) + .collect(Collectors.joining(File.pathSeparator)); + return this; + } + + public ModuleBuilder exportsTo(String pkg, String module) { + return exports(pkg + " to " + module); + } + + public ModuleBuilder exports(String pkg) { + this.exports += " exports " + pkg + ";\n"; + return this; + } + + public ModuleBuilder uses(String uses) { + this.uses += " uses " + uses + ";\n"; + return this; + } + + public ModuleBuilder provides(String service, String implementation) { + this.provides += " provides " + service + " with " + implementation + ";\n"; + return this; + } + + public ModuleBuilder classes(String... content) { + this.content.addAll(Arrays.asList(content)); + return this; + } + + public Path write(Path where) throws IOException { + Files.createDirectories(where); + List sources = new ArrayList<>(); + sources.add("module " + name + "{" + + requires + + exports + + uses + + provides + + "}"); + sources.addAll(content); + Path moduleSrc = where.resolve(name + "/src"); + tb.writeJavaFiles(moduleSrc, sources.toArray(new String[]{})); + return moduleSrc; + } + + public void build(Path where) throws IOException { + Path moduleSrc = write(where); + new JavacTask(tb) + .outdir(where.resolve(name)) + .options("-mp", modulePath) + .files(tb.findJavaFiles(moduleSrc)) + .run() + .writeAll(); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/lib/toolbox/TestRunner.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/lib/toolbox/TestRunner.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,119 @@ +/* + * 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. + * + * 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 toolbox; + +import java.io.PrintStream; +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.function.Function; + +/** + * Utility class to manage and execute sub-tests within a test. + * + * This class does the following: + *
        + *
      • invokes those test methods annotated with @Test + *
      • keeps track of successful and failed tests + *
      • throws an Exception if any test fails. + *
      • provides a test summary at the end of the run. + *
      + + * Tests must extend this class, annotate the test methods + * with @Test and call one of the runTests method. + */ +public abstract class TestRunner { + /** Marker annotation for test cases. */ + @Retention(RetentionPolicy.RUNTIME) + public @interface Test { } + + int testCount = 0; + int errorCount = 0; + + public String testName = null; + + protected PrintStream out; + + /** + * Constructs the Object. + * @param out the PrintStream to print output to. + */ + public TestRunner(PrintStream out) { + this.out = out; + } + + /** + * Invoke all methods annotated with @Test. + * @throws java.lang.Exception if any errors occur + */ + protected void runTests() throws Exception { + runTests(f -> new Object[0]); + } + + /** + * Invoke all methods annotated with @Test. + * @param f a lambda expression to specify arguments for the test method + * @throws java.lang.Exception if any errors occur + */ + protected void runTests(Function f) throws Exception { + for (Method m : getClass().getDeclaredMethods()) { + Annotation a = m.getAnnotation(Test.class); + if (a != null) { + testName = m.getName(); + try { + testCount++; + out.println("test: " + testName); + m.invoke(this, f.apply(m)); + } catch (InvocationTargetException e) { + errorCount++; + Throwable cause = e.getCause(); + out.println("Exception: " + e.getCause()); + cause.printStackTrace(out); + } + out.println(); + } + } + + if (testCount == 0) { + throw new Error("no tests found"); + } + + StringBuilder summary = new StringBuilder(); + if (testCount != 1) { + summary.append(testCount).append(" tests"); + } + if (errorCount > 0) { + if (summary.length() > 0) { + summary.append(", "); + } + summary.append(errorCount).append(" errors"); + } + out.println(summary); + if (errorCount > 0) { + throw new Exception(errorCount + " errors found"); + } + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d langtools/test/tools/lib/toolbox/ToolBox.java --- a/langtools/test/tools/lib/toolbox/ToolBox.java Tue May 03 09:48:02 2016 -0700 +++ b/langtools/test/tools/lib/toolbox/ToolBox.java Tue May 03 12:25:20 2016 -0700 @@ -41,6 +41,7 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -49,6 +50,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -315,7 +317,7 @@ * Reads the lines of a file. * The file is read using the default character encoding. * @param path the file to be read - * @return the lines of the file. + * @return the lines of the file * @throws IOException if an error occurred while reading the file */ public List readAllLines(String path) throws IOException { @@ -326,7 +328,7 @@ * Reads the lines of a file. * The file is read using the default character encoding. * @param path the file to be read - * @return the lines of the file. + * @return the lines of the file * @throws IOException if an error occurred while reading the file */ public List readAllLines(Path path) throws IOException { @@ -348,7 +350,7 @@ * Reads the lines of a file using the given encoding. * @param path the file to be read * @param encoding the encoding to be used to read the file - * @return the lines of the file. + * @return the lines of the file * @throws IOException if an error occurred while reading the file */ public List readAllLines(Path path, String encoding) throws IOException { @@ -360,6 +362,30 @@ } /** + * Find .java files in one or more directories. + *

      Similar to the shell "find" command: {@code find paths -name \*.java}. + * @param paths the directories in which to search for .java files + * @return the .java files found + * @throws IOException if an error occurred while searching for files + */ + public Path[] findJavaFiles(Path... paths) throws IOException { + Set files = new TreeSet<>(); // use TreeSet to force a consistent order + for (Path p : paths) { + Files.walkFileTree(p, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + if (file.getFileName().toString().endsWith(".java")) { + files.add(file); + } + return FileVisitResult.CONTINUE; + } + }); + } + return files.toArray(new Path[files.size()]); + } + + /** * Writes a file containing the given content. * Any necessary directories for the file will be created. * @param path where to write the file diff -r 8ff4232c93c2 -r 74cd426ebb3d make/CompileJavaModules.gmk --- a/make/CompileJavaModules.gmk Tue May 03 09:48:02 2016 -0700 +++ b/make/CompileJavaModules.gmk Tue May 03 12:25:20 2016 -0700 @@ -99,7 +99,7 @@ ################################################################################ java.desktop_ADD_JAVAC_FLAGS := -Xdoclint:all/protected,-reference \ - '-Xdoclint/package:java.*,javax.*' -Xlint:-deprecation + '-Xdoclint/package:java.*,javax.*' java.desktop_COPY := .gif .png .wav .txt .xml .css .pf java.desktop_CLEAN := iio-plugin.properties cursors.properties diff -r 8ff4232c93c2 -r 74cd426ebb3d make/CreateJmods.gmk --- a/make/CreateJmods.gmk Tue May 03 09:48:02 2016 -0700 +++ b/make/CreateJmods.gmk Tue May 03 12:25:20 2016 -0700 @@ -82,7 +82,7 @@ --os-version $(REQUIRED_OS_VERSION) \ --modulepath $(IMAGES_OUTPUTDIR)/jmods\ --hash-dependencies '.*' \ - --exclude '**_the.*' \ + --exclude '**{_the.*,*.diz,*.debuginfo,*.dSYM/**,*.pdb,*.map}' \ $(JMOD_FLAGS) $(SUPPORT_OUTPUTDIR)/jmods/$(notdir $@) $(MV) $(SUPPORT_OUTPUTDIR)/jmods/$(notdir $@) $@ diff -r 8ff4232c93c2 -r 74cd426ebb3d make/GensrcModuleInfo.gmk --- a/make/GensrcModuleInfo.gmk Tue May 03 09:48:02 2016 -0700 +++ b/make/GensrcModuleInfo.gmk Tue May 03 12:25:20 2016 -0700 @@ -78,21 +78,30 @@ # let space represent new lines in the variable as $(shell) normalizes all # whitespace. $(foreach f, $(MOD_FILES), \ - $(eval MOD_FILE_CONTENTS += $(shell $(GREP) -v ".\*" $f | $(TR) ' ' '/'))) + $(eval MOD_FILE_CONTENTS += $(shell $(GREP) -v -e ".\*" -e "//" $f | $(TR) ' ' '/'))) + + # Separate the modifications into qualified exports and the rest + MODS_QUALIFIED_EXPORTS := $(call containing, /to/, $(MOD_FILE_CONTENTS)) + MODS_REST := $(filter-out $(MODS_QUALIFIED_EXPORTS), $(MOD_FILE_CONTENTS)) # Filter the contents for modules that are actually being built MODULES_FILTER := $(addprefix %/, $(addsuffix ;, $(ALL_MODULES))) - MODULES_FILTER += provides% - MODIFICATIONS := $(filter $(MODULES_FILTER), $(MOD_FILE_CONTENTS)) + MODIFICATIONS := $(filter $(MODULES_FILTER), $(MODS_QUALIFIED_EXPORTS)) \ + $(MODS_REST) # Convert the modification lines into arguments for the modification tool. # Filter out modifications for non existing to-modules. $(foreach line, $(MODIFICATIONS), \ $(eval split_line := $(subst /,$(SPACE),$(line))) \ $(eval command := $(word 1, $(split_line))) \ - $(eval package := $(word 2, $(split_line))) \ + $(eval package := $(patsubst %;,%,$(word 2, $(split_line)))) \ $(eval to_module := $(patsubst %;,%,$(word 4, $(split_line)))) \ - $(eval ARGS += -$(command) $(package)/$(to_module))) + $(if $(to_module), \ + $(eval ARGS += -$(command) $(package)/$(to_module)) \ + , \ + $(eval ARGS += -$(command) $(package)) \ + ) \ + ) ifneq ($(ARGS), ) $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/module-info.java: \ diff -r 8ff4232c93c2 -r 74cd426ebb3d make/Images.gmk --- a/make/Images.gmk Tue May 03 09:48:02 2016 -0700 +++ b/make/Images.gmk Tue May 03 12:25:20 2016 -0700 @@ -423,6 +423,55 @@ endif ################################################################################ +# Debug symbols +# Since debug symbols are not included in the jmod files, they need to be copied +# in manually after generating the images. + +ALL_JDK_MODULES := $(JDK_MODULES) +ALL_JRE_MODULES := $(sort $(JRE_MODULES), $(foreach m, $(JRE_MODULES), \ + $(call FindTransitiveDepsForModule, $m))) + +ifeq ($(OPENJDK_TARGET_OS), windows) + LIBS_TARGET_SUBDIR := bin +else + LIBS_TARGET_SUBDIR := lib +endif + +DEBUGINFO_SUFFIXES := .diz .debuginfo .pdb .map + +# Param 1 - dir to find debuginfo files in +FindDebuginfoFiles = \ + $(wildcard $(addprefix $1/*, $(DEBUGINFO_SUFFIXES)) \ + $(addprefix $1/*/*, $(DEBUGINFO_SUFFIXES)) \ + $(addprefix $1/*/*/*, $(DEBUGINFO_SUFFIXES))) + +# On Macosx, if debug symbols have not been zipped, find all files inside *.dSYM +# dirs. +ifeq ($(OPENJDK_TARGET_OS)-$(ZIP_EXTERNAL_DEBUG_SYMBOLS), macosx-false) + $(eval $(call FillCacheFind, \ + $(SUPPORT_OUTPUTDIR)/modules_cmds $(SUPPORT_OUTPUTDIR)/modules_libs/)) + FindDebuginfoFiles = \ + $(if $(wildcard $1), $(call containing, .dSYM/, $(call CacheFind, $1))) +endif + +# Param 1 - either JDK or JRE +SetupCopyDebuginfo = \ + $(foreach m, $(ALL_$1_MODULES), \ + $(eval $(call SetupCopyFiles, COPY_$1_LIBS_DEBUGINFO_$m, \ + SRC := $(SUPPORT_OUTPUTDIR)/modules_libs/$m, \ + DEST := $($1_IMAGE_DIR)/$(LIBS_TARGET_SUBDIR), \ + FILES := $(call FindDebuginfoFiles, \ + $(SUPPORT_OUTPUTDIR)/modules_libs/$m), \ + )) \ + $(eval $1_TARGETS += $$(COPY_$1_LIBS_DEBUGINFO_$m)) \ + ) + +# No space before argument to avoid having to put $(strip ) everywhere in +# implementation above. +$(call SetupCopyDebuginfo,JDK) +$(call SetupCopyDebuginfo,JRE) + +################################################################################ # Include custom post hook here to make it possible to augment the target lists # before actual target prerequisites are declared. diff -r 8ff4232c93c2 -r 74cd426ebb3d make/Init.gmk --- a/make/Init.gmk Tue May 03 09:48:02 2016 -0700 +++ b/make/Init.gmk Tue May 03 12:25:20 2016 -0700 @@ -268,8 +268,13 @@ ############################################################################## MAIN_TARGETS := $(SEQUENTIAL_TARGETS) $(PARALLEL_TARGETS) $(COMPARE_BUILD_MAKE) + # If building the default target, add what they are to the description. + DESCRIPTION_TARGETS := $(strip $(MAIN_TARGETS)) + ifeq ($(DESCRIPTION_TARGETS), default) + DESCRIPTION_TARGETS += ($(DEFAULT_MAKE_TARGET)) + endif TARGET_DESCRIPTION := target$(if $(word 2, $(MAIN_TARGETS)),s) \ - '$(strip $(MAIN_TARGETS))' in configuration '$(CONF_NAME)' + '$(strip $(DESCRIPTION_TARGETS))' in configuration '$(CONF_NAME)' # MAKEOVERRIDES is automatically set and propagated by Make to sub-Make calls. # We need to clear it of the init-specific variables. The user-specified diff -r 8ff4232c93c2 -r 74cd426ebb3d make/Javadoc.gmk --- a/make/Javadoc.gmk Tue May 03 09:48:02 2016 -0700 +++ b/make/Javadoc.gmk Tue May 03 12:25:20 2016 -0700 @@ -1580,7 +1580,7 @@ JDKNET_PACKAGES_FILE = $(DOCSTMPDIR)/jdknet.packages # The modules required to be documented -JDKNET_MODULES = java.base +JDKNET_MODULES = jdk.net jdknetdocs: $(JDKNET_INDEX_HTML) diff -r 8ff4232c93c2 -r 74cd426ebb3d make/Jprt.gmk --- a/make/Jprt.gmk Tue May 03 09:48:02 2016 -0700 +++ b/make/Jprt.gmk Tue May 03 12:25:20 2016 -0700 @@ -108,8 +108,8 @@ SRC_JRE_MACOSX_BUNDLE_DIR := $(JRE_MACOSX_BUNDLE_DIR) # Bundle up the images -JPRT_TARGET ?= all -ifeq ($(JPRT_TARGET), all) +JPRT_TARGET ?= default +ifeq ($(JPRT_TARGET), default) bundles: $(JPRT_TARGET) @$(call TargetEnter) $(MKDIR) -p $(BUILD_OUTPUT)/bundles diff -r 8ff4232c93c2 -r 74cd426ebb3d make/Main.gmk --- a/make/Main.gmk Tue May 03 09:48:02 2016 -0700 +++ b/make/Main.gmk Tue May 03 12:25:20 2016 -0700 @@ -340,10 +340,10 @@ docs-jvmtidoc: +($(CD) $(SRC_ROOT)/make && $(MAKE) $(MAKE_ARGS) -f Javadoc.gmk jvmtidocs) -zip-docs: docs-javadoc docs-jvmtidoc +zip-docs: +($(CD) $(SRC_ROOT)/make && $(MAKE) $(MAKE_ARGS) -f Javadoc.gmk zip-docs) -ALL_TARGETS += docs-javadoc docs-jvmtidoc +ALL_TARGETS += docs-javadoc docs-jvmtidoc zip-docs ################################################################################ # Cross compilation support @@ -602,6 +602,8 @@ docs-jvmtidoc: hotspot + zip-docs: docs-javadoc docs-jvmtidoc + test: jimages test-image create-buildjdk-copy: jdk.jlink-java java.base-gendata @@ -703,7 +705,7 @@ endif # This target builds the documentation image -docs-image: zip-docs +docs-image: docs-javadoc docs-jvmtidoc # This target builds the test image test-image: prepare-test-image test-image-hotspot-jtreg-native \ @@ -727,7 +729,7 @@ docs: docs-image all: all-images -ALL_TARGETS += default jdk images docs all zip-docs +ALL_TARGETS += default jdk images docs all ################################################################################ ################################################################################ @@ -829,6 +831,10 @@ ################################################################################ + +# workaround issue when building open targets when closed jib-profiles.js is used +installer: product-images test-image + .PHONY: $(ALL_TARGETS) FRC: # Force target diff -r 8ff4232c93c2 -r 74cd426ebb3d make/common/Modules.gmk --- a/make/common/Modules.gmk Tue May 03 09:48:02 2016 -0700 +++ b/make/common/Modules.gmk Tue May 03 12:25:20 2016 -0700 @@ -63,6 +63,7 @@ java.xml.crypto \ jdk.httpserver \ jdk.management \ + jdk.net \ jdk.sctp \ jdk.security.auth \ jdk.security.jgss \ @@ -185,7 +186,8 @@ $(call GetModuleNameFromModuleInfo, $(MODULE_INFOS)))) FindImportedModules = \ - $(if $(IMPORT_MODULES_CLASSES), $(notdir $(wildcard $(IMPORT_MODULES_CLASSES)/*))) + $(filter-out $(MODULES_FILTER), \ + $(if $(IMPORT_MODULES_CLASSES), $(notdir $(wildcard $(IMPORT_MODULES_CLASSES)/*)))) # Find all source dirs for a particular module # $1 - Module to find source dirs for diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/.hgtags --- a/nashorn/.hgtags Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/.hgtags Tue May 03 12:25:20 2016 -0700 @@ -349,3 +349,4 @@ a5d1990fd32d908da8154d79116fce8013ba4d40 jdk-9+113 ba21793a0e4816283cc0ecdab5142a4959363529 jdk-9+114 295ac208a4443d433214d0c1f32d2ea45a3a32d2 jdk-9+115 +208388a5622dcca8227d6ad6c268f2c88087d283 jdk-9+116 diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Tue May 03 12:25:20 2016 -0700 @@ -4310,7 +4310,7 @@ * @param ident identifier for block or function where applicable */ private void printSymbols(final Block block, final FunctionNode function, final String ident) { - if (compiler.getScriptEnvironment()._print_symbols || function.getFlag(FunctionNode.IS_PRINT_SYMBOLS)) { + if (compiler.getScriptEnvironment()._print_symbols || function.getDebugFlag(FunctionNode.DEBUG_PRINT_SYMBOLS)) { final PrintWriter out = compiler.getScriptEnvironment().getErr(); out.println("[BLOCK in '" + ident + "']"); if (!block.printSymbols(out)) { diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilationPhase.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilationPhase.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilationPhase.java Tue May 03 12:25:20 2016 -0700 @@ -278,12 +278,12 @@ final PrintWriter err = senv.getErr(); //TODO separate phase for the debug printouts for abstraction and clarity - if (senv._print_lower_ast || fn.getFlag(FunctionNode.IS_PRINT_LOWER_AST)) { + if (senv._print_lower_ast || fn.getDebugFlag(FunctionNode.DEBUG_PRINT_LOWER_AST)) { err.println("Lower AST for: " + quote(newFunctionNode.getName())); err.println(new ASTWriter(newFunctionNode)); } - if (senv._print_lower_parse || fn.getFlag(FunctionNode.IS_PRINT_LOWER_PARSE)) { + if (senv._print_lower_parse || fn.getDebugFlag(FunctionNode.DEBUG_PRINT_LOWER_PARSE)) { err.println("Lower AST for: " + quote(newFunctionNode.getName())); err.println(new PrintVisitor(newFunctionNode)); } diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Tue May 03 12:25:20 2016 -0700 @@ -175,7 +175,9 @@ // we still use IS_SPLIT as the criteria in CompilationPhase.SERIALIZE_SPLIT_PHASE. FunctionNode.IS_ANONYMOUS | FunctionNode.USES_ANCESTOR_SCOPE | FunctionNode.IS_SPLIT, body, - null + null, + originalFn.getModule(), + originalFn.getDebugFlags() ) .setCompileUnit(lc, splitNode.getCompileUnit()); diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/WeighNodes.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/WeighNodes.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/WeighNodes.java Tue May 03 12:25:20 2016 -0700 @@ -435,7 +435,7 @@ } @Override - public Node leaveBIND(final BinaryNode binaryNode) { + public Node leaveARROW(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/AccessNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/AccessNode.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/AccessNode.java Tue May 03 12:25:20 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -48,12 +48,13 @@ * @param property property */ public AccessNode(final long token, final int finish, final Expression base, final String property) { - super(token, finish, base, false); + super(token, finish, base, false, false); this.property = property; } - private AccessNode(final AccessNode accessNode, final Expression base, final String property, final boolean isFunction, final Type type, final int id) { - super(accessNode, base, isFunction, type, id); + private AccessNode(final AccessNode accessNode, final Expression base, final String property, final boolean isFunction, + final Type type, final int id, final boolean isSuper) { + super(accessNode, base, isFunction, type, id, isSuper); this.property = property; } @@ -105,7 +106,7 @@ if (this.base == base) { return this; } - return new AccessNode(this, base, property, isFunction(), type, programPoint); + return new AccessNode(this, base, property, isFunction(), type, programPoint, isSuper()); } @Override @@ -113,7 +114,7 @@ if (this.type == type) { return this; } - return new AccessNode(this, base, property, isFunction(), type, programPoint); + return new AccessNode(this, base, property, isFunction(), type, programPoint, isSuper()); } @Override @@ -121,7 +122,7 @@ if (this.programPoint == programPoint) { return this; } - return new AccessNode(this, base, property, isFunction(), type, programPoint); + return new AccessNode(this, base, property, isFunction(), type, programPoint, isSuper()); } @Override @@ -129,6 +130,14 @@ if (isFunction()) { return this; } - return new AccessNode(this, base, property, true, type, programPoint); + return new AccessNode(this, base, property, true, type, programPoint, isSuper()); + } + + @Override + public AccessNode setIsSuper() { + if (isSuper()) { + return this; + } + return new AccessNode(this, base, property, isFunction(), type, programPoint, true); } } diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BaseNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BaseNode.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BaseNode.java Tue May 03 12:25:20 2016 -0700 @@ -52,6 +52,9 @@ /** Program point id */ protected final int programPoint; + /** Super property access. */ + private final boolean isSuper; + /** * Constructor * @@ -59,13 +62,15 @@ * @param finish finish * @param base base node * @param isFunction is this a function + * @param isSuper is this a super property access */ - public BaseNode(final long token, final int finish, final Expression base, final boolean isFunction) { + public BaseNode(final long token, final int finish, final Expression base, final boolean isFunction, final boolean isSuper) { super(token, base.getStart(), finish); this.base = base; this.isFunction = isFunction; this.type = null; this.programPoint = INVALID_PROGRAM_POINT; + this.isSuper = isSuper; } /** @@ -75,13 +80,15 @@ * @param isFunction is this a function * @param callSiteType the callsite type for this base node, either optimistic or conservative * @param programPoint program point id + * @param isSuper is this a super property access */ - protected BaseNode(final BaseNode baseNode, final Expression base, final boolean isFunction, final Type callSiteType, final int programPoint) { + protected BaseNode(final BaseNode baseNode, final Expression base, final boolean isFunction, final Type callSiteType, final int programPoint, final boolean isSuper) { super(baseNode); this.base = base; this.isFunction = isFunction; this.type = callSiteType; this.programPoint = programPoint; + this.isSuper = isSuper; } /** @@ -136,4 +143,17 @@ */ public abstract BaseNode setIsFunction(); + /** + * @return {@code true} if a SuperProperty access. + */ + public boolean isSuper() { + return isSuper; + } + + /** + * Mark this node as being a SuperProperty access. + * + * @return a base node identical to this one in all aspects except with its super flag set. + */ + public abstract BaseNode setIsSuper(); } diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java Tue May 03 12:25:20 2016 -0700 @@ -65,24 +65,39 @@ private final LocalVariableConversion conversion; /** Flag indicating that this block needs scope */ - public static final int NEEDS_SCOPE = 1 << 0; + public static final int NEEDS_SCOPE = 1 << 0; /** * Is this block tagged as terminal based on its contents * (usually the last statement) */ - public static final int IS_TERMINAL = 1 << 2; + public static final int IS_TERMINAL = 1 << 2; /** * Is this block the eager global scope - i.e. the original program. This isn't true for the * outermost level of recompiles */ - public static final int IS_GLOBAL_SCOPE = 1 << 3; + public static final int IS_GLOBAL_SCOPE = 1 << 3; /** * Is this block a synthetic one introduced by Parser? */ - public static final int IS_SYNTHETIC = 1 << 4; + public static final int IS_SYNTHETIC = 1 << 4; + + /** + * Is this the function body block? May not be the first, if parameter list contains expressions. + */ + public static final int IS_BODY = 1 << 5; + + /** + * Is this the parameter initialization block? If present, must be the first block, immediately wrapping the function body block. + */ + public static final int IS_PARAMETER_BLOCK = 1 << 6; + + /** + * Marks the variable declaration block for case clauses of a switch statement. + */ + public static final int IS_SWITCH_BLOCK = 1 << 7; /** * Constructor @@ -489,4 +504,31 @@ public Node accept(final NodeVisitor visitor) { return Acceptor.accept(this, visitor); } + + /** + * Checks if this is a function body. + * + * @return true if the function body flag is set + */ + public boolean isFunctionBody() { + return getFlag(IS_BODY); + } + + /** + * Checks if this is a parameter block. + * + * @return true if the parameter block flag is set + */ + public boolean isParameterBlock() { + return getFlag(IS_PARAMETER_BLOCK); + } + + /** + * Checks whether this is a switch block. + * + * @return true if this is a switch block + */ + public boolean isSwitchBlock() { + return getFlag(IS_SWITCH_BLOCK); + } } diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ClassNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ClassNode.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2015, 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.nashorn.internal.ir; + +import java.util.Collections; +import java.util.List; + +import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; + +/** + * IR representation for class definitions. + */ +public class ClassNode extends Expression { + private static final long serialVersionUID = 1L; + + private final IdentNode ident; + private final Expression classHeritage; + private final PropertyNode constructor; + private final List classElements; + private final int line; + + /** + * Constructor. + * + * @param line line number + * @param token token + * @param finish finish + * @param ident ident + * @param classHeritage class heritage + * @param constructor constructor + * @param classElements class elements + */ + public ClassNode(final int line, final long token, final int finish, final IdentNode ident, final Expression classHeritage, final PropertyNode constructor, + final List classElements) { + super(token, finish); + this.line = line; + this.ident = ident; + this.classHeritage = classHeritage; + this.constructor = constructor; + this.classElements = classElements; + } + + /** + * Class identifier. Optional. + * + * @return the class identifier + */ + public IdentNode getIdent() { + return ident; + } + + /** + * The expression of the {@code extends} clause. Optional. + * + * @return the class heritage + */ + public Expression getClassHeritage() { + return classHeritage; + } + + /** + * Get the constructor method definition. + * + * @return the constructor + */ + public PropertyNode getConstructor() { + return constructor; + } + + /** + * Get method definitions except the constructor. + * + * @return the class elements + */ + public List getClassElements() { + return Collections.unmodifiableList(classElements); + } + + /** + * Returns the line number. + * + * @return the line number + */ + public int getLineNumber() { + return line; + } + + @Override + public Type getType() { + return Type.OBJECT; + } + + @Override + public Node accept(final NodeVisitor visitor) { + if (visitor.enterClassNode(this)) { + return visitor.leaveClassNode(this); + } + + return this; + } + + @Override + public void toString(final StringBuilder sb, final boolean printType) { + sb.append("class"); + if (ident != null) { + sb.append(' '); + ident.toString(sb, printType); + } + if (classHeritage != null) { + sb.append(" extends"); + classHeritage.toString(sb, printType); + } + sb.append(" {"); + if (constructor != null) { + constructor.toString(sb, printType); + } + for (final PropertyNode classElement : classElements) { + sb.append(" "); + classElement.toString(sb, printType); + } + sb.append("}"); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ExpressionList.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ExpressionList.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2015, 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.nashorn.internal.ir; + +import java.util.Collections; +import java.util.List; + +import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; + +/** + * IR for CoverParenthesizedExpressionAndArrowParameterList, used only during parsing. + */ +public final class ExpressionList extends Expression { + private static final long serialVersionUID = 1L; + + private final List expressions; + + /** + * Constructor. + * + * @param token token + * @param finish finish + * @param expressions expression + */ + public ExpressionList(final long token, final int finish, final List expressions) { + super(token, finish); + this.expressions = expressions; + } + + /** + * Get the list of expressions. + * + * @return the list of expressions + */ + public List getExpressions() { + return Collections.unmodifiableList(expressions); + } + + @Override + public Node accept(final NodeVisitor visitor) { + throw new UnsupportedOperationException(); + } + + @Override + public Type getType() { + return null; + } + + @Override + public void toString(StringBuilder sb, boolean printType) { + sb.append("("); + boolean first = true; + for (Expression expression : expressions) { + if (first) { + first = false; + } else { + sb.append(", "); + } + expression.toString(sb, printType); + } + sb.append(")"); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java Tue May 03 12:25:20 2016 -0700 @@ -90,7 +90,6 @@ this.init = init; this.modify = modify; this.iterator = null; - } private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test, diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java Tue May 03 12:25:20 2016 -0700 @@ -69,7 +69,13 @@ /** a getter, @see {@link UserAccessorProperty} */ GETTER, /** a setter, @see {@link UserAccessorProperty} */ - SETTER + SETTER, + /** an arrow function */ + ARROW, + /** a generator function */ + GENERATOR, + /** a module function */ + MODULE } /** Source of entity. */ @@ -122,6 +128,12 @@ /** Root class for function */ private final Class rootClass; + /** The ES6 module */ + private final Module module; + + /** The debug flags */ + private final int debugFlags; + /** Is anonymous function flag. */ public static final int IS_ANONYMOUS = 1 << 0; @@ -172,49 +184,21 @@ /** * Is this function the top-level program? */ - public static final int IS_PROGRAM = 1 << 13; + public static final int IS_PROGRAM = 1 << 13; /** * Flag indicating whether this function uses the local variable symbol for itself. Only named function expressions * can have this flag set if they reference themselves (e.g. "(function f() { return f })". Declared functions will * use the symbol in their parent scope instead when they reference themselves by name. */ - public static final int USES_SELF_SYMBOL = 1 << 14; + public static final int USES_SELF_SYMBOL = 1 << 14; /** Does this function use the "this" keyword? */ - public static final int USES_THIS = 1 << 15; + public static final int USES_THIS = 1 << 15; /** Is this declared in a dynamic context */ - public static final int IN_DYNAMIC_CONTEXT = 1 << 16; - - /** - * The following flags are derived from directive comments within this function. - * Note that even IS_STRICT is one such flag but that requires special handling. - */ + public static final int IN_DYNAMIC_CONTEXT = 1 << 16; - /** parser, print parse tree */ - public static final int IS_PRINT_PARSE = 1 << 17; - /** parser, print lower parse tree */ - public static final int IS_PRINT_LOWER_PARSE = 1 << 18; - /** parser, print AST */ - public static final int IS_PRINT_AST = 1 << 19; - /** parser, print lower AST */ - public static final int IS_PRINT_LOWER_AST = 1 << 20; - /** parser, print symbols */ - public static final int IS_PRINT_SYMBOLS = 1 << 21; - - // callsite tracing, profiling within this function - /** profile callsites in this function? */ - public static final int IS_PROFILE = 1 << 22; - - /** trace callsite enterexit in this function? */ - public static final int IS_TRACE_ENTEREXIT = 1 << 23; - - /** trace callsite misses in this function? */ - public static final int IS_TRACE_MISSES = 1 << 24; - - /** trace callsite values in this function? */ - public static final int IS_TRACE_VALUES = 1 << 25; /** * Whether this function needs the callee {@link ScriptFunction} instance passed to its code as a @@ -222,18 +206,41 @@ * Rather, it is always calculated (see {@link #needsCallee()}). {@link RecompilableScriptFunctionData} * will, however, cache the value of this flag. */ - public static final int NEEDS_CALLEE = 1 << 26; + public static final int NEEDS_CALLEE = 1 << 17; /** * Is the function node cached? */ - public static final int IS_CACHED = 1 << 27; + public static final int IS_CACHED = 1 << 18; + + /** + * Does this function contain a super call? (cf. ES6 14.3.5 Static Semantics: HasDirectSuper) + */ + public static final int ES6_HAS_DIRECT_SUPER = 1 << 19; + + /** + * Does this function use the super binding? + */ + public static final int ES6_USES_SUPER = 1 << 20; - /** extension callsite flags mask */ - public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE | - IS_PRINT_LOWER_PARSE | IS_PRINT_AST | IS_PRINT_LOWER_AST | - IS_PRINT_SYMBOLS | IS_PROFILE | IS_TRACE_ENTEREXIT | - IS_TRACE_MISSES | IS_TRACE_VALUES; + /** + * Is this function a (class or object) method? + */ + public static final int ES6_IS_METHOD = 1 << 21; + + /** + * Is this the constructor method? + */ + public static final int ES6_IS_CLASS_CONSTRUCTOR = 1 << 22; + + /** Is this the constructor of a subclass (i.e., a class with an extends declaration)? */ + public static final int ES6_IS_SUBCLASS_CONSTRUCTOR = 1 << 23; + + /** is this a strong mode function? */ + public static final int ES6_IS_STRONG = 1 << 24; + + /** Does this function use new.target? */ + public static final int ES6_USES_NEW_TARGET = 1 << 25; /** Does this function or any nested functions contain an eval? */ private static final int HAS_DEEP_EVAL = HAS_EVAL | HAS_NESTED_EVAL; @@ -247,8 +254,44 @@ /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval, or it's the program. */ public static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL | IS_PROGRAM; + + /** + * The following flags are derived from directive comments within this function. + * Note that even IS_STRICT is one such flag but that requires special handling. + */ + + /** parser, print parse tree */ + public static final int DEBUG_PRINT_PARSE = 1 << 0; + /** parser, print lower parse tree */ + public static final int DEBUG_PRINT_LOWER_PARSE = 1 << 1; + /** parser, print AST */ + public static final int DEBUG_PRINT_AST = 1 << 2; + /** parser, print lower AST */ + public static final int DEBUG_PRINT_LOWER_AST = 1 << 3; + /** parser, print symbols */ + public static final int DEBUG_PRINT_SYMBOLS = 1 << 4; + + // callsite tracing, profiling within this function + /** profile callsites in this function? */ + public static final int DEBUG_PROFILE = 1 << 5; + + /** trace callsite enterexit in this function? */ + public static final int DEBUG_TRACE_ENTEREXIT = 1 << 6; + + /** trace callsite misses in this function? */ + public static final int DEBUG_TRACE_MISSES = 1 << 7; + + /** trace callsite values in this function? */ + public static final int DEBUG_TRACE_VALUES = 1 << 8; + + /** extension callsite flags mask */ + public static final int DEBUG_CALLSITE_FLAGS = DEBUG_PRINT_PARSE | + DEBUG_PRINT_LOWER_PARSE | DEBUG_PRINT_AST | DEBUG_PRINT_LOWER_AST | + DEBUG_PRINT_SYMBOLS | DEBUG_PROFILE | DEBUG_TRACE_ENTEREXIT | + DEBUG_TRACE_MISSES | DEBUG_TRACE_VALUES; + /** What is the return type of this function? */ - private Type returnType = Type.UNKNOWN; + public Type returnType = Type.UNKNOWN; /** * Constructor @@ -267,6 +310,8 @@ * @param flags initial flags * @param body body of the function * @param endParserState The parser state at the end of the parsing. + * @param module the module + * @param debugFlags the debug flags */ public FunctionNode( final Source source, @@ -282,7 +327,9 @@ final FunctionNode.Kind kind, final int flags, final Block body, - final Object endParserState) { + final Object endParserState, + final Module module, + final int debugFlags) { super(token, finish); this.source = source; @@ -299,7 +346,9 @@ this.body = body; this.thisProperties = 0; this.rootClass = null; - this.endParserState = endParserState; + this.endParserState = endParserState; + this.module = module; + this.debugFlags = debugFlags; } private FunctionNode( @@ -335,6 +384,8 @@ this.ident = functionNode.ident; this.kind = functionNode.kind; this.firstToken = functionNode.firstToken; + this.module = functionNode.module; + this.debugFlags = functionNode.debugFlags; } @Override @@ -366,23 +417,23 @@ } // quick check for extension callsite flags turned on by directives. - if ((flags & EXTENSION_CALLSITE_FLAGS) == 0) { + if ((debugFlags & DEBUG_CALLSITE_FLAGS) == 0) { return callsiteFlags; } - if (getFlag(IS_PROFILE)) { + if (getDebugFlag(DEBUG_PROFILE)) { callsiteFlags |= CALLSITE_PROFILE; } - if (getFlag(IS_TRACE_MISSES)) { + if (getDebugFlag(DEBUG_TRACE_MISSES)) { callsiteFlags |= CALLSITE_TRACE | CALLSITE_TRACE_MISSES; } - if (getFlag(IS_TRACE_VALUES)) { + if (getDebugFlag(DEBUG_TRACE_VALUES)) { callsiteFlags |= CALLSITE_TRACE | CALLSITE_TRACE_ENTEREXIT | CALLSITE_TRACE_VALUES; } - if (getFlag(IS_TRACE_ENTEREXIT)) { + if (getDebugFlag(DEBUG_TRACE_ENTEREXIT)) { callsiteFlags |= CALLSITE_TRACE | CALLSITE_TRACE_ENTEREXIT; } @@ -466,23 +517,23 @@ public static int getDirectiveFlag(final String directive) { switch (directive) { case "nashorn callsite trace enterexit": - return IS_TRACE_ENTEREXIT; + return DEBUG_TRACE_ENTEREXIT; case "nashorn callsite trace misses": - return IS_TRACE_MISSES; + return DEBUG_TRACE_MISSES; case "nashorn callsite trace objects": - return IS_TRACE_VALUES; + return DEBUG_TRACE_VALUES; case "nashorn callsite profile": - return IS_PROFILE; + return DEBUG_PROFILE; case "nashorn print parse": - return IS_PRINT_PARSE; + return DEBUG_PRINT_PARSE; case "nashorn print lower parse": - return IS_PRINT_LOWER_PARSE; + return DEBUG_PRINT_LOWER_PARSE; case "nashorn print ast": - return IS_PRINT_AST; + return DEBUG_PRINT_AST; case "nashorn print lower ast": - return IS_PRINT_LOWER_AST; + return DEBUG_PRINT_LOWER_AST; case "nashorn print symbols": - return IS_PRINT_SYMBOLS; + return DEBUG_PRINT_SYMBOLS; default: // unknown/unsupported directive return 0; @@ -579,6 +630,25 @@ } /** + * Returns the debug flags for this function. + * + * @return the debug flags + */ + public int getDebugFlags() { + return debugFlags; + } + + /** + * Checks whether a debug flag is set for this function. + * + * @param debugFlag the debug flag + * @return true if the flag is set + */ + public boolean getDebugFlag(final int debugFlag) { + return (debugFlags & debugFlag) != 0; + } + + /** * Returns true if the function is the top-level program. * @return True if this function node represents the top-level program. */ @@ -1065,6 +1135,86 @@ return setFlag(lc, IS_CACHED); } + /** + * Checks if the function is generated in strong mode. + * + * @return true if strong mode enabled for function + */ + public boolean isStrong() { + return getFlag(ES6_IS_STRONG); + } + + /** + * Checks if this is an ES6 method. + * + * @return true if the ES6 method flag is set + */ + public boolean isMethod() { + return getFlag(ES6_IS_METHOD); + } + + /** + * Checks if this function uses the ES6 super binding. + * + * @return true if the ES6 super flag is set + */ + public boolean usesSuper() { + return getFlag(ES6_USES_SUPER); + } + + /** + * Checks if this function directly uses the super binding. + * + * @return true if the ES6 has-direct-super flag is set + */ + public boolean hasDirectSuper() { + return getFlag(ES6_HAS_DIRECT_SUPER); + } + + /** + * Checks if this is an ES6 class constructor. + * + * @return true if the ES6 class constructor flag is set + */ + public boolean isClassConstructor() { + return getFlag(ES6_IS_CLASS_CONSTRUCTOR); + } + + /** + * Checks if this is an ES6 subclass constructor. + * + * @return true if the ES6 subclass constructor flag is set + */ + public boolean isSubclassConstructor() { + return getFlag(ES6_IS_SUBCLASS_CONSTRUCTOR); + } + + /** + * Checks if this function uses the ES6 new-targert. + * + * @return true if the ES6 new-target flag is set + */ + public boolean usesNewTarget() { + return getFlag(ES6_USES_NEW_TARGET); + } + + /** + * Checks if this is an ES6 module. + * + * @return true if this is an ES6 module + */ + public boolean isModule() { + return kind == Kind.MODULE; + } + + /** + * Returns the functions's ES6 module. + * + * @return the module, or null if this function is not part of one + */ + public Module getModule() { + return module; + } /** * Get the compile unit used to compile this function diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java Tue May 03 12:25:20 2016 -0700 @@ -49,6 +49,11 @@ private static final int FUTURESTRICT_NAME = 1 << 3; private static final int IS_DECLARED_HERE = 1 << 4; private static final int IS_DEAD = 1 << 5; + private static final int DIRECT_SUPER = 1 << 6; + private static final int REST_PARAMETER = 1 << 7; + private static final int PROTO_PROPERTY = 1 << 8; + private static final int DEFAULT_PARAMETER = 1 << 9; + private static final int DESTRUCTURED_PARAMETER = 1 << 10; /** Identifier. */ private final String name; @@ -382,4 +387,94 @@ public LocalVariableConversion getLocalVariableConversion() { return conversion; } + + /** + * Checks if this is a direct super identifier + * + * @return true if the direct super flag is set + */ + public boolean isDirectSuper() { + return (flags & DIRECT_SUPER) != 0; + } + + /** + * Return a new identifier with the direct super flag set. + * + * @return the new identifier + */ + public IdentNode setIsDirectSuper() { + return new IdentNode(this, name, type, flags | DIRECT_SUPER, programPoint, conversion); + } + + /** + * Checks if this is a rest parameter + * + * @return true if the rest parameter flag is set + */ + public boolean isRestParameter() { + return (flags & REST_PARAMETER) != 0; + } + + /** + * Return a new identifier with the rest parameter flag set. + * + * @return the new identifier + */ + public IdentNode setIsRestParameter() { + return new IdentNode(this, name, type, flags | REST_PARAMETER, programPoint, conversion); + } + + /** + * Checks if this is a proto property name. + * + * @return true if this is the proto property name + */ + public boolean isProtoPropertyName() { + return (flags & PROTO_PROPERTY) != 0; + } + + /** + * Return a new identifier with the proto property name flag set. + * + * @return the new identifier + */ + public IdentNode setIsProtoPropertyName() { + return new IdentNode(this, name, type, flags | PROTO_PROPERTY, programPoint, conversion); + } + + /** + * Checks whether this is a default parameter. + * + * @return true if this is a default parameter + */ + public boolean isDefaultParameter() { + return (flags & DEFAULT_PARAMETER) != 0; + } + + /** + * Return a new identifier with the default parameter flag set. + * + * @return the new identifier + */ + public IdentNode setIsDefaultParameter() { + return new IdentNode(this, name, type, flags | DEFAULT_PARAMETER, programPoint, conversion); + } + + /** + * Checks whether this is a destructured parameter. + * + * @return true if this is a destructured parameter + */ + public boolean isDestructuredParameter() { + return (flags & DESTRUCTURED_PARAMETER) != 0; + } + + /** + * Return a new identifier with the destructured parameter flag set. + * + * @return the new identifier + */ + public IdentNode setIsDestructuredParameter() { + return new IdentNode(this, name, type, flags | DESTRUCTURED_PARAMETER, programPoint, conversion); + } } diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IndexNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IndexNode.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IndexNode.java Tue May 03 12:25:20 2016 -0700 @@ -47,12 +47,13 @@ * @param index index for access */ public IndexNode(final long token, final int finish, final Expression base, final Expression index) { - super(token, finish, base, false); + super(token, finish, base, false, false); this.index = index; } - private IndexNode(final IndexNode indexNode, final Expression base, final Expression index, final boolean isFunction, final Type type, final int programPoint) { - super(indexNode, base, isFunction, type, programPoint); + private IndexNode(final IndexNode indexNode, final Expression base, final Expression index, final boolean isFunction, + final Type type, final int programPoint, final boolean isSuper) { + super(indexNode, base, isFunction, type, programPoint, isSuper); this.index = index; } @@ -101,7 +102,7 @@ if (this.base == base) { return this; } - return new IndexNode(this, base, index, isFunction(), type, programPoint); + return new IndexNode(this, base, index, isFunction(), type, programPoint, isSuper()); } /** @@ -113,7 +114,7 @@ if(this.index == index) { return this; } - return new IndexNode(this, base, index, isFunction(), type, programPoint); + return new IndexNode(this, base, index, isFunction(), type, programPoint, isSuper()); } @Override @@ -121,7 +122,7 @@ if (this.type == type) { return this; } - return new IndexNode(this, base, index, isFunction(), type, programPoint); + return new IndexNode(this, base, index, isFunction(), type, programPoint, isSuper()); } @Override @@ -129,7 +130,7 @@ if (isFunction()) { return this; } - return new IndexNode(this, base, index, true, type, programPoint); + return new IndexNode(this, base, index, true, type, programPoint, isSuper()); } @Override @@ -137,6 +138,14 @@ if (this.programPoint == programPoint) { return this; } - return new IndexNode(this, base, index, isFunction(), type, programPoint); + return new IndexNode(this, base, index, isFunction(), type, programPoint, isSuper()); + } + + @Override + public IndexNode setIsSuper() { + if (isSuper()) { + return this; + } + return new IndexNode(this, base, index, isFunction(), type, programPoint, true); } } diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Module.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Module.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2015, 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.nashorn.internal.ir; + +import java.util.List; + +/** + * ES6 Module information. + */ +public final class Module { + + /** The synthetic binding name assigned to export default declarations with unnamed expressions. */ + public static final String DEFAULT_EXPORT_BINDING_NAME = "*default*"; + + /** The {@code export default} name. */ + public static final String DEFAULT_NAME = "default"; + + /** The {@code export *} name. */ + public static final String STAR_NAME = "*"; + + /** + * A module ExportEntry record. + * + * @link http://www.ecma-international.org/ecma-262/6.0/#sec-source-text-module-records + */ + public static final class ExportEntry { + private final String exportName; + private final String moduleRequest; + private final String importName; + private final String localName; + + private ExportEntry(final String exportName, final String moduleRequest, final String importName, final String localName) { + this.exportName = exportName; + this.moduleRequest = moduleRequest; + this.importName = importName; + this.localName = localName; + } + + /** + * Creates a {@code export *} export entry. + * + * @param moduleRequest the module request + * @return the export entry + */ + public static ExportEntry exportStarFrom(final String moduleRequest) { + return new ExportEntry(null, moduleRequest, STAR_NAME, null); + } + + /** + * Creates a {@code export default} export entry. + * + * @return the export entry + */ + public static ExportEntry exportDefault() { + return exportDefault(DEFAULT_EXPORT_BINDING_NAME); + } + + /** + * Creates a {@code export default} export entry with a local name. + * + * @param localName the local name + * @return the export entry + */ + public static ExportEntry exportDefault(final String localName) { + return new ExportEntry(DEFAULT_NAME, null, null, localName); + } + + /** + * Creates a export entry with a local name and export name. + * + * @param exportName the export name + * @param localName the local name + * @return the export entry + */ + public static ExportEntry exportSpecifier(final String exportName, final String localName) { + return new ExportEntry(exportName, null, null, localName); + } + + /** + * Creates a export entry with an export name. + * + * @param exportName the export name + * @return the export entry + */ + public static ExportEntry exportSpecifier(final String exportName) { + return exportSpecifier(exportName, exportName); + } + + /** + * Create a copy of this entry with the specified {@code module request} string. + * + * @param moduleRequest the module request + * @return the new export entry + */ + public ExportEntry withFrom(@SuppressWarnings("hiding") final String moduleRequest) { + return new ExportEntry(exportName, moduleRequest, localName, null); + } + + /** + * Returns the entry's export name. + * + * @return the export name + */ + public String getExportName() { + return exportName; + } + + /** + * Returns the entry's module request. + * + * @return the module request + */ + public String getModuleRequest() { + return moduleRequest; + } + + /** + * Returns the entry's import name. + * + * @return the import name + */ + public String getImportName() { + return importName; + } + + /** + * Returns the entry's local name. + * + * @return the local name + */ + public String getLocalName() { + return localName; + } + + @Override + public String toString() { + return "ExportEntry [exportName=" + exportName + ", moduleRequest=" + moduleRequest + ", importName=" + importName + ", localName=" + localName + "]"; + } + } + + /** + * An ImportEntry record. + * + * @link http://www.ecma-international.org/ecma-262/6.0/#sec-source-text-module-records + */ + public static final class ImportEntry { + private final String moduleRequest; + private final String importName; + private final String localName; + + private ImportEntry(final String moduleRequest, final String importName, final String localName) { + this.moduleRequest = moduleRequest; + this.importName = importName; + this.localName = localName; + } + + /** + * Creates an import entry with default name. + * + * @param localName the local name + * @return the import entry + */ + public static ImportEntry importDefault(final String localName) { + return new ImportEntry(null, DEFAULT_NAME, localName); + } + + /** + * Creates an import entry with {@code *} import name. + * + * @param localName the local name + * @return the import entry + */ + public static ImportEntry importStarAsNameSpaceFrom(final String localName) { + return new ImportEntry(null, STAR_NAME, localName); + } + + /** + * Creates an import entry with the given import and local names. + * + * @param importName the import name + * @param localName the local name + * @return the import entry + */ + public static ImportEntry importSpecifier(final String importName, final String localName) { + return new ImportEntry(null, importName, localName); + } + + /** + * Creates a new import entry with the given import name. + * + * @param importName the import name + * @return the import entry + */ + public static ImportEntry importSpecifier(final String importName) { + return importSpecifier(importName, importName); + } + + /** + * Returns a copy of this import entry with the given module request. + * + * @param moduleRequest the module request + * @return the new import entry + */ + public ImportEntry withFrom(@SuppressWarnings("hiding") final String moduleRequest) { + return new ImportEntry(moduleRequest, importName, localName); + } + + /** + * Returns the entry's module request. + * + * @return the module request + */ + public String getModuleRequest() { + return moduleRequest; + } + + /** + * Returns the entry's import name. + * + * @return the import name + */ + public String getImportName() { + return importName; + } + + /** + * Returns the entry's local name. + * + * @return the local name + */ + public String getLocalName() { + return localName; + } + + @Override + public String toString() { + return "ImportEntry [moduleRequest=" + moduleRequest + ", importName=" + importName + ", localName=" + localName + "]"; + } + } + + private final List requestedModules; + private final List importEntries; + private final List localExportEntries; + private final List indirectExportEntries; + private final List starExportEntries; + + /** + * Creates a module with the specified requested modules and import and export entries. + * + * @param requestedModules the requested modules + * @param importEntries the import entries + * @param localExportEntries local export entries + * @param indirectExportEntries indirect export entries + * @param starExportEntries star export entries + */ + public Module(final List requestedModules, final List importEntries, final List localExportEntries, + final List indirectExportEntries, final List starExportEntries) { + this.requestedModules = requestedModules; + this.importEntries = importEntries; + this.localExportEntries = localExportEntries; + this.indirectExportEntries = indirectExportEntries; + this.starExportEntries = starExportEntries; + } + + /** + * Returns the list of requested modules. + * + * @return the requested modules + */ + public List getRequestedModules() { + return requestedModules; + } + + /** + * Returns the list of import entries. + * + * @return the import entries + */ + public List getImportEntries() { + return importEntries; + } + + /** + * Returns the list of local export entries. + * + * @return the local export entries + */ + public List getLocalExportEntries() { + return localExportEntries; + } + + /** + * Returns the list of indirect export entries. + * + * @return the indirect export entries + */ + public List getIndirectExportEntries() { + return indirectExportEntries; + } + + /** + * Returns the list of star export entries. + * + * @return the star export entries + */ + public List getStarExportEntries() { + return starExportEntries; + } + + @Override + public String toString() { + return "Module [requestedModules=" + requestedModules + ", importEntries=" + importEntries + ", localExportEntries=" + localExportEntries + ", indirectExportEntries=" + + indirectExportEntries + ", starExportEntries=" + starExportEntries + "]"; + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/PropertyNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/PropertyNode.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/PropertyNode.java Tue May 03 12:25:20 2016 -0700 @@ -36,7 +36,7 @@ private static final long serialVersionUID = 1L; /** Property key. */ - private final PropertyKey key; + private final Expression key; /** Property value. */ private final Expression value; @@ -47,6 +47,12 @@ /** Property getter. */ private final FunctionNode setter; + /** static property flag */ + private final boolean isStatic; + + /** Computed property flag */ + private final boolean computed; + /** * Constructor * @@ -56,21 +62,27 @@ * @param value the value of this property * @param getter getter function body * @param setter setter function body + * @param isStatic is this a static property? + * @param computed is this a computed property? */ - public PropertyNode(final long token, final int finish, final PropertyKey key, final Expression value, final FunctionNode getter, final FunctionNode setter) { + public PropertyNode(final long token, final int finish, final Expression key, final Expression value, final FunctionNode getter, final FunctionNode setter, final boolean isStatic, final boolean computed) { super(token, finish); this.key = key; this.value = value; this.getter = getter; this.setter = setter; + this.isStatic = isStatic; + this.computed = computed; } - private PropertyNode(final PropertyNode propertyNode, final PropertyKey key, final Expression value, final FunctionNode getter, final FunctionNode setter) { + private PropertyNode(final PropertyNode propertyNode, final Expression key, final Expression value, final FunctionNode getter, final FunctionNode setter, final boolean isStatic, final boolean computed) { super(propertyNode); this.key = key; this.value = value; this.getter = getter; this.setter = setter; + this.isStatic = isStatic; + this.computed = computed; } /** @@ -78,14 +90,14 @@ * @return key name */ public String getKeyName() { - return key.getPropertyName(); + return key instanceof PropertyKey ? ((PropertyKey) key).getPropertyName() : null; } @Override public Node accept(final NodeVisitor visitor) { if (visitor.enterPropertyNode(this)) { return visitor.leavePropertyNode( - setKey((PropertyKey)((Node)key).accept(visitor)). + setKey((Expression) key.accept(visitor)). setValue(value == null ? null : (Expression)value.accept(visitor)). setGetter(getter == null ? null : (FunctionNode)getter.accept(visitor)). setSetter(setter == null ? null : (FunctionNode)setter.accept(visitor))); @@ -134,7 +146,7 @@ if (this.getter == getter) { return this; } - return new PropertyNode(this, key, value, getter, setter); + return new PropertyNode(this, key, value, getter, setter, isStatic, computed); } /** @@ -142,14 +154,14 @@ * @return the key */ public Expression getKey() { - return (Expression)key; + return key; } - private PropertyNode setKey(final PropertyKey key) { + private PropertyNode setKey(final Expression key) { if (this.key == key) { return this; } - return new PropertyNode(this, key, value, getter, setter); + return new PropertyNode(this, key, value, getter, setter, isStatic, computed); } /** @@ -169,7 +181,7 @@ if (this.setter == setter) { return this; } - return new PropertyNode(this, key, value, getter, setter); + return new PropertyNode(this, key, value, getter, setter, isStatic, computed); } /** @@ -189,6 +201,24 @@ if (this.value == value) { return this; } - return new PropertyNode(this, key, value, getter, setter); - } + return new PropertyNode(this, key, value, getter, setter, isStatic, computed); + } + + /** + * Returns true if this is a static property. + * + * @return true if static flag is set + */ + public boolean isStatic() { + return isStatic; + } + + /** + * Returns true if this is a computed property. + * + * @return true if the computed flag is set + */ + public boolean isComputed() { + return computed; + } } diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java Tue May 03 12:25:20 2016 -0700 @@ -133,8 +133,8 @@ return enterASSIGN_SHR(binaryNode); case ASSIGN_SUB: return enterASSIGN_SUB(binaryNode); - case BIND: - return enterBIND(binaryNode); + case ARROW: + return enterARROW(binaryNode); case BIT_AND: return enterBIT_AND(binaryNode); case BIT_OR: @@ -217,8 +217,8 @@ return leaveASSIGN_SHR(binaryNode); case ASSIGN_SUB: return leaveASSIGN_SUB(binaryNode); - case BIND: - return leaveBIND(binaryNode); + case ARROW: + return leaveARROW(binaryNode); case BIT_AND: return leaveBIT_AND(binaryNode); case BIT_OR: @@ -735,22 +735,22 @@ } /** - * Binary enter - callback for entering a bind operator + * Binary enter - callback for entering a arrow operator * * @param binaryNode the node * @return true if traversal should continue and node children be traversed, false otherwise */ - public boolean enterBIND(final BinaryNode binaryNode) { + public boolean enterARROW(final BinaryNode binaryNode) { return enterDefault(binaryNode); } /** - * Binary leave - callback for leaving a bind operator + * Binary leave - callback for leaving a arrow operator * * @param binaryNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leaveBIND(final BinaryNode binaryNode) { + public Node leaveARROW(final BinaryNode binaryNode) { return leaveDefault(binaryNode); } diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeVisitor.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Tue May 03 12:25:20 2016 -0700 @@ -33,6 +33,7 @@ import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; +import jdk.nashorn.internal.ir.ClassNode; import jdk.nashorn.internal.ir.ContinueNode; import jdk.nashorn.internal.ir.DebuggerNode; import jdk.nashorn.internal.ir.EmptyNode; @@ -897,5 +898,23 @@ return leaveDefault(withNode); } + /** + * Callback for entering a ClassNode + * + * @param classNode the node + * @return true if traversal should continue and node children be traversed, false otherwise + */ + public boolean enterClassNode(final ClassNode classNode) { + return enterDefault(classNode); + } + /** + * Callback for leaving a ClassNode + * + * @param classNode the node + * @return processed node, which will replace the original one, or the original node + */ + public Node leaveClassNode(final ClassNode classNode) { + return leaveDefault(classNode); + } } diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java Tue May 03 12:25:20 2016 -0700 @@ -28,35 +28,55 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.ANON_FUNCTION_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL; import static jdk.nashorn.internal.codegen.CompilerConstants.PROGRAM; +import static jdk.nashorn.internal.parser.TokenType.ARROW; import static jdk.nashorn.internal.parser.TokenType.ASSIGN; import static jdk.nashorn.internal.parser.TokenType.CASE; import static jdk.nashorn.internal.parser.TokenType.CATCH; +import static jdk.nashorn.internal.parser.TokenType.CLASS; import static jdk.nashorn.internal.parser.TokenType.COLON; import static jdk.nashorn.internal.parser.TokenType.COMMARIGHT; +import static jdk.nashorn.internal.parser.TokenType.COMMENT; import static jdk.nashorn.internal.parser.TokenType.CONST; import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX; import static jdk.nashorn.internal.parser.TokenType.DECPREFIX; +import static jdk.nashorn.internal.parser.TokenType.ELLIPSIS; import static jdk.nashorn.internal.parser.TokenType.ELSE; import static jdk.nashorn.internal.parser.TokenType.EOF; import static jdk.nashorn.internal.parser.TokenType.EOL; +import static jdk.nashorn.internal.parser.TokenType.EQ_STRICT; +import static jdk.nashorn.internal.parser.TokenType.ESCSTRING; +import static jdk.nashorn.internal.parser.TokenType.EXPORT; +import static jdk.nashorn.internal.parser.TokenType.EXTENDS; import static jdk.nashorn.internal.parser.TokenType.FINALLY; import static jdk.nashorn.internal.parser.TokenType.FUNCTION; import static jdk.nashorn.internal.parser.TokenType.IDENT; import static jdk.nashorn.internal.parser.TokenType.IF; +import static jdk.nashorn.internal.parser.TokenType.IMPORT; import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX; import static jdk.nashorn.internal.parser.TokenType.LBRACE; +import static jdk.nashorn.internal.parser.TokenType.LBRACKET; import static jdk.nashorn.internal.parser.TokenType.LET; import static jdk.nashorn.internal.parser.TokenType.LPAREN; +import static jdk.nashorn.internal.parser.TokenType.MUL; +import static jdk.nashorn.internal.parser.TokenType.PERIOD; import static jdk.nashorn.internal.parser.TokenType.RBRACE; import static jdk.nashorn.internal.parser.TokenType.RBRACKET; import static jdk.nashorn.internal.parser.TokenType.RPAREN; import static jdk.nashorn.internal.parser.TokenType.SEMICOLON; +import static jdk.nashorn.internal.parser.TokenType.SPREAD_ARRAY; +import static jdk.nashorn.internal.parser.TokenType.STATIC; +import static jdk.nashorn.internal.parser.TokenType.STRING; +import static jdk.nashorn.internal.parser.TokenType.SUPER; import static jdk.nashorn.internal.parser.TokenType.TEMPLATE; import static jdk.nashorn.internal.parser.TokenType.TEMPLATE_HEAD; import static jdk.nashorn.internal.parser.TokenType.TEMPLATE_MIDDLE; import static jdk.nashorn.internal.parser.TokenType.TEMPLATE_TAIL; import static jdk.nashorn.internal.parser.TokenType.TERNARY; +import static jdk.nashorn.internal.parser.TokenType.VAR; +import static jdk.nashorn.internal.parser.TokenType.VOID; import static jdk.nashorn.internal.parser.TokenType.WHILE; +import static jdk.nashorn.internal.parser.TokenType.YIELD; +import static jdk.nashorn.internal.parser.TokenType.YIELD_STAR; import java.io.Serializable; import java.util.ArrayDeque; @@ -68,6 +88,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.codegen.Namespace; import jdk.nashorn.internal.ir.AccessNode; @@ -79,11 +101,13 @@ import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; +import jdk.nashorn.internal.ir.ClassNode; import jdk.nashorn.internal.ir.ContinueNode; import jdk.nashorn.internal.ir.DebuggerNode; import jdk.nashorn.internal.ir.EmptyNode; import jdk.nashorn.internal.ir.ErrorNode; import jdk.nashorn.internal.ir.Expression; +import jdk.nashorn.internal.ir.ExpressionList; import jdk.nashorn.internal.ir.ExpressionStatement; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; @@ -92,7 +116,9 @@ import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.JoinPredecessorExpression; import jdk.nashorn.internal.ir.LabelNode; +import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; +import jdk.nashorn.internal.ir.Module; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ObjectNode; import jdk.nashorn.internal.ir.PropertyKey; @@ -110,6 +136,7 @@ import jdk.nashorn.internal.ir.WithNode; import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.debug.PrintVisitor; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ErrorManager; import jdk.nashorn.internal.runtime.JSErrorType; @@ -254,6 +281,14 @@ } /** + * Set up first token. Skips opening EOL. + */ + private void scanFirstToken() { + k = -1; + next(); + } + + /** * Execute parse and return the resulting function node. * Errors will be thrown and the error manager will contain information * if parsing should fail @@ -280,9 +315,7 @@ lexer.line = lexer.pendingLine = lineOffset + 1; line = lineOffset; - // Set up first token (skips opening EOL.) - k = -1; - next(); + scanFirstToken(); // Begin parse. return program(scriptName, allowPropertyFunction); } catch (final Exception e) { @@ -301,6 +334,44 @@ } /** + * Parse and return the resulting module. + * Errors will be thrown and the error manager will contain information + * if parsing should fail + * + * @param moduleName name for the module, given to the parsed FunctionNode + * @param startPos start position in source + * @param len length of parse + * + * @return function node resulting from successful parse + */ + public FunctionNode parseModule(final String moduleName, final int startPos, final int len) { + try { + stream = new TokenStream(); + lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions, env._es6, reparsedFunction != null); + lexer.line = lexer.pendingLine = lineOffset + 1; + line = lineOffset; + + scanFirstToken(); + // Begin parse. + return module(moduleName); + } catch (final Exception e) { + handleParseException(e); + + return null; + } + } + + /** + * Entry point for parsing a module. + * + * @param moduleName the module name + * @return the parsed module + */ + public FunctionNode parseModule(final String moduleName) { + return parseModule(moduleName, 0, source.getLength()); + } + + /** * Parse and return the list of function parameter list. A comma * separated list of function parameter identifiers is expected to be parsed. * Errors will be thrown and the error manager will contain information @@ -314,11 +385,9 @@ stream = new TokenStream(); lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions, env._es6); - // Set up first token (skips opening EOL.) - k = -1; - next(); - - return formalParameterList(TokenType.EOF); + scanFirstToken(); + + return formalParameterList(TokenType.EOF, false); } catch (final Exception e) { handleParseException(e); return null; @@ -339,9 +408,7 @@ lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions, env._es6); final int functionLine = line; - // Set up first token (skips opening EOL.) - k = -1; - next(); + scanFirstToken(); // Make a fake token for the function. final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength()); @@ -432,7 +499,7 @@ } // Skip to a recovery point. -loop: + loop: while (true) { switch (type) { case EOF: @@ -474,7 +541,7 @@ sb.append(ident.getName()); final String name = namespace.uniqueName(sb.toString()); - assert parentFunction != null || name.equals(PROGRAM.symbolName()) || name.startsWith(RecompilableScriptFunctionData.RECOMPILATION_PREFIX) : "name = " + name; + assert parentFunction != null || name.equals(PROGRAM.symbolName()) : "name = " + name; int flags = 0; if (isStrictMode) { @@ -489,7 +556,8 @@ return functionNode; } - private FunctionNode createFunctionNode(final ParserContextFunctionNode function, final long startToken, final IdentNode ident, final List parameters, final FunctionNode.Kind kind, final int functionLine, final Block body){ + private FunctionNode createFunctionNode(final ParserContextFunctionNode function, final long startToken, final IdentNode ident, final List parameters, final FunctionNode.Kind kind, final int functionLine, final Block body) { + // assert body.isFunctionBody() || body.getFlag(Block.IS_PARAMETER_BLOCK) && ((BlockStatement) body.getLastStatement()).getBlock().isFunctionBody(); // Start new block. final FunctionNode functionNode = new FunctionNode( @@ -506,7 +574,9 @@ kind, function.getFlags(), body, - function.getEndParserState()); + function.getEndParserState(), + function.getModule(), + function.getDebugFlags()); printAST(functionNode); @@ -544,23 +614,39 @@ expect(RBRACE); } - final int flags = newBlock.getFlags() | (needsBraces? 0 : Block.IS_SYNTHETIC); + final int flags = newBlock.getFlags() | (needsBraces ? 0 : Block.IS_SYNTHETIC); return new Block(blockToken, finish, flags, newBlock.getStatements()); } + /** + * Get the statements in a case clause. + */ + private List caseStatementList() { + final ParserContextBlockNode newBlock = newBlock(); + try { + statementList(); + } finally { + restoreBlock(newBlock); + } + return newBlock.getStatements(); + } /** * Get all the statements generated by a single statement. * @return Statements. */ private Block getStatement() { + return getStatement(false); + } + + private Block getStatement(boolean labelledStatement) { if (type == LBRACE) { return getBlock(true); } // Set up new block. Captures first token. final ParserContextBlockNode newBlock = newBlock(); try { - statement(false, false, true); + statement(false, false, true, labelledStatement); } finally { restoreBlock(newBlock); } @@ -576,6 +662,9 @@ if (EVAL.symbolName().equals(name)) { markEval(lc); + } else if (SUPER.getName().equals(name)) { + assert ident.isDirectSuper(); + markSuperCall(lc); } } @@ -585,7 +674,8 @@ */ private void detectSpecialProperty(final IdentNode ident) { if (isArguments(ident)) { - lc.getCurrentFunction().setFlag(FunctionNode.USES_ARGUMENTS); + // skip over arrow functions, e.g. function f() { return (() => arguments.length)(); } + getCurrentNonArrowFunction().setFlag(FunctionNode.USES_ARGUMENTS); } } @@ -593,11 +683,15 @@ return env._es6; } + private boolean isES6() { + return env._es6; + } + private static boolean isArguments(final String name) { return ARGUMENTS_NAME.equals(name); } - private static boolean isArguments(final IdentNode ident) { + static boolean isArguments(final IdentNode ident) { return isArguments(ident.getName()); } @@ -634,20 +728,20 @@ case ASSIGN_SHL: case ASSIGN_SHR: case ASSIGN_SUB: - if (!(lhs instanceof AccessNode || - lhs instanceof IndexNode || - lhs instanceof IdentNode)) { - return referenceError(lhs, rhs, env._early_lvalue_error); - } - if (lhs instanceof IdentNode) { if (!checkIdentLValue((IdentNode)lhs)) { return referenceError(lhs, rhs, false); } - verifyStrictIdent((IdentNode)lhs, "assignment"); + verifyIdent((IdentNode)lhs, "assignment"); + break; + } else if (lhs instanceof AccessNode || lhs instanceof IndexNode) { + break; + } else if (opType == ASSIGN && isDestructuringLhs(lhs)) { + verifyDestructuringAssignmentPattern(lhs, "assignment"); + break; + } else { + return referenceError(lhs, rhs, env._early_lvalue_error); } - break; - default: break; } @@ -659,6 +753,114 @@ return new BinaryNode(op, lhs, rhs); } + private boolean isDestructuringLhs(Expression lhs) { + if (lhs instanceof ObjectNode || lhs instanceof LiteralNode.ArrayLiteralNode) { + return isES6(); + } + return false; + } + + private void verifyDestructuringAssignmentPattern(Expression pattern, String contextString) { + assert pattern instanceof ObjectNode || pattern instanceof LiteralNode.ArrayLiteralNode; + pattern.accept(new NodeVisitor(new LexicalContext()) { + @Override + public boolean enterLiteralNode(LiteralNode literalNode) { + if (literalNode.isArray()) { + boolean restElement = false; + for (Expression element : literalNode.getElementExpressions()) { + if (element != null) { + if (restElement) { + throw error(String.format("Unexpected element after rest element"), element.getToken()); + } + if (element.isTokenType(SPREAD_ARRAY)) { + restElement = true; + Expression lvalue = ((UnaryNode) element).getExpression(); + if (!checkValidLValue(lvalue, contextString)) { + throw error(AbstractParser.message("invalid.lvalue"), lvalue.getToken()); + } + } + element.accept(this); + } + } + return false; + } else { + return enterDefault(literalNode); + } + } + + @Override + public boolean enterObjectNode(ObjectNode objectNode) { + return true; + } + + @Override + public boolean enterPropertyNode(PropertyNode propertyNode) { + if (propertyNode.getValue() != null) { + propertyNode.getValue().accept(this); + return false; + } else { + return enterDefault(propertyNode); + } + } + + @Override + public boolean enterIdentNode(IdentNode identNode) { + verifyIdent(identNode, contextString); + if (!checkIdentLValue(identNode)) { + referenceError(identNode, null, true); + return false; + } + return false; + } + + @Override + public boolean enterAccessNode(AccessNode accessNode) { + return false; + } + + @Override + public boolean enterIndexNode(IndexNode indexNode) { + return false; + } + + @Override + public boolean enterBinaryNode(BinaryNode binaryNode) { + if (binaryNode.isTokenType(ASSIGN)) { + binaryNode.lhs().accept(this); + // Initializer(rhs) can be any AssignmentExpression + return false; + } else { + return enterDefault(binaryNode); + } + } + + @Override + public boolean enterUnaryNode(UnaryNode unaryNode) { + if (unaryNode.isTokenType(SPREAD_ARRAY)) { + // rest element + return true; + } else { + return enterDefault(unaryNode); + } + } + + @Override + protected boolean enterDefault(Node node) { + throw error(String.format("unexpected node in AssignmentPattern: %s", node)); + } + }); + } + + private static Expression newBinaryExpression(final long op, final Expression lhs, final Expression rhs) { + final TokenType opType = Token.descType(op); + + // Build up node. + if (BinaryNode.isLogical(opType)) { + return new BinaryNode(op, new JoinPredecessorExpression(lhs), new JoinPredecessorExpression(rhs)); + } + return new BinaryNode(op, lhs, rhs); + } + /** * Reduce increment/decrement to simpler operations. @@ -717,7 +919,7 @@ restoreBlock(body); body.setFlag(Block.NEEDS_SCOPE); - final Block programBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC, body.getStatements()); + final Block programBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC | Block.IS_BODY, body.getStatements()); lc.pop(script); script.setLastToken(token); @@ -776,7 +978,7 @@ try { // Get the next element. - statement(true, allowPropertyFunction, false); + statement(true, allowPropertyFunction, false, false); allowPropertyFunction = false; // check for directive prologues @@ -816,16 +1018,16 @@ // verify that function name as well as parameter names // satisfy strict mode restrictions. - verifyStrictIdent(function.getIdent(), "function name"); + verifyIdent(function.getIdent(), "function name"); for (final IdentNode param : function.getParameters()) { - verifyStrictIdent(param, "function parameter"); + verifyIdent(param, "function parameter"); } } } else if (Context.DEBUG) { - final int flag = FunctionNode.getDirectiveFlag(directive); - if (flag != 0) { + final int debugFlag = FunctionNode.getDirectiveFlag(directive); + if (debugFlag != 0) { final ParserContextFunctionNode function = lc.getCurrentFunction(); - function.setFlag(flag); + function.setDebugFlag(debugFlag); } } } @@ -849,29 +1051,53 @@ } /** + * Parse any of the basic statement types. + * * Statement : - * Block + * BlockStatement * VariableStatement * EmptyStatement * ExpressionStatement * IfStatement - * IterationStatement + * BreakableStatement * ContinueStatement * BreakStatement * ReturnStatement * WithStatement * LabelledStatement - * SwitchStatement * ThrowStatement * TryStatement * DebuggerStatement * - * see 12 + * BreakableStatement : + * IterationStatement + * SwitchStatement + * + * BlockStatement : + * Block + * + * Block : + * { StatementList opt } * - * Parse any of the basic statement types. + * StatementList : + * StatementListItem + * StatementList StatementListItem + * + * StatementItem : + * Statement + * Declaration + * + * Declaration : + * HoistableDeclaration + * ClassDeclaration + * LexicalDeclaration + * + * HoistableDeclaration : + * FunctionDeclaration + * GeneratorDeclaration */ private void statement() { - statement(false, false, false); + statement(false, false, false, false); } /** @@ -879,14 +1105,7 @@ * @param allowPropertyFunction allow property "get" and "set" functions? * @param singleStatement are we in a single statement context? */ - private void statement(final boolean topLevel, final boolean allowPropertyFunction, final boolean singleStatement) { - if (type == FUNCTION) { - // As per spec (ECMA section 12), function declarations as arbitrary statement - // is not "portable". Implementation can issue a warning or disallow the same. - functionExpression(true, topLevel); - return; - } - + private void statement(final boolean topLevel, final boolean allowPropertyFunction, final boolean singleStatement, final boolean labelledStatement) { switch (type) { case LBRACE: block(); @@ -918,9 +1137,6 @@ case RETURN: returnStatement(); break; - case YIELD: - yieldStatement(); - break; case WITH: withStatement(); break; @@ -941,13 +1157,32 @@ case EOF: expect(SEMICOLON); break; + case FUNCTION: + // As per spec (ECMA section 12), function declarations as arbitrary statement + // is not "portable". Implementation can issue a warning or disallow the same. + if (singleStatement) { + // ES6 B.3.2 Labelled Function Declarations + // It is a Syntax Error if any strict mode source code matches this rule: + // LabelledItem : FunctionDeclaration. + if (!labelledStatement || isStrictMode) { + throw error(AbstractParser.message("expected.stmt", "function declaration"), token); + } + } + functionExpression(true, topLevel || labelledStatement); + return; default: - if (useBlockScope() && (type == LET || type == CONST)) { + if (useBlockScope() && (type == LET && lookaheadIsLetDeclaration(false) || type == CONST)) { if (singleStatement) { throw error(AbstractParser.message("expected.stmt", type.getName() + " declaration"), token); } variableStatement(type); break; + } else if (type == CLASS && isES6()) { + if (singleStatement) { + throw error(AbstractParser.message("expected.stmt", "class declaration"), token); + } + classDeclaration(false); + break; } if (env._const_as_var && type == CONST) { variableStatement(TokenType.VAR); @@ -963,11 +1198,11 @@ final String ident = (String)getValue(); final long propertyToken = token; final int propertyLine = line; - if("get".equals(ident)) { + if ("get".equals(ident)) { next(); addPropertyFunctionStatement(propertyGetterFunction(propertyToken, propertyLine)); return; - } else if("set".equals(ident)) { + } else if ("set".equals(ident)) { next(); addPropertyFunctionStatement(propertySetterFunction(propertyToken, propertyLine)); return; @@ -986,6 +1221,267 @@ } /** + * ClassDeclaration[Yield, Default] : + * class BindingIdentifier[?Yield] ClassTail[?Yield] + * [+Default] class ClassTail[?Yield] + */ + private ClassNode classDeclaration(boolean isDefault) { + int classLineNumber = line; + + ClassNode classExpression = classExpression(!isDefault); + + if (!isDefault) { + VarNode classVar = new VarNode(classLineNumber, classExpression.getToken(), classExpression.getIdent().getFinish(), classExpression.getIdent(), classExpression, VarNode.IS_CONST); + appendStatement(classVar); + } + return classExpression; + } + + /** + * ClassExpression[Yield] : + * class BindingIdentifier[?Yield]opt ClassTail[?Yield] + */ + private ClassNode classExpression(boolean isStatement) { + assert type == CLASS; + int classLineNumber = line; + long classToken = token; + next(); + + IdentNode className = null; + if (isStatement || type == IDENT) { + className = getIdent(); + } + + return classTail(classLineNumber, classToken, className); + } + + private static final class ClassElementKey { + private final boolean isStatic; + private final String propertyName; + + private ClassElementKey(boolean isStatic, String propertyName) { + this.isStatic = isStatic; + this.propertyName = propertyName; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (isStatic ? 1231 : 1237); + result = prime * result + ((propertyName == null) ? 0 : propertyName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ClassElementKey) { + ClassElementKey other = (ClassElementKey) obj; + return this.isStatic == other.isStatic && Objects.equals(this.propertyName, other.propertyName); + } + return false; + } + } + + /** + * Parse ClassTail and ClassBody. + * + * ClassTail[Yield] : + * ClassHeritage[?Yield]opt { ClassBody[?Yield]opt } + * ClassHeritage[Yield] : + * extends LeftHandSideExpression[?Yield] + * + * ClassBody[Yield] : + * ClassElementList[?Yield] + * ClassElementList[Yield] : + * ClassElement[?Yield] + * ClassElementList[?Yield] ClassElement[?Yield] + * ClassElement[Yield] : + * MethodDefinition[?Yield] + * static MethodDefinition[?Yield] + * ; + */ + private ClassNode classTail(final int classLineNumber, final long classToken, final IdentNode className) { + final boolean oldStrictMode = isStrictMode; + isStrictMode = true; + try { + Expression classHeritage = null; + if (type == EXTENDS) { + next(); + classHeritage = leftHandSideExpression(); + } + + expect(LBRACE); + + PropertyNode constructor = null; + final ArrayList classElements = new ArrayList<>(); + final Map keyToIndexMap = new HashMap<>(); + for (;;) { + if (type == SEMICOLON) { + next(); + continue; + } + if (type == RBRACE) { + break; + } + final long classElementToken = token; + boolean isStatic = false; + if (type == STATIC) { + isStatic = true; + next(); + } + boolean generator = false; + if (isES6() && type == MUL) { + generator = true; + next(); + } + final PropertyNode classElement = methodDefinition(isStatic, classHeritage != null, generator); + if (classElement.isComputed()) { + classElements.add(classElement); + } else if (!classElement.isStatic() && classElement.getKeyName().equals("constructor")) { + if (constructor == null) { + constructor = classElement; + } else { + throw error(AbstractParser.message("multiple.constructors"), classElementToken); + } + } else { + // Check for duplicate method definitions and combine accessor methods. + // In ES6, a duplicate is never an error regardless of strict mode (in consequence of computed property names). + + final ClassElementKey key = new ClassElementKey(classElement.isStatic(), classElement.getKeyName()); + final Integer existing = keyToIndexMap.get(key); + + if (existing == null) { + keyToIndexMap.put(key, classElements.size()); + classElements.add(classElement); + } else { + final PropertyNode existingProperty = classElements.get(existing); + + final Expression value = classElement.getValue(); + final FunctionNode getter = classElement.getGetter(); + final FunctionNode setter = classElement.getSetter(); + + if (value != null || existingProperty.getValue() != null) { + keyToIndexMap.put(key, classElements.size()); + classElements.add(classElement); + } else if (getter != null) { + assert existingProperty.getGetter() != null || existingProperty.getSetter() != null; + classElements.set(existing, existingProperty.setGetter(getter)); + } else if (setter != null) { + assert existingProperty.getGetter() != null || existingProperty.getSetter() != null; + classElements.set(existing, existingProperty.setSetter(setter)); + } + } + } + } + + final long lastToken = token; + expect(RBRACE); + + if (constructor == null) { + constructor = createDefaultClassConstructor(classLineNumber, classToken, lastToken, className, classHeritage != null); + } + + classElements.trimToSize(); + return new ClassNode(classLineNumber, classToken, finish, className, classHeritage, constructor, classElements); + } finally { + isStrictMode = oldStrictMode; + } + } + + private PropertyNode createDefaultClassConstructor(int classLineNumber, long classToken, long lastToken, IdentNode className, boolean subclass) { + final int ctorFinish = finish; + final List statements; + final List parameters; + final long identToken = Token.recast(classToken, TokenType.IDENT); + if (subclass) { + final IdentNode superIdent = createIdentNode(identToken, ctorFinish, SUPER.getName()).setIsDirectSuper(); + final IdentNode argsIdent = createIdentNode(identToken, ctorFinish, "args").setIsRestParameter(); + final Expression spreadArgs = new UnaryNode(Token.recast(classToken, TokenType.SPREAD_ARGUMENT), argsIdent); + final CallNode superCall = new CallNode(classLineNumber, classToken, ctorFinish, superIdent, Collections.singletonList(spreadArgs), false); + statements = Collections.singletonList(new ExpressionStatement(classLineNumber, classToken, ctorFinish, superCall)); + parameters = Collections.singletonList(argsIdent); + } else { + statements = Collections.emptyList(); + parameters = Collections.emptyList(); + } + + final Block body = new Block(classToken, ctorFinish, Block.IS_BODY, statements); + final IdentNode ctorName = className != null ? className : createIdentNode(identToken, ctorFinish, "constructor"); + final ParserContextFunctionNode function = createParserContextFunctionNode(ctorName, classToken, FunctionNode.Kind.NORMAL, classLineNumber, parameters); + function.setLastToken(lastToken); + + function.setFlag(FunctionNode.ES6_IS_METHOD); + function.setFlag(FunctionNode.ES6_IS_CLASS_CONSTRUCTOR); + if (subclass) { + function.setFlag(FunctionNode.ES6_IS_SUBCLASS_CONSTRUCTOR); + function.setFlag(FunctionNode.ES6_HAS_DIRECT_SUPER); + } + if (className == null) { + function.setFlag(FunctionNode.IS_ANONYMOUS); + } + + final PropertyNode constructor = new PropertyNode(classToken, ctorFinish, ctorName, createFunctionNode( + function, + classToken, + ctorName, + parameters, + FunctionNode.Kind.NORMAL, + classLineNumber, + body + ), null, null, false, false); + return constructor; + } + + private PropertyNode methodDefinition(final boolean isStatic, final boolean subclass, final boolean generator) { + final long methodToken = token; + final int methodLine = line; + final boolean computed = type == LBRACKET; + final boolean isIdent = type == IDENT; + final Expression propertyName = propertyName(); + int flags = FunctionNode.ES6_IS_METHOD; + if (!computed) { + final String name = ((PropertyKey)propertyName).getPropertyName(); + if (!generator && isIdent && type != LPAREN && name.equals("get")) { + final PropertyFunction methodDefinition = propertyGetterFunction(methodToken, methodLine, flags); + verifyAllowedMethodName(methodDefinition.key, isStatic, methodDefinition.computed, generator, true); + return new PropertyNode(methodToken, finish, methodDefinition.key, null, methodDefinition.functionNode, null, isStatic, methodDefinition.computed); + } else if (!generator && isIdent && type != LPAREN && name.equals("set")) { + final PropertyFunction methodDefinition = propertySetterFunction(methodToken, methodLine, flags); + verifyAllowedMethodName(methodDefinition.key, isStatic, methodDefinition.computed, generator, true); + return new PropertyNode(methodToken, finish, methodDefinition.key, null, null, methodDefinition.functionNode, isStatic, methodDefinition.computed); + } else { + if (!isStatic && !generator && name.equals("constructor")) { + flags |= FunctionNode.ES6_IS_CLASS_CONSTRUCTOR; + if (subclass) { + flags |= FunctionNode.ES6_IS_SUBCLASS_CONSTRUCTOR; + } + } + verifyAllowedMethodName(propertyName, isStatic, computed, generator, false); + } + } + final PropertyFunction methodDefinition = propertyMethodFunction(propertyName, methodToken, methodLine, generator, flags, computed); + return new PropertyNode(methodToken, finish, methodDefinition.key, methodDefinition.functionNode, null, null, isStatic, computed); + } + + /** + * ES6 14.5.1 Static Semantics: Early Errors. + */ + private void verifyAllowedMethodName(final Expression key, final boolean isStatic, final boolean computed, final boolean generator, final boolean accessor) { + if (!computed) { + if (!isStatic && generator && ((PropertyKey) key).getPropertyName().equals("constructor")) { + throw error(AbstractParser.message("generator.constructor"), key.getToken()); + } + if (!isStatic && accessor && ((PropertyKey) key).getPropertyName().equals("constructor")) { + throw error(AbstractParser.message("accessor.constructor"), key.getToken()); + } + if (isStatic && ((PropertyKey) key).getPropertyName().equals("prototype")) { + throw error(AbstractParser.message("static.prototype.method"), key.getToken()); + } + } + } + + /** * block : * { StatementList? } * @@ -1008,7 +1504,7 @@ */ private void statementList() { // Accumulate statements until end of list. */ -loop: + loop: while (type != EOF) { switch (type) { case EOF: @@ -1026,6 +1522,22 @@ } /** + * Make sure that the identifier name used is allowed. + * + * @param ident Identifier that is verified + * @param contextString String used in error message to give context to the user + */ + private void verifyIdent(final IdentNode ident, final String contextString) { + verifyStrictIdent(ident, contextString); + if (isES6()) { + final TokenType tokenType = TokenLookup.lookupKeyword(ident.getName().toCharArray(), 0, ident.getName().length()); + if (tokenType != IDENT && tokenType.getKind() != TokenKind.FUTURESTRICT) { + throw error(expectMessage(IDENT)); + } + } + } + + /** * Make sure that in strict mode, the identifier name used is allowed. * * @param ident Identifier that is verified @@ -1066,15 +1578,16 @@ * Parse a VAR statement. * @param isStatement True if a statement (not used in a FOR.) */ - private List variableStatement(final TokenType varType) { - return variableStatement(varType, true, -1); + private void variableStatement(final TokenType varType) { + variableDeclarationList(varType, true, -1); } - private List variableStatement(final TokenType varType, final boolean isStatement, final int sourceOrder) { + private List variableDeclarationList(final TokenType varType, final boolean isStatement, final int sourceOrder) { // VAR tested in caller. + assert varType == VAR || varType == LET || varType == CONST; next(); - final List vars = new ArrayList<>(); + final List bindings = new ArrayList<>(); int varFlags = 0; if (varType == LET) { varFlags |= VarNode.IS_LET; @@ -1082,13 +1595,29 @@ varFlags |= VarNode.IS_CONST; } + Expression missingAssignment = null; while (true) { // Get starting token. final int varLine = line; final long varToken = token; // Get name of var. - final IdentNode name = getIdent(); - verifyStrictIdent(name, "variable name"); + if (type == YIELD && inGeneratorFunction()) { + expect(IDENT); + } + + final String contextString = "variable name"; + Expression binding = bindingIdentifierOrPattern(contextString); + final boolean isDestructuring = !(binding instanceof IdentNode); + if (isDestructuring) { + final int finalVarFlags = varFlags; + verifyDestructuringBindingPattern(binding, new Consumer() { + public void accept(final IdentNode identNode) { + verifyIdent(identNode, contextString); + final VarNode var = new VarNode(varLine, varToken, sourceOrder, identNode.getFinish(), identNode.setIsDeclaredHere(), null, finalVarFlags); + appendStatement(var); + } + }); + } // Assume no init. Expression init = null; @@ -1098,22 +1627,53 @@ next(); // Get initializer expression. Suppress IN if not statement. - defaultNames.push(name); + if (!isDestructuring) { + defaultNames.push(binding); + } try { init = assignmentExpression(!isStatement); } finally { - defaultNames.pop(); + if (!isDestructuring) { + defaultNames.pop(); + } } - } else if (varType == CONST && isStatement) { - throw error(AbstractParser.message("missing.const.assignment", name.getName())); + } else if (isStatement) { + if (isDestructuring) { + throw error(AbstractParser.message("missing.destructuring.assignment"), token); + } else if (varType == CONST) { + throw error(AbstractParser.message("missing.const.assignment", ((IdentNode)binding).getName())); + } + // else, if we are in a for loop, delay checking until we know the kind of loop } - // Only set declaration flag on lexically scoped let/const as it adds runtime overhead. - final IdentNode actualName = varType == LET || varType == CONST ? name.setIsDeclaredHere() : name; - // Allocate var node. - final VarNode var = new VarNode(varLine, varToken, sourceOrder, finish, actualName, init, varFlags); - vars.add(var); - appendStatement(var); + if (!isDestructuring) { + assert init != null || varType != CONST || !isStatement; + final IdentNode ident = (IdentNode)binding; + if (!isStatement && ident.getName().equals("let")) { + throw error(AbstractParser.message("let.binding.for")); //ES6 13.7.5.1 + } + // Only set declaration flag on lexically scoped let/const as it adds runtime overhead. + final IdentNode name = varType == LET || varType == CONST ? ident.setIsDeclaredHere() : ident; + binding = name; + final VarNode var = new VarNode(varLine, varToken, sourceOrder, finish, name, init, varFlags); + appendStatement(var); + if (init == null && varType == CONST) { + if (missingAssignment == null) { + missingAssignment = binding; + } + } + } else { + assert init != null || !isStatement; + binding = init == null ? binding : verifyAssignment(Token.recast(varToken, ASSIGN), binding, init); + if (isStatement) { + appendStatement(new ExpressionStatement(varLine, binding.getToken(), finish, binding)); + } else if (init == null) { + if (missingAssignment == null) { + missingAssignment = binding; + } + } + } + bindings.add(binding); if (type != COMMARIGHT) { break; @@ -1124,9 +1684,128 @@ // If is a statement then handle end of line. if (isStatement) { endOfLine(); + } else { + if (type == SEMICOLON) { + // late check for missing assignment, now we know it's a for (init; test; modify) loop + if (missingAssignment != null) { + if (missingAssignment instanceof IdentNode) { + throw error(AbstractParser.message("missing.const.assignment", ((IdentNode)missingAssignment).getName())); + } else { + throw error(AbstractParser.message("missing.destructuring.assignment"), missingAssignment.getToken()); + } + } + } } - return vars; + return bindings; + } + + private boolean isBindingIdentifier() { + return type == IDENT || isNonStrictModeIdent(); + } + + private IdentNode bindingIdentifier(final String contextString) { + final IdentNode name = getIdent(); + verifyIdent(name, contextString); + return name; + } + + private Expression bindingPattern() { + if (type == LBRACKET) { + return arrayLiteral(); + } else if (type == LBRACE) { + return objectLiteral(); + } else { + throw error(AbstractParser.message("expected.binding")); + } + } + + private Expression bindingIdentifierOrPattern(final String contextString) { + if (isBindingIdentifier() || !isES6()) { + return bindingIdentifier(contextString); + } else { + return bindingPattern(); + } + } + + /** + * Verify destructuring variable declaration binding pattern and extract bound variable declarations. + */ + private void verifyDestructuringBindingPattern(final Expression pattern, final Consumer identifierCallback) { + assert pattern instanceof ObjectNode || pattern instanceof LiteralNode.ArrayLiteralNode; + pattern.accept(new NodeVisitor(new LexicalContext()) { + @Override + public boolean enterLiteralNode(final LiteralNode literalNode) { + if (literalNode.isArray()) { + boolean restElement = false; + for (final Expression element : literalNode.getElementExpressions()) { + if (restElement) { + throw error(String.format("Unexpected element after rest element"), element.getToken()); + } + if (element != null) { + if (element.isTokenType(SPREAD_ARRAY)) { + restElement = true; + if (!(((UnaryNode) element).getExpression() instanceof IdentNode)) { + throw error(String.format("Expected a valid binding identifier"), element.getToken()); + + } + } + element.accept(this); + } + } + return false; + } else { + return enterDefault(literalNode); + } + } + + @Override + public boolean enterObjectNode(final ObjectNode objectNode) { + return true; + } + + @Override + public boolean enterPropertyNode(final PropertyNode propertyNode) { + if (propertyNode.getValue() != null) { + propertyNode.getValue().accept(this); + return false; + } else { + return enterDefault(propertyNode); + } + } + + @Override + public boolean enterIdentNode(final IdentNode identNode) { + identifierCallback.accept(identNode); + return false; + } + + @Override + public boolean enterBinaryNode(final BinaryNode binaryNode) { + if (binaryNode.isTokenType(ASSIGN)) { + binaryNode.lhs().accept(this); + // Initializer(rhs) can be any AssignmentExpression + return false; + } else { + return enterDefault(binaryNode); + } + } + + @Override + public boolean enterUnaryNode(final UnaryNode unaryNode) { + if (unaryNode.isTokenType(SPREAD_ARRAY)) { + // rest element + return true; + } else { + return enterDefault(unaryNode); + } + } + + @Override + protected boolean enterDefault(final Node node) { + throw error(String.format("unexpected node in BindingPattern: %s", node)); + } + }); } /** @@ -1230,7 +1909,7 @@ final ParserContextLoopNode forNode = new ParserContextLoopNode(); lc.push(forNode); Block body = null; - List vars = null; + List vars = null; Expression init = null; JoinPredecessorExpression test = null; JoinPredecessorExpression modify = null; @@ -1254,20 +1933,20 @@ switch (type) { case VAR: // Var declaration captured in for outer block. - vars = variableStatement(type, false, forStart); + vars = variableDeclarationList(type, false, forStart); break; case SEMICOLON: break; default: - if (useBlockScope() && (type == LET || type == CONST)) { + if (useBlockScope() && (type == LET && lookaheadIsLetDeclaration(true) || type == CONST)) { flags |= ForNode.PER_ITERATION_SCOPE; // LET/CONST declaration captured in container block created above. - vars = variableStatement(type, false, forStart); + vars = variableDeclarationList(type, false, forStart); break; } if (env._const_as_var && type == CONST) { // Var declaration captured in for outer block. - vars = variableStatement(TokenType.VAR, false, forStart); + vars = variableDeclarationList(TokenType.VAR, false, forStart); break; } @@ -1309,7 +1988,11 @@ if (vars != null) { // for (var i in obj) if (vars.size() == 1) { - init = new IdentNode(vars.get(0).getName()); + init = new IdentNode((IdentNode)vars.get(0)); + if (init.isTokenType(ASSIGN)) { + throw error(AbstractParser.message("for.in.loop.initializer"), init.getToken()); + } + assert init instanceof IdentNode || isDestructuringLhs(init); } else { // for (var i, j in obj) is invalid throw error(AbstractParser.message("many.vars.in.for.in.loop", isForOf ? "of" : "in"), vars.get(1).getToken()); @@ -1351,10 +2034,9 @@ } finally { lc.pop(forNode); - if (vars != null) { - for (final VarNode var : vars) { - appendStatement(var); - } + for (final Statement var : forNode.getStatements()) { + assert var instanceof VarNode; + appendStatement(var); } if (body != null) { appendStatement(new ForNode(forLine, forToken, body.getFinish(), body, (forNode.getFlags() | flags), init, test, modify)); @@ -1371,6 +2053,49 @@ } } + private boolean checkValidLValue(final Expression init, final String contextString) { + if (init instanceof IdentNode) { + if (!checkIdentLValue((IdentNode)init)) { + return false; + } + verifyIdent((IdentNode)init, contextString); + return true; + } else if (init instanceof AccessNode || init instanceof IndexNode) { + return true; + } else if (isDestructuringLhs(init)) { + verifyDestructuringAssignmentPattern(init, contextString); + return true; + } else { + return false; + } + } + + private boolean lookaheadIsLetDeclaration(final boolean ofContextualKeyword) { + assert type == LET; + for (int i = 1;; i++) { + TokenType t = T(k + i); + switch (t) { + case EOL: + case COMMENT: + continue; + case IDENT: + if (ofContextualKeyword && isES6() && "of".equals(getValue(getToken(k + i)))) { + return false; + } + // fall through + case LBRACKET: + case LBRACE: + return true; + default: + // accept future strict tokens in non-strict mode (including LET) + if (!isStrictMode && t.getKind() == TokenKind.FUTURESTRICT) { + return true; + } + return false; + } + } + } + /** * ...IterationStatement : * ... @@ -1559,7 +2284,7 @@ */ private void returnStatement() { // check for return outside function - if (lc.getCurrentFunction().getKind() == FunctionNode.Kind.SCRIPT) { + if (lc.getCurrentFunction().getKind() == FunctionNode.Kind.SCRIPT || lc.getCurrentFunction().getKind() == FunctionNode.Kind.MODULE) { throw error(AbstractParser.message("invalid.return")); } @@ -1591,39 +2316,61 @@ } /** - * YieldStatement : - * yield Expression? ; // [no LineTerminator here] - * - * JavaScript 1.8 + * Parse YieldExpression. * - * Parse YIELD statement. + * YieldExpression[In] : + * yield + * yield [no LineTerminator here] AssignmentExpression[?In, Yield] + * yield [no LineTerminator here] * AssignmentExpression[?In, Yield] */ - private void yieldStatement() { + private Expression yieldExpression(final boolean noIn) { + assert inGeneratorFunction(); // Capture YIELD token. - final int yieldLine = line; - final long yieldToken = token; + long yieldToken = token; // YIELD tested in caller. + assert type == YIELD; nextOrEOL(); Expression expression = null; - // SEMICOLON or expression. + boolean yieldAsterisk = false; + if (type == MUL) { + yieldAsterisk = true; + yieldToken = Token.recast(yieldToken, YIELD_STAR); + next(); + } + switch (type) { case RBRACE: case SEMICOLON: case EOL: case EOF: - break; + case COMMARIGHT: + case RPAREN: + case RBRACKET: + case COLON: + if (!yieldAsterisk) { + // treat (yield) as (yield void 0) + expression = newUndefinedLiteral(yieldToken, finish); + if (type == EOL) { + next(); + } + break; + } else { + // AssignmentExpression required, fall through + } default: - expression = expression(); + expression = assignmentExpression(noIn); break; } - endOfLine(); - // Construct and add YIELD node. - appendStatement(new ReturnNode(yieldLine, yieldToken, finish, expression)); + return new UnaryNode(yieldToken, expression); + } + + private static UnaryNode newUndefinedLiteral(final long token, final int finish) { + return new UnaryNode(Token.recast(token, VOID), LiteralNode.newInstance(token, finish, 0)); } /** @@ -1679,11 +2426,15 @@ private void switchStatement() { final int switchLine = line; final long switchToken = token; + + // Block to capture variables declared inside the switch statement. + final ParserContextBlockNode switchBlock = newBlock(); + // SWITCH tested in caller. next(); // Create and add switch statement. - final ParserContextSwitchNode switchNode= new ParserContextSwitchNode(); + final ParserContextSwitchNode switchNode = new ParserContextSwitchNode(); lc.push(switchNode); CaseNode defaultCase = null; @@ -1727,7 +2478,7 @@ expect(COLON); // Get CASE body. - final Block statements = getBlock(false); + final Block statements = getBlock(false); // TODO: List statements = caseStatementList(); final CaseNode caseNode = new CaseNode(caseToken, finish, caseExpression, statements); if (caseExpression == null) { @@ -1740,9 +2491,11 @@ next(); } finally { lc.pop(switchNode); + restoreBlock(switchBlock); } - appendStatement(new SwitchNode(switchLine, switchToken, finish, expression, cases, defaultCase)); + final SwitchNode switchStatement = new SwitchNode(switchLine, switchToken, finish, expression, cases, defaultCase); + appendStatement(new BlockStatement(switchLine, new Block(switchToken, finish, switchBlock.getFlags() | Block.IS_SYNTHETIC | Block.IS_SWITCH_BLOCK, switchStatement))); } /** @@ -1769,7 +2522,7 @@ Block body = null; try { lc.push(labelNode); - body = getStatement(); + body = getStatement(true); } finally { assert lc.peek() instanceof ParserContextLabelNode; lc.pop(labelNode); @@ -1935,13 +2688,19 @@ /** * PrimaryExpression : * this - * Identifier + * IdentifierReference * Literal * ArrayLiteral * ObjectLiteral * RegularExpressionLiteral * TemplateLiteral + * CoverParenthesizedExpressionAndArrowParameterList + * + * CoverParenthesizedExpressionAndArrowParameterList : * ( Expression ) + * ( ) + * ( ... BindingIdentifier ) + * ( Expression , ... BindingIdentifier ) * * Parse primary expression. * @return Expression node. @@ -1956,7 +2715,7 @@ case THIS: final String name = type.getName(); next(); - lc.getCurrentFunction().setFlag(FunctionNode.USES_THIS); + markThis(lc); return new IdentNode(primaryToken, finish, name); case IDENT: final IdentNode ident = getIdent(); @@ -1997,6 +2756,22 @@ case LPAREN: next(); + if (isES6()) { + if (type == RPAREN) { + // () + nextOrEOL(); + expectDontAdvance(ARROW); + return new ExpressionList(primaryToken, finish, Collections.emptyList()); + } else if (type == ELLIPSIS) { + // (...rest) + final IdentNode restParam = formalParameterList(false).get(0); + expectDontAdvance(RPAREN); + nextOrEOL(); + expectDontAdvance(ARROW); + return new ExpressionList(primaryToken, finish, Collections.singletonList(restParam)); + } + } + final Expression expression = expression(); expect(RPAREN); @@ -2073,8 +2848,9 @@ final List elements = new ArrayList<>(); // Track elisions. boolean elision = true; -loop: + loop: while (true) { + long spreadToken = 0; switch (type) { case RBRACKET: next(); @@ -2093,14 +2869,24 @@ break; + case ELLIPSIS: + if (isES6()) { + spreadToken = token; + next(); + } + // fall through + default: if (!elision) { throw error(AbstractParser.message("expected.comma", type.getNameOrType())); } + // Add expression element. - final Expression expression = assignmentExpression(false); - + Expression expression = assignmentExpression(false); if (expression != null) { + if (spreadToken != 0) { + expression = new UnaryNode(Token.recast(spreadToken, SPREAD_ARRAY), expression); + } elements.add(expression); } else { expect(RBRACKET); @@ -2141,7 +2927,7 @@ // Create a block for the object literal. boolean commaSeen = true; -loop: + loop: while (true) { switch (type) { case RBRACE: @@ -2164,6 +2950,12 @@ commaSeen = false; // Get and add the next property. final PropertyNode property = propertyAssignment(); + + if (property.isComputed()) { + elements.add(property); + break; + } + final String key = property.getKeyName(); final Integer existing = map.get(key); @@ -2185,36 +2977,23 @@ final FunctionNode prevGetter = existingProperty.getGetter(); final FunctionNode prevSetter = existingProperty.getSetter(); - // ECMA 11.1.5 strict mode restrictions - if (isStrictMode && value != null && prevValue != null) { - throw error(AbstractParser.message("property.redefinition", key), property.getToken()); - } - - final boolean isPrevAccessor = prevGetter != null || prevSetter != null; - final boolean isAccessor = getter != null || setter != null; - - // data property redefined as accessor property - if (prevValue != null && isAccessor) { - throw error(AbstractParser.message("property.redefinition", key), property.getToken()); - } - - // accessor property redefined as data - if (isPrevAccessor && value != null) { - throw error(AbstractParser.message("property.redefinition", key), property.getToken()); - } - - if (isAccessor && isPrevAccessor) { - if (getter != null && prevGetter != null || - setter != null && prevSetter != null) { - throw error(AbstractParser.message("property.redefinition", key), property.getToken()); + if (!isES6()) { + checkPropertyRedefinition(property, value, getter, setter, prevValue, prevGetter, prevSetter); + } else { + if (property.getKey() instanceof IdentNode && ((IdentNode)property.getKey()).isProtoPropertyName() && + existingProperty.getKey() instanceof IdentNode && ((IdentNode)existingProperty.getKey()).isProtoPropertyName()) { + throw error(AbstractParser.message("multiple.proto.key"), property.getToken()); } } - if (value != null) { + if (value != null || prevValue != null) { + map.put(key, elements.size()); elements.add(property); } else if (getter != null) { + assert prevGetter != null || prevSetter != null; elements.set(existing, existingProperty.setGetter(getter)); } else if (setter != null) { + assert prevGetter != null || prevSetter != null; elements.set(existing, existingProperty.setSetter(setter)); } break; @@ -2224,18 +3003,43 @@ return new ObjectNode(objectToken, finish, elements); } + private void checkPropertyRedefinition(final PropertyNode property, final Expression value, final FunctionNode getter, final FunctionNode setter, final Expression prevValue, final FunctionNode prevGetter, final FunctionNode prevSetter) { + // ECMA 11.1.5 strict mode restrictions + if (isStrictMode && value != null && prevValue != null) { + throw error(AbstractParser.message("property.redefinition", property.getKeyName()), property.getToken()); + } + + final boolean isPrevAccessor = prevGetter != null || prevSetter != null; + final boolean isAccessor = getter != null || setter != null; + + // data property redefined as accessor property + if (prevValue != null && isAccessor) { + throw error(AbstractParser.message("property.redefinition", property.getKeyName()), property.getToken()); + } + + // accessor property redefined as data + if (isPrevAccessor && value != null) { + throw error(AbstractParser.message("property.redefinition", property.getKeyName()), property.getToken()); + } + + if (isAccessor && isPrevAccessor) { + if (getter != null && prevGetter != null || + setter != null && prevSetter != null) { + throw error(AbstractParser.message("property.redefinition", property.getKeyName()), property.getToken()); + } + } + } + /** - * PropertyName : + * LiteralPropertyName : * IdentifierName * StringLiteral * NumericLiteral * - * See 11.1.5 - * * @return PropertyName node */ @SuppressWarnings("fallthrough") - private PropertyKey propertyName() { + private PropertyKey literalPropertyName() { switch (type) { case IDENT: return getIdent().setIsPropertyName(); @@ -2257,6 +3061,34 @@ } /** + * ComputedPropertyName : + * AssignmentExpression + * + * @return PropertyName node + */ + private Expression computedPropertyName() { + expect(LBRACKET); + Expression expression = assignmentExpression(false); + expect(RBRACKET); + return expression; + } + + /** + * PropertyName : + * LiteralPropertyName + * ComputedPropertyName + * + * @return PropertyName node + */ + private Expression propertyName() { + if (type == LBRACKET && isES6()) { + return computedPropertyName(); + } else { + return (Expression)literalPropertyName(); + } + } + + /** * PropertyAssignment : * PropertyName : AssignmentExpression * get PropertyName ( ) { FunctionBody } @@ -2280,51 +3112,95 @@ final long propertyToken = token; final int functionLine = line; - PropertyKey propertyName; - + final Expression propertyName; + final boolean isIdentifier; + + boolean generator = false; + if (type == MUL && isES6()) { + generator = true; + next(); + } + + final boolean computed = type == LBRACKET; if (type == IDENT) { // Get IDENT. final String ident = (String)expectValue(IDENT); - if (type != COLON) { + if (type != COLON && (type != LPAREN || !isES6())) { final long getSetToken = propertyToken; switch (ident) { case "get": final PropertyFunction getter = propertyGetterFunction(getSetToken, functionLine); - return new PropertyNode(propertyToken, finish, getter.ident, null, getter.functionNode, null); + return new PropertyNode(propertyToken, finish, getter.key, null, getter.functionNode, null, false, getter.computed); case "set": final PropertyFunction setter = propertySetterFunction(getSetToken, functionLine); - return new PropertyNode(propertyToken, finish, setter.ident, null, null, setter.functionNode); + return new PropertyNode(propertyToken, finish, setter.key, null, null, setter.functionNode, false, setter.computed); default: break; } } - propertyName = createIdentNode(propertyToken, finish, ident).setIsPropertyName(); + isIdentifier = true; + IdentNode identNode = createIdentNode(propertyToken, finish, ident).setIsPropertyName(); + if (type == COLON && ident.equals("__proto__")) { + identNode = identNode.setIsProtoPropertyName(); + } + propertyName = identNode; } else { + isIdentifier = isNonStrictModeIdent(); propertyName = propertyName(); } - expect(COLON); - - defaultNames.push(propertyName); - try { - return new PropertyNode(propertyToken, finish, propertyName, assignmentExpression(false), null, null); - } finally { - defaultNames.pop(); + Expression propertyValue; + + if (generator) { + expectDontAdvance(LPAREN); } + + if (type == LPAREN && isES6()) { + propertyValue = propertyMethodFunction(propertyName, propertyToken, functionLine, generator, FunctionNode.ES6_IS_METHOD, computed).functionNode; + } else if (isIdentifier && (type == COMMARIGHT || type == RBRACE || type == ASSIGN) && isES6()) { + propertyValue = createIdentNode(propertyToken, finish, ((IdentNode) propertyName).getPropertyName()); + if (type == ASSIGN && isES6()) { + // TODO if not destructuring, this is a SyntaxError + final long assignToken = token; + next(); + final Expression rhs = assignmentExpression(false); + propertyValue = verifyAssignment(assignToken, propertyValue, rhs); + } + } else { + expect(COLON); + + defaultNames.push(propertyName); + try { + propertyValue = assignmentExpression(false); + } finally { + defaultNames.pop(); + } + } + + return new PropertyNode(propertyToken, finish, propertyName, propertyValue, null, null, false, computed); } private PropertyFunction propertyGetterFunction(final long getSetToken, final int functionLine) { - final PropertyKey getIdent = propertyName(); - final String getterName = getIdent.getPropertyName(); - final IdentNode getNameNode = createIdentNode(((Node)getIdent).getToken(), finish, NameCodec.encode("get " + getterName)); + return propertyGetterFunction(getSetToken, functionLine, FunctionNode.ES6_IS_METHOD); + } + + private PropertyFunction propertyGetterFunction(final long getSetToken, final int functionLine, final int flags) { + final boolean computed = type == LBRACKET; + final Expression propertyName = propertyName(); + final String getterName = propertyName instanceof PropertyKey ? ((PropertyKey) propertyName).getPropertyName() : getDefaultValidFunctionName(functionLine, false); + final IdentNode getNameNode = createIdentNode((propertyName).getToken(), finish, NameCodec.encode("get " + getterName)); expect(LPAREN); expect(RPAREN); final ParserContextFunctionNode functionNode = createParserContextFunctionNode(getNameNode, getSetToken, FunctionNode.Kind.GETTER, functionLine, Collections.emptyList()); + functionNode.setFlag(flags); + if (computed) { + functionNode.setFlag(FunctionNode.IS_ANONYMOUS); + } lc.push(functionNode); Block functionBody; @@ -2345,20 +3221,25 @@ functionLine, functionBody); - return new PropertyFunction(getIdent, function); + return new PropertyFunction(propertyName, function, computed); } private PropertyFunction propertySetterFunction(final long getSetToken, final int functionLine) { - final PropertyKey setIdent = propertyName(); - final String setterName = setIdent.getPropertyName(); - final IdentNode setNameNode = createIdentNode(((Node)setIdent).getToken(), finish, NameCodec.encode("set " + setterName)); + return propertySetterFunction(getSetToken, functionLine, FunctionNode.ES6_IS_METHOD); + } + + private PropertyFunction propertySetterFunction(final long getSetToken, final int functionLine, final int flags) { + final boolean computed = type == LBRACKET; + final Expression propertyName = propertyName(); + final String setterName = propertyName instanceof PropertyKey ? ((PropertyKey) propertyName).getPropertyName() : getDefaultValidFunctionName(functionLine, false); + final IdentNode setNameNode = createIdentNode((propertyName).getToken(), finish, NameCodec.encode("set " + setterName)); expect(LPAREN); // be sloppy and allow missing setter parameter even though // spec does not permit it! final IdentNode argIdent; - if (type == IDENT || isNonStrictModeIdent()) { + if (isBindingIdentifier()) { argIdent = getIdent(); - verifyStrictIdent(argIdent, "setter argument"); + verifyIdent(argIdent, "setter argument"); } else { argIdent = null; } @@ -2370,6 +3251,10 @@ final ParserContextFunctionNode functionNode = createParserContextFunctionNode(setNameNode, getSetToken, FunctionNode.Kind.SETTER, functionLine, parameters); + functionNode.setFlag(flags); + if (computed) { + functionNode.setFlag(FunctionNode.IS_ANONYMOUS); + } lc.push(functionNode); Block functionBody; @@ -2389,33 +3274,81 @@ functionLine, functionBody); - return new PropertyFunction(setIdent, function); + return new PropertyFunction(propertyName, function, computed); + } + + private PropertyFunction propertyMethodFunction(Expression key, final long methodToken, final int methodLine, final boolean generator, final int flags, boolean computed) { + final String methodName = key instanceof PropertyKey ? ((PropertyKey) key).getPropertyName() : getDefaultValidFunctionName(methodLine, false); + final IdentNode methodNameNode = createIdentNode(((Node)key).getToken(), finish, methodName); + + FunctionNode.Kind functionKind = generator ? FunctionNode.Kind.GENERATOR : FunctionNode.Kind.NORMAL; + final ParserContextFunctionNode functionNode = createParserContextFunctionNode(methodNameNode, methodToken, functionKind, methodLine, null); + functionNode.setFlag(flags); + if (computed) { + functionNode.setFlag(FunctionNode.IS_ANONYMOUS); + } + lc.push(functionNode); + + try { + final ParserContextBlockNode parameterBlock = newBlock(); + final List parameters; + try { + expect(LPAREN); + parameters = formalParameterList(generator); + functionNode.setParameters(parameters); + expect(RPAREN); + } finally { + restoreBlock(parameterBlock); + } + + Block functionBody = functionBody(functionNode); + + functionBody = maybeWrapBodyInParameterBlock(functionBody, parameterBlock); + + final FunctionNode function = createFunctionNode( + functionNode, + methodToken, + methodNameNode, + parameters, + functionKind, + methodLine, + functionBody); + return new PropertyFunction(key, function, computed); + } finally { + lc.pop(functionNode); + } } private static class PropertyFunction { - final PropertyKey ident; + final Expression key; final FunctionNode functionNode; - - PropertyFunction(final PropertyKey ident, final FunctionNode function) { - this.ident = ident; + final boolean computed; + + PropertyFunction(final Expression key, final FunctionNode function, final boolean computed) { + this.key = key; this.functionNode = function; + this.computed = computed; } } /** - * Parse left hand side expression. - * * LeftHandSideExpression : * NewExpression * CallExpression * * CallExpression : * MemberExpression Arguments + * SuperCall * CallExpression Arguments * CallExpression [ Expression ] * CallExpression . IdentifierName - * CallExpression TemplateLiteral + * + * SuperCall : + * super Arguments * + * See 11.2 + * + * Parse left hand side expression. * @return Expression node. */ private Expression leftHandSideExpression() { @@ -2435,7 +3368,7 @@ lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false); } -loop: + loop: while (true) { // Capture token. callLine = line; @@ -2479,6 +3412,7 @@ // tagged template literal final List arguments = templateLiteralArgumentList(); + // Create call node. lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false); break; @@ -2506,6 +3440,20 @@ // NEW is tested in caller. next(); + if (type == PERIOD && isES6()) { + next(); + if (type == IDENT && "target".equals(getValue())) { + if (lc.getCurrentFunction().isProgram()) { + throw error(AbstractParser.message("new.target.in.function"), token); + } + next(); + markNewTarget(lc); + return new IdentNode(newToken, finish, "new.target"); + } else { + throw error(AbstractParser.message("expected.target"), token); + } + } + // Get function base. final int callLine = line; final Expression constructor = memberExpression(); @@ -2541,21 +3489,33 @@ } /** - * Parse member expression. - * * MemberExpression : * PrimaryExpression - * FunctionExpression + * FunctionExpression + * ClassExpression + * GeneratorExpression * MemberExpression [ Expression ] * MemberExpression . IdentifierName * MemberExpression TemplateLiteral + * SuperProperty + * MetaProperty * new MemberExpression Arguments * + * SuperProperty : + * super [ Expression ] + * super . IdentifierName + * + * MetaProperty : + * NewTarget + * + * Parse member expression. * @return Expression node. */ + @SuppressWarnings("fallthrough") private Expression memberExpression() { // Prepare to build operation. Expression lhs; + boolean isSuper = false; switch (type) { case NEW: @@ -2568,13 +3528,53 @@ lhs = functionExpression(false, false); break; + case CLASS: + if (isES6()) { + lhs = classExpression(false); + break; + } else { + // fall through + } + + case SUPER: + if (isES6()) { + final ParserContextFunctionNode currentFunction = getCurrentNonArrowFunction(); + if (currentFunction.isMethod()) { + long identToken = Token.recast(token, IDENT); + next(); + lhs = createIdentNode(identToken, finish, SUPER.getName()); + + switch (type) { + case LBRACKET: + case PERIOD: + getCurrentNonArrowFunction().setFlag(FunctionNode.ES6_USES_SUPER); + isSuper = true; + break; + case LPAREN: + if (currentFunction.isSubclassConstructor()) { + lhs = ((IdentNode)lhs).setIsDirectSuper(); + break; + } else { + // fall through to throw error + } + default: + throw error(AbstractParser.message("invalid.super"), identToken); + } + break; + } else { + // fall through + } + } else { + // fall through + } + default: // Get primary expression. lhs = primaryExpression(); break; } -loop: + loop: while (true) { // Capture token. final long callToken = token; @@ -2591,6 +3591,11 @@ // Create indexing node. lhs = new IndexNode(callToken, finish, lhs, index); + if (isSuper) { + isSuper = false; + lhs = ((BaseNode) lhs).setIsSuper(); + } + break; } case PERIOD: { @@ -2605,6 +3610,11 @@ // Create property access node. lhs = new AccessNode(callToken, finish, lhs, property.getName()); + if (isSuper) { + isSuper = false; + lhs = ((BaseNode) lhs).setIsSuper(); + } + break; } case TEMPLATE: @@ -2632,7 +3642,9 @@ * * ArgumentList : * AssignmentExpression + * ... AssignmentExpression * ArgumentList , AssignmentExpression + * ArgumentList , ... AssignmentExpression * * See 11.2 * @@ -2656,8 +3668,18 @@ first = false; } + long spreadToken = 0; + if (type == ELLIPSIS && isES6()) { + spreadToken = token; + next(); + } + // Get argument expression. - nodeList.add(assignmentExpression(false)); + Expression expression = assignmentExpression(false); + if (spreadToken != 0) { + expression = new UnaryNode(Token.recast(spreadToken, TokenType.SPREAD_ARGUMENT), expression); + } + nodeList.add(expression); } expect(RPAREN); @@ -2697,11 +3719,24 @@ final long functionToken = token; final int functionLine = line; // FUNCTION is tested in caller. + assert type == FUNCTION; next(); + boolean generator = false; + if (type == MUL && isES6()) { + generator = true; + next(); + } + IdentNode name = null; - if (type == IDENT || isNonStrictModeIdent()) { + if (isBindingIdentifier()) { + if (type == YIELD && ((!isStatement && generator) || (isStatement && inGeneratorFunction()))) { + // 12.1.1 Early SyntaxError if: + // GeneratorExpression with BindingIdentifier yield + // HoistableDeclaration with BindingIdentifier yield in generator function body + expect(IDENT); + } name = getIdent(); verifyStrictIdent(name, "function name"); } else if (isStatement) { @@ -2723,25 +3758,36 @@ isAnonymous = true; } - expect(LPAREN); - final List parameters = formalParameterList(); - expect(RPAREN); - - final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, FunctionNode.Kind.NORMAL, functionLine, parameters); + FunctionNode.Kind functionKind = generator ? FunctionNode.Kind.GENERATOR : FunctionNode.Kind.NORMAL; + List parameters = Collections.emptyList(); + final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, functionKind, functionLine, parameters); lc.push(functionNode); + Block functionBody = null; // Hide the current default name across function boundaries. E.g. "x3 = function x1() { function() {}}" // If we didn't hide the current default name, then the innermost anonymous function would receive "x3". hideDefaultName(); - try{ + try { + final ParserContextBlockNode parameterBlock = newBlock(); + try { + expect(LPAREN); + parameters = formalParameterList(generator); + functionNode.setParameters(parameters); + expect(RPAREN); + } finally { + restoreBlock(parameterBlock); + } + functionBody = functionBody(functionNode); + + functionBody = maybeWrapBodyInParameterBlock(functionBody, parameterBlock); } finally { defaultNames.pop(); lc.pop(functionNode); } if (isStatement) { - if (topLevel || useBlockScope()) { + if (topLevel || useBlockScope() || (!isStrictMode && env._function_statement == ScriptEnvironment.FunctionStatementBehavior.ACCEPT)) { functionNode.setFlag(FunctionNode.IS_DECLARED); } else if (isStrictMode) { throw error(JSErrorType.SYNTAX_ERROR, AbstractParser.message("strict.no.func.decl.here"), functionToken); @@ -2759,45 +3805,14 @@ functionNode.setFlag(FunctionNode.IS_ANONYMOUS); } - final int arity = parameters.size(); - - final boolean strict = functionNode.isStrict(); - if (arity > 1) { - final HashSet parametersSet = new HashSet<>(arity); - - for (int i = arity - 1; i >= 0; i--) { - final IdentNode parameter = parameters.get(i); - String parameterName = parameter.getName(); - - if (isArguments(parameterName)) { - functionNode.setFlag(FunctionNode.DEFINES_ARGUMENTS); - } - - if (parametersSet.contains(parameterName)) { - // redefinition of parameter name - if (strict) { - throw error(AbstractParser.message("strict.param.redefinition", parameterName), parameter.getToken()); - } - // rename in non-strict mode - parameterName = functionNode.uniqueName(parameterName); - final long parameterToken = parameter.getToken(); - parameters.set(i, new IdentNode(parameterToken, Token.descPosition(parameterToken), functionNode.uniqueName(parameterName))); - } - - parametersSet.add(parameterName); - } - } else if (arity == 1) { - if (isArguments(parameters.get(0))) { - functionNode.setFlag(FunctionNode.DEFINES_ARGUMENTS); - } - } + verifyParameterList(parameters, functionNode); final FunctionNode function = createFunctionNode( functionNode, functionToken, name, parameters, - FunctionNode.Kind.NORMAL, + functionKind, functionLine, functionBody); @@ -2822,6 +3837,40 @@ return function; } + private void verifyParameterList(final List parameters, final ParserContextFunctionNode functionNode) { + final IdentNode duplicateParameter = functionNode.getDuplicateParameterBinding(); + if (duplicateParameter != null) { + if (functionNode.isStrict() || functionNode.getKind() == FunctionNode.Kind.ARROW || !functionNode.isSimpleParameterList()) { + throw error(AbstractParser.message("strict.param.redefinition", duplicateParameter.getName()), duplicateParameter.getToken()); + } + + final int arity = parameters.size(); + final HashSet parametersSet = new HashSet<>(arity); + + for (int i = arity - 1; i >= 0; i--) { + final IdentNode parameter = parameters.get(i); + String parameterName = parameter.getName(); + + if (parametersSet.contains(parameterName)) { + // redefinition of parameter name, rename in non-strict mode + parameterName = functionNode.uniqueName(parameterName); + final long parameterToken = parameter.getToken(); + parameters.set(i, new IdentNode(parameterToken, Token.descPosition(parameterToken), functionNode.uniqueName(parameterName))); + } + parametersSet.add(parameterName); + } + } + } + + private static Block maybeWrapBodyInParameterBlock(Block functionBody, ParserContextBlockNode parameterBlock) { + assert functionBody.isFunctionBody(); + if (!parameterBlock.getStatements().isEmpty()) { + parameterBlock.appendStatement(new BlockStatement(functionBody)); + return new Block(parameterBlock.getToken(), functionBody.getFinish(), (functionBody.getFlags() | Block.IS_PARAMETER_BLOCK) & ~Block.IS_BODY, parameterBlock.getStatements()); + } + return functionBody; + } + private String getDefaultValidFunctionName(final int functionLine, final boolean isStatement) { final String defaultFunctionName = getDefaultFunctionName(); if (isValidIdentifier(defaultFunctionName)) { @@ -2836,14 +3885,14 @@ } private static boolean isValidIdentifier(final String name) { - if(name == null || name.isEmpty()) { + if (name == null || name.isEmpty()) { return false; } - if(!Character.isJavaIdentifierStart(name.charAt(0))) { + if (!Character.isJavaIdentifierStart(name.charAt(0))) { return false; } - for(int i = 1; i < name.length(); ++i) { - if(!Character.isJavaIdentifierPart(name.charAt(i))) { + for (int i = 1; i < name.length(); ++i) { + if (!Character.isJavaIdentifierPart(name.charAt(i))) { return false; } } @@ -2851,12 +3900,12 @@ } private String getDefaultFunctionName() { - if(!defaultNames.isEmpty()) { + if (!defaultNames.isEmpty()) { final Object nameExpr = defaultNames.peek(); - if(nameExpr instanceof PropertyKey) { + if (nameExpr instanceof PropertyKey) { markDefaultNameUsed(); return ((PropertyKey)nameExpr).getPropertyName(); - } else if(nameExpr instanceof AccessNode) { + } else if (nameExpr instanceof AccessNode) { markDefaultNameUsed(); return ((AccessNode)nameExpr).getProperty(); } @@ -2885,8 +3934,8 @@ * Parse function parameter list. * @return List of parameter nodes. */ - private List formalParameterList() { - return formalParameterList(RPAREN); + private List formalParameterList(final boolean yield) { + return formalParameterList(RPAREN, yield); } /** @@ -2902,7 +3951,7 @@ * Parse function parameter list. * @return List of parameter nodes. */ - private List formalParameterList(final TokenType endType) { + private List formalParameterList(final TokenType endType, final boolean yield) { // Prepare to gather parameters. final ArrayList parameters = new ArrayList<>(); // Track commas. @@ -2916,12 +3965,84 @@ first = false; } - // Get and add parameter. - final IdentNode ident = getIdent(); - - // ECMA 13.1 strict mode restrictions - verifyStrictIdent(ident, "function parameter"); - + boolean restParameter = false; + if (type == ELLIPSIS && isES6()) { + next(); + restParameter = true; + } + + if (type == YIELD && yield) { + expect(IDENT); + } + + final long paramToken = token; + final int paramLine = line; + final String contextString = "function parameter"; + IdentNode ident; + if (isBindingIdentifier() || restParameter || !isES6()) { + ident = bindingIdentifier(contextString); + + if (restParameter) { + ident = ident.setIsRestParameter(); + // rest parameter must be last + expectDontAdvance(endType); + parameters.add(ident); + break; + } else if (type == ASSIGN && isES6()) { + next(); + ident = ident.setIsDefaultParameter(); + + if (type == YIELD && yield) { + // error: yield in default expression + expect(IDENT); + } + + // default parameter + Expression initializer = assignmentExpression(false); + + ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); + if (currentFunction != null) { + // desugar to: param = (param === undefined) ? initializer : param; + // possible alternative: if (param === undefined) param = initializer; + BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); + TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); + BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), ident, value); + lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + } + } + + ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); + if (currentFunction != null) { + currentFunction.addParameterBinding(ident); + if (ident.isRestParameter() || ident.isDefaultParameter()) { + currentFunction.setSimpleParameterList(false); + } + } + } else { + final Expression pattern = bindingPattern(); + // Introduce synthetic temporary parameter to capture the object to be destructured. + ident = createIdentNode(paramToken, pattern.getFinish(), String.format("arguments[%d]", parameters.size())).setIsDestructuredParameter(); + verifyDestructuringParameterBindingPattern(pattern, paramToken, paramLine, contextString); + + Expression value = ident; + if (type == ASSIGN) { + next(); + ident = ident.setIsDefaultParameter(); + + // binding pattern with initializer. desugar to: (param === undefined) ? initializer : param + Expression initializer = assignmentExpression(false); + // TODO initializer must not contain yield expression if yield=true (i.e. this is generator function's parameter list) + BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); + value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); + } + + ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); + if (currentFunction != null) { + // destructuring assignment + BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), pattern, value); + lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + } + } parameters.add(ident); } @@ -2929,6 +4050,23 @@ return parameters; } + private void verifyDestructuringParameterBindingPattern(final Expression pattern, final long paramToken, final int paramLine, final String contextString) { + verifyDestructuringBindingPattern(pattern, new Consumer() { + public void accept(IdentNode identNode) { + verifyIdent(identNode, contextString); + + ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); + if (currentFunction != null) { + // declare function-scope variables for destructuring bindings + lc.getFunctionBody(currentFunction).appendStatement(new VarNode(paramLine, Token.recast(paramToken, VAR), pattern.getFinish(), identNode, null)); + // detect duplicate bounds names in parameter list + currentFunction.addParameterBinding(identNode); + currentFunction.setSimpleParameterList(false); + } + } + }); + } + /** * FunctionBody : * SourceElements? @@ -2958,7 +4096,7 @@ final int functionId = functionNode.getId(); parseBody = reparsedFunction == null || functionId <= reparsedFunction.getFunctionNodeId(); // Nashorn extension: expression closures - if (!env._no_syntax_extensions && type != LBRACE) { + if ((!env._no_syntax_extensions || functionNode.getKind() == FunctionNode.Kind.ARROW) && type != LBRACE) { /* * Example: * @@ -2967,7 +4105,7 @@ */ // just expression as function body - final Expression expr = assignmentExpression(true); + final Expression expr = assignmentExpression(false); lastToken = previousToken; functionNode.setLastToken(previousToken); assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode); @@ -2982,6 +4120,7 @@ final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr); appendStatement(returnNode); } + // bodyFinish = finish; } else { expectDontAdvance(LBRACE); if (parseBody || !skipFunctionBody(functionNode)) { @@ -3054,7 +4193,7 @@ } } } - functionBody = new Block(bodyToken, bodyFinish, body.getFlags(), body.getStatements()); + functionBody = new Block(bodyToken, bodyFinish, body.getFlags() | Block.IS_BODY, body.getStatements()); return functionBody; } @@ -3095,8 +4234,7 @@ // Doesn't really matter, but it's safe to treat it as if there were a semicolon before // the RBRACE. type = SEMICOLON; - k = -1; - next(); + scanFirstToken(); return true; } @@ -3126,11 +4264,11 @@ } private void printAST(final FunctionNode functionNode) { - if (functionNode.getFlag(FunctionNode.IS_PRINT_AST)) { + if (functionNode.getDebugFlag(FunctionNode.DEBUG_PRINT_AST)) { env.getErr().println(new ASTWriter(functionNode)); } - if (functionNode.getFlag(FunctionNode.IS_PRINT_PARSE)) { + if (functionNode.getDebugFlag(FunctionNode.DEBUG_PRINT_PARSE)) { env.getErr().println(new PrintVisitor(functionNode, true, false)); } } @@ -3222,20 +4360,7 @@ throw error(AbstractParser.message("expected.lvalue", type.getNameOrType())); } - if (!(lhs instanceof AccessNode || - lhs instanceof IndexNode || - lhs instanceof IdentNode)) { - return referenceError(lhs, null, env._early_lvalue_error); - } - - if (lhs instanceof IdentNode) { - if (!checkIdentLValue((IdentNode)lhs)) { - return referenceError(lhs, null, false); - } - verifyStrictIdent((IdentNode)lhs, "operand for " + opType.getName() + " operator"); - } - - return incDecExpression(unaryToken, opType, lhs, false); + return verifyIncDecExpression(unaryToken, opType, lhs, false); default: break; @@ -3247,29 +4372,16 @@ switch (type) { case INCPREFIX: case DECPREFIX: + final long opToken = token; final TokenType opType = type; final Expression lhs = expression; // ++, -- without operand.. if (lhs == null) { throw error(AbstractParser.message("expected.lvalue", type.getNameOrType())); } - - if (!(lhs instanceof AccessNode || - lhs instanceof IndexNode || - lhs instanceof IdentNode)) { - next(); - return referenceError(lhs, null, env._early_lvalue_error); - } - if (lhs instanceof IdentNode) { - if (!checkIdentLValue((IdentNode)lhs)) { - next(); - return referenceError(lhs, null, false); - } - verifyStrictIdent((IdentNode)lhs, "operand for " + opType.getName() + " operator"); - } - expression = incDecExpression(token, type, expression, true); next(); - break; + + return verifyIncDecExpression(opToken, opType, lhs, true); default: break; } @@ -3282,6 +4394,25 @@ return expression; } + private Expression verifyIncDecExpression(final long unaryToken, final TokenType opType, final Expression lhs, final boolean isPostfix) { + assert lhs != null; + + if (!(lhs instanceof AccessNode || + lhs instanceof IndexNode || + lhs instanceof IdentNode)) { + return referenceError(lhs, null, env._early_lvalue_error); + } + + if (lhs instanceof IdentNode) { + if (!checkIdentLValue((IdentNode)lhs)) { + return referenceError(lhs, null, false); + } + verifyIdent((IdentNode)lhs, "operand for " + opType.getName() + " operator"); + } + + return incDecExpression(unaryToken, opType, lhs, isPostfix); + } + /** * {@code * MultiplicativeExpression : @@ -3380,7 +4511,42 @@ // at expression start point! // Include commas in expression parsing. - return expression(unaryExpression(), COMMARIGHT.getPrecedence(), false); + return expression(false); + } + + private Expression expression(final boolean noIn) { + Expression assignmentExpression = assignmentExpression(noIn); + while (type == COMMARIGHT) { + long commaToken = token; + next(); + + boolean rhsRestParameter = false; + if (type == ELLIPSIS && isES6()) { + // (a, b, ...rest) is not a valid expression, unless we're parsing the parameter list of an arrow function (we need to throw the right error). + // But since the rest parameter is always last, at least we know that the expression has to end here and be followed by RPAREN and ARROW, so peek ahead. + if (isRestParameterEndOfArrowFunctionParameterList()) { + next(); + rhsRestParameter = true; + } + } + + Expression rhs = assignmentExpression(noIn); + + if (rhsRestParameter) { + rhs = ((IdentNode)rhs).setIsRestParameter(); + // Our only valid move is to end Expression here and continue with ArrowFunction. + // We've already checked that this is the parameter list of an arrow function (see above). + // RPAREN is next, so we'll finish the binary expression and drop out of the loop. + assert type == RPAREN; + } + + assignmentExpression = new BinaryNode(commaToken, assignmentExpression, rhs); + } + return assignmentExpression; + } + + private Expression expression(final int minPrecedence, final boolean noIn) { + return expression(unaryExpression(), minPrecedence, noIn); } private JoinPredecessorExpression joinPredecessorExpression() { @@ -3448,12 +4614,316 @@ return lhs; } + /** + * AssignmentExpression. + * + * AssignmentExpression[In, Yield] : + * ConditionalExpression[?In, ?Yield] + * [+Yield] YieldExpression[?In] + * ArrowFunction[?In, ?Yield] + * LeftHandSideExpression[?Yield] = AssignmentExpression[?In, ?Yield] + * LeftHandSideExpression[?Yield] AssignmentOperator AssignmentExpression[?In, ?Yield] + */ protected Expression assignmentExpression(final boolean noIn) { // This method is protected so that subclass can get details // at assignment expression start point! - // Exclude commas in expression parsing. - return expression(unaryExpression(), ASSIGN.getPrecedence(), noIn); + if (type == YIELD && inGeneratorFunction() && isES6()) { + return yieldExpression(noIn); + } + + final long startToken = token; + final int startLine = line; + final Expression exprLhs = conditionalExpression(noIn); + + if (type == ARROW && isES6()) { + if (checkNoLineTerminator()) { + final Expression paramListExpr; + if (exprLhs instanceof ExpressionList) { + paramListExpr = (((ExpressionList)exprLhs).getExpressions().isEmpty() ? null : ((ExpressionList)exprLhs).getExpressions().get(0)); + } else { + paramListExpr = exprLhs; + } + return arrowFunction(startToken, startLine, paramListExpr); + } + } + assert !(exprLhs instanceof ExpressionList); + + if (isAssignmentOperator(type)) { + final boolean isAssign = type == ASSIGN; + if (isAssign) { + defaultNames.push(exprLhs); + } + try { + final long assignToken = token; + next(); + final Expression exprRhs = assignmentExpression(noIn); + return verifyAssignment(assignToken, exprLhs, exprRhs); + } finally { + if (isAssign) { + defaultNames.pop(); + } + } + } else { + return exprLhs; + } + } + + /** + * Is type one of {@code = *= /= %= += -= <<= >>= >>>= &= ^= |=}? + */ + private static boolean isAssignmentOperator(TokenType type) { + switch (type) { + case ASSIGN: + case ASSIGN_ADD: + case ASSIGN_BIT_AND: + case ASSIGN_BIT_OR: + case ASSIGN_BIT_XOR: + case ASSIGN_DIV: + case ASSIGN_MOD: + case ASSIGN_MUL: + case ASSIGN_SAR: + case ASSIGN_SHL: + case ASSIGN_SHR: + case ASSIGN_SUB: + return true; + } + return false; + } + + /** + * ConditionalExpression. + */ + private Expression conditionalExpression(boolean noIn) { + return expression(TERNARY.getPrecedence(), noIn); + } + + /** + * ArrowFunction. + * + * @param startToken start token of the ArrowParameters expression + * @param functionLine start line of the arrow function + * @param paramListExpr ArrowParameters expression or {@code null} for {@code ()} (empty list) + */ + private Expression arrowFunction(final long startToken, final int functionLine, final Expression paramListExpr) { + // caller needs to check that there's no LineTerminator between parameter list and arrow + assert type != ARROW || checkNoLineTerminator(); + expect(ARROW); + + final long functionToken = Token.recast(startToken, ARROW); + final IdentNode name = new IdentNode(functionToken, Token.descPosition(functionToken), "=>:" + functionLine); + final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, FunctionNode.Kind.ARROW, functionLine, null); + functionNode.setFlag(FunctionNode.IS_ANONYMOUS); + + lc.push(functionNode); + try { + ParserContextBlockNode parameterBlock = newBlock(); + final List parameters; + try { + parameters = convertArrowFunctionParameterList(paramListExpr, functionLine); + functionNode.setParameters(parameters); + + if (!functionNode.isSimpleParameterList()) { + markEvalInArrowParameterList(parameterBlock); + } + } finally { + restoreBlock(parameterBlock); + } + Block functionBody = functionBody(functionNode); + + functionBody = maybeWrapBodyInParameterBlock(functionBody, parameterBlock); + + verifyParameterList(parameters, functionNode); + + final FunctionNode function = createFunctionNode( + functionNode, + functionToken, + name, + parameters, + FunctionNode.Kind.ARROW, + functionLine, + functionBody); + return function; + } finally { + lc.pop(functionNode); + } + } + + private void markEvalInArrowParameterList(final ParserContextBlockNode parameterBlock) { + final Iterator iter = lc.getFunctions(); + final ParserContextFunctionNode current = iter.next(); + final ParserContextFunctionNode parent = iter.next(); + + if (parent.getFlag(FunctionNode.HAS_EVAL) != 0) { + // we might have flagged has-eval in the parent function during parsing the parameter list, + // if the parameter list contains eval; must tag arrow function as has-eval. + for (final Statement st : parameterBlock.getStatements()) { + st.accept(new NodeVisitor(new LexicalContext()) { + @Override + public boolean enterCallNode(final CallNode callNode) { + if (callNode.getFunction() instanceof IdentNode && ((IdentNode) callNode.getFunction()).getName().equals("eval")) { + current.setFlag(FunctionNode.HAS_EVAL); + } + return true; + } + }); + } + // TODO: function containing the arrow function should not be flagged has-eval + } + } + + private List convertArrowFunctionParameterList(final Expression paramListExpr, final int functionLine) { + final List parameters; + if (paramListExpr == null) { + // empty parameter list, i.e. () => + parameters = Collections.emptyList(); + } else if (paramListExpr instanceof IdentNode || paramListExpr.isTokenType(ASSIGN) || isDestructuringLhs(paramListExpr)) { + parameters = Collections.singletonList(verifyArrowParameter(paramListExpr, 0, functionLine)); + } else if (paramListExpr instanceof BinaryNode && Token.descType(paramListExpr.getToken()) == COMMARIGHT) { + parameters = new ArrayList<>(); + Expression car = paramListExpr; + do { + final Expression cdr = ((BinaryNode) car).rhs(); + parameters.add(0, verifyArrowParameter(cdr, parameters.size(), functionLine)); + car = ((BinaryNode) car).lhs(); + } while (car instanceof BinaryNode && Token.descType(car.getToken()) == COMMARIGHT); + parameters.add(0, verifyArrowParameter(car, parameters.size(), functionLine)); + } else { + throw error(AbstractParser.message("expected.arrow.parameter"), paramListExpr.getToken()); + } + return parameters; + } + + private IdentNode verifyArrowParameter(Expression param, int index, int paramLine) { + final String contextString = "function parameter"; + if (param instanceof IdentNode) { + IdentNode ident = (IdentNode)param; + verifyStrictIdent(ident, contextString); + ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); + if (currentFunction != null) { + currentFunction.addParameterBinding(ident); + } + return ident; + } + + if (param.isTokenType(ASSIGN)) { + Expression lhs = ((BinaryNode) param).lhs(); + long paramToken = lhs.getToken(); + Expression initializer = ((BinaryNode) param).rhs(); + if (lhs instanceof IdentNode) { + // default parameter + IdentNode ident = (IdentNode) lhs; + + ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); + if (currentFunction != null) { + BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); + TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); + BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), ident, value); + lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + + currentFunction.addParameterBinding(ident); + currentFunction.setSimpleParameterList(false); + } + return ident; + } else if (isDestructuringLhs(lhs)) { + // binding pattern with initializer + // Introduce synthetic temporary parameter to capture the object to be destructured. + IdentNode ident = createIdentNode(paramToken, param.getFinish(), String.format("arguments[%d]", index)).setIsDestructuredParameter().setIsDefaultParameter(); + verifyDestructuringParameterBindingPattern(param, paramToken, paramLine, contextString); + + ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); + if (currentFunction != null) { + BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); + TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); + BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), param, value); + lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + } + return ident; + } + } else if (isDestructuringLhs(param)) { + // binding pattern + long paramToken = param.getToken(); + + // Introduce synthetic temporary parameter to capture the object to be destructured. + IdentNode ident = createIdentNode(paramToken, param.getFinish(), String.format("arguments[%d]", index)).setIsDestructuredParameter(); + verifyDestructuringParameterBindingPattern(param, paramToken, paramLine, contextString); + + ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); + if (currentFunction != null) { + BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), param, ident); + lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + } + return ident; + } + throw error(AbstractParser.message("invalid.arrow.parameter"), param.getToken()); + } + + private boolean checkNoLineTerminator() { + assert type == ARROW; + if (last == RPAREN) { + return true; + } else if (last == IDENT) { + return true; + } + for (int i = k - 1; i >= 0; i--) { + TokenType t = T(i); + switch (t) { + case RPAREN: + case IDENT: + return true; + case EOL: + return false; + case COMMENT: + continue; + default: + if (t.getKind() == TokenKind.FUTURESTRICT) { + return true; + } + return false; + } + } + return false; + } + + /** + * Peek ahead to see if what follows after the ellipsis is a rest parameter + * at the end of an arrow function parameter list. + */ + private boolean isRestParameterEndOfArrowFunctionParameterList() { + assert type == ELLIPSIS; + // find IDENT, RPAREN, ARROW, in that order, skipping over EOL (where allowed) and COMMENT + int i = 1; + for (;;) { + TokenType t = T(k + i++); + if (t == IDENT) { + break; + } else if (t == EOL || t == COMMENT) { + continue; + } else { + return false; + } + } + for (;;) { + TokenType t = T(k + i++); + if (t == RPAREN) { + break; + } else if (t == EOL || t == COMMENT) { + continue; + } else { + return false; + } + } + for (;;) { + TokenType t = T(k + i++); + if (t == ARROW) { + break; + } else if (t == COMMENT) { + continue; + } else { + return false; + } + } + return true; } /** @@ -3551,6 +5021,380 @@ cookedStrings.add(LiteralNode.newInstance(stringToken, finish, cookedString)); } + + /** + * Parse a module. + * + * Module : + * ModuleBody? + * + * ModuleBody : + * ModuleItemList + */ + private FunctionNode module(final String moduleName) { + boolean oldStrictMode = isStrictMode; + try { + isStrictMode = true; // Module code is always strict mode code. (ES6 10.2.1) + + // Make a pseudo-token for the script holding its start and length. + int functionStart = Math.min(Token.descPosition(Token.withDelimiter(token)), finish); + final long functionToken = Token.toDesc(FUNCTION, functionStart, source.getLength() - functionStart); + final int functionLine = line; + + final IdentNode ident = new IdentNode(functionToken, Token.descPosition(functionToken), moduleName); + final ParserContextFunctionNode script = createParserContextFunctionNode( + ident, + functionToken, + FunctionNode.Kind.MODULE, + functionLine, + Collections.emptyList()); + lc.push(script); + + final ParserContextModuleNode module = new ParserContextModuleNode(moduleName); + lc.push(module); + + final ParserContextBlockNode body = newBlock(); + + functionDeclarations = new ArrayList<>(); + moduleBody(); + addFunctionDeclarations(script); + functionDeclarations = null; + + restoreBlock(body); + body.setFlag(Block.NEEDS_SCOPE); + final Block programBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC | Block.IS_BODY, body.getStatements()); + lc.pop(module); + lc.pop(script); + script.setLastToken(token); + + expect(EOF); + + script.setModule(module.createModule()); + return createFunctionNode(script, functionToken, ident, Collections.emptyList(), FunctionNode.Kind.MODULE, functionLine, programBody); + } finally { + isStrictMode = oldStrictMode; + } + } + + /** + * Parse module body. + * + * ModuleBody : + * ModuleItemList + * + * ModuleItemList : + * ModuleItem + * ModuleItemList ModuleItem + * + * ModuleItem : + * ImportDeclaration + * ExportDeclaration + * StatementListItem + */ + private void moduleBody() { + loop: + while (type != EOF) { + switch (type) { + case EOF: + break loop; + case IMPORT: + importDeclaration(); + break; + case EXPORT: + exportDeclaration(); + break; + default: + // StatementListItem + statement(true, false, false, false); + break; + } + } + } + + + /** + * Parse import declaration. + * + * ImportDeclaration : + * import ImportClause FromClause ; + * import ModuleSpecifier ; + * ImportClause : + * ImportedDefaultBinding + * NameSpaceImport + * NamedImports + * ImportedDefaultBinding , NameSpaceImport + * ImportedDefaultBinding , NamedImports + * ImportedDefaultBinding : + * ImportedBinding + * ModuleSpecifier : + * StringLiteral + * ImportedBinding : + * BindingIdentifier + */ + private void importDeclaration() { + expect(IMPORT); + final ParserContextModuleNode module = lc.getCurrentModule(); + if (type == STRING || type == ESCSTRING) { + // import ModuleSpecifier ; + final String moduleSpecifier = (String) getValue(); + next(); + module.addModuleRequest(moduleSpecifier); + } else { + // import ImportClause FromClause ; + List importEntries; + if (type == MUL) { + importEntries = Collections.singletonList(nameSpaceImport()); + } else if (type == LBRACE) { + importEntries = namedImports(); + } else if (isBindingIdentifier()) { + // ImportedDefaultBinding + final IdentNode importedDefaultBinding = bindingIdentifier("ImportedBinding"); + Module.ImportEntry defaultImport = Module.ImportEntry.importDefault(importedDefaultBinding.getName()); + + if (type == COMMARIGHT) { + next(); + importEntries = new ArrayList<>(); + if (type == MUL) { + importEntries.add(nameSpaceImport()); + } else if (type == LBRACE) { + importEntries.addAll(namedImports()); + } else { + throw error(AbstractParser.message("expected.named.import")); + } + } else { + importEntries = Collections.singletonList(defaultImport); + } + } else { + throw error(AbstractParser.message("expected.import")); + } + + final String moduleSpecifier = fromClause(); + module.addModuleRequest(moduleSpecifier); + for (int i = 0; i < importEntries.size(); i++) { + module.addImportEntry(importEntries.get(i).withFrom(moduleSpecifier)); + } + } + expect(SEMICOLON); + } + + /** + * NameSpaceImport : + * * as ImportedBinding + * + * @return imported binding identifier + */ + private Module.ImportEntry nameSpaceImport() { + assert type == MUL; + next(); + final long asToken = token; + final String as = (String) expectValue(IDENT); + if (!"as".equals(as)) { + throw error(AbstractParser.message("expected.as"), asToken); + } + final IdentNode localNameSpace = bindingIdentifier("ImportedBinding"); + return Module.ImportEntry.importStarAsNameSpaceFrom(localNameSpace.getName()); + } + + /** + * NamedImports : + * { } + * { ImportsList } + * { ImportsList , } + * ImportsList : + * ImportSpecifier + * ImportsList , ImportSpecifier + * ImportSpecifier : + * ImportedBinding + * IdentifierName as ImportedBinding + * ImportedBinding : + * BindingIdentifier + */ + private List namedImports() { + assert type == LBRACE; + next(); + List importEntries = new ArrayList<>(); + while (type != RBRACE) { + final boolean bindingIdentifier = isBindingIdentifier(); + final long nameToken = token; + final IdentNode importName = getIdentifierName(); + if (type == IDENT && "as".equals(getValue())) { + next(); + final IdentNode localName = bindingIdentifier("ImportedBinding"); + importEntries.add(Module.ImportEntry.importSpecifier(importName.getName(), localName.getName())); + } else if (!bindingIdentifier) { + throw error(AbstractParser.message("expected.binding.identifier"), nameToken); + } else { + importEntries.add(Module.ImportEntry.importSpecifier(importName.getName())); + } + if (type == COMMARIGHT) { + next(); + } else { + break; + } + } + expect(RBRACE); + return importEntries; + } + + /** + * FromClause : + * from ModuleSpecifier + */ + private String fromClause() { + final long fromToken = token; + final String name = (String) expectValue(IDENT); + if (!"from".equals(name)) { + throw error(AbstractParser.message("expected.from"), fromToken); + } + if (type == STRING || type == ESCSTRING) { + final String moduleSpecifier = (String) getValue(); + next(); + return moduleSpecifier; + } else { + throw error(expectMessage(STRING)); + } + } + + /** + * Parse export declaration. + * + * ExportDeclaration : + * export * FromClause ; + * export ExportClause FromClause ; + * export ExportClause ; + * export VariableStatement + * export Declaration + * export default HoistableDeclaration[Default] + * export default ClassDeclaration[Default] + * export default [lookahead !in {function, class}] AssignmentExpression[In] ; + */ + private void exportDeclaration() { + expect(EXPORT); + final ParserContextModuleNode module = lc.getCurrentModule(); + switch (type) { + case MUL: { + next(); + final String moduleRequest = fromClause(); + expect(SEMICOLON); + module.addModuleRequest(moduleRequest); + module.addStarExportEntry(Module.ExportEntry.exportStarFrom(moduleRequest)); + break; + } + case LBRACE: { + final List exportEntries = exportClause(); + if (type == IDENT && "from".equals(getValue())) { + final String moduleRequest = fromClause(); + module.addModuleRequest(moduleRequest); + for (Module.ExportEntry exportEntry : exportEntries) { + module.addIndirectExportEntry(exportEntry.withFrom(moduleRequest)); + } + } else { + for (Module.ExportEntry exportEntry : exportEntries) { + module.addLocalExportEntry(exportEntry); + } + } + expect(SEMICOLON); + break; + } + case DEFAULT: + next(); + final Expression assignmentExpression; + IdentNode ident; + final int lineNumber = line; + final long rhsToken = token; + final boolean declaration; + switch (type) { + case FUNCTION: + assignmentExpression = functionExpression(false, true); + ident = ((FunctionNode) assignmentExpression).getIdent(); + declaration = true; + break; + case CLASS: + assignmentExpression = classDeclaration(true); + ident = ((ClassNode) assignmentExpression).getIdent(); + declaration = true; + break; + default: + assignmentExpression = assignmentExpression(false); + ident = null; + declaration = false; + break; + } + if (ident != null) { + module.addLocalExportEntry(Module.ExportEntry.exportDefault(ident.getName())); + } else { + ident = createIdentNode(Token.recast(rhsToken, IDENT), finish, Module.DEFAULT_EXPORT_BINDING_NAME); + lc.appendStatementToCurrentNode(new VarNode(lineNumber, Token.recast(rhsToken, LET), finish, ident, assignmentExpression)); + if (!declaration) { + expect(SEMICOLON); + } + module.addLocalExportEntry(Module.ExportEntry.exportDefault()); + } + break; + case VAR: + case LET: + case CONST: + final List statements = lc.getCurrentBlock().getStatements(); + final int previousEnd = statements.size(); + variableStatement(type); + for (final Statement statement : statements.subList(previousEnd, statements.size())) { + if (statement instanceof VarNode) { + module.addLocalExportEntry(Module.ExportEntry.exportSpecifier(((VarNode) statement).getName().getName())); + } + } + break; + case CLASS: { + final ClassNode classDeclaration = classDeclaration(false); + module.addLocalExportEntry(Module.ExportEntry.exportSpecifier(classDeclaration.getIdent().getName())); + break; + } + case FUNCTION: { + final FunctionNode functionDeclaration = (FunctionNode) functionExpression(true, true); + module.addLocalExportEntry(Module.ExportEntry.exportSpecifier(functionDeclaration.getIdent().getName())); + break; + } + default: + throw error(AbstractParser.message("invalid.export"), token); + } + } + + /** + * ExportClause : + * { } + * { ExportsList } + * { ExportsList , } + * ExportsList : + * ExportSpecifier + * ExportsList , ExportSpecifier + * ExportSpecifier : + * IdentifierName + * IdentifierName as IdentifierName + * + * @return a list of ExportSpecifiers + */ + private List exportClause() { + assert type == LBRACE; + next(); + List exports = new ArrayList<>(); + while (type != RBRACE) { + final IdentNode localName = getIdentifierName(); + if (type == IDENT && "as".equals(getValue())) { + next(); + final IdentNode exportName = getIdentifierName(); + exports.add(Module.ExportEntry.exportSpecifier(exportName.getName(), localName.getName())); + } else { + exports.add(Module.ExportEntry.exportSpecifier(localName.getName())); + } + if (type == COMMARIGHT) { + next(); + } else { + break; + } + } + expect(RBRACE); + return exports; + } + @Override public String toString() { return "'JavaScript Parsing'"; @@ -3564,6 +5408,12 @@ if (!flaggedCurrentFn) { fn.setFlag(FunctionNode.HAS_EVAL); flaggedCurrentFn = true; + if (fn.getKind() == FunctionNode.Kind.ARROW) { + // possible use of this in an eval that's nested in an arrow function, e.g.: + // function fun(){ return (() => eval("this"))(); }; + markThis(lc); + markNewTarget(lc); + } } else { fn.setFlag(FunctionNode.HAS_NESTED_EVAL); } @@ -3583,4 +5433,55 @@ private void appendStatement(final Statement statement) { lc.appendStatementToCurrentNode(statement); } + + private static void markSuperCall(final ParserContext lc) { + final Iterator iter = lc.getFunctions(); + while (iter.hasNext()) { + final ParserContextFunctionNode fn = iter.next(); + if (fn.getKind() != FunctionNode.Kind.ARROW) { + assert fn.isSubclassConstructor(); + fn.setFlag(FunctionNode.ES6_HAS_DIRECT_SUPER); + break; + } + } + } + + private ParserContextFunctionNode getCurrentNonArrowFunction() { + final Iterator iter = lc.getFunctions(); + while (iter.hasNext()) { + final ParserContextFunctionNode fn = iter.next(); + if (fn.getKind() != FunctionNode.Kind.ARROW) { + return fn; + } + } + return null; + } + + private static void markThis(final ParserContext lc) { + final Iterator iter = lc.getFunctions(); + while (iter.hasNext()) { + final ParserContextFunctionNode fn = iter.next(); + fn.setFlag(FunctionNode.USES_THIS); + if (fn.getKind() != FunctionNode.Kind.ARROW) { + break; + } + } + } + + private static void markNewTarget(final ParserContext lc) { + final Iterator iter = lc.getFunctions(); + while (iter.hasNext()) { + final ParserContextFunctionNode fn = iter.next(); + if (fn.getKind() != FunctionNode.Kind.ARROW) { + if (!fn.isProgram()) { + fn.setFlag(FunctionNode.ES6_USES_NEW_TARGET); + } + break; + } + } + } + + private boolean inGeneratorFunction() { + return lc.getCurrentFunction().getKind() == FunctionNode.Kind.GENERATOR; + } } diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContext.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContext.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContext.java Tue May 03 12:25:20 2016 -0700 @@ -278,7 +278,12 @@ return new NodeIterator<>(ParserContextFunctionNode.class); } - private class NodeIterator implements Iterator { + public ParserContextModuleNode getCurrentModule() { + final Iterator iter = new NodeIterator<>(ParserContextModuleNode.class, getCurrentFunction()); + return iter.hasNext() ? iter.next() : null; + } + + private class NodeIterator implements Iterator { private int index; private T next; private final Class clazz; diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java Tue May 03 12:25:20 2016 -0700 @@ -24,10 +24,12 @@ */ package jdk.nashorn.internal.parser; +import java.util.HashSet; import java.util.List; import jdk.nashorn.internal.codegen.Namespace; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.IdentNode; +import jdk.nashorn.internal.ir.Module; /** * ParserContextNode that represents a function that is currently being parsed @@ -46,11 +48,11 @@ /** Line number for function declaration */ private final int line; - /** Function node kind, see {@link FunctionNode#Kind} */ + /** Function node kind, see {@link FunctionNode.Kind} */ private final FunctionNode.Kind kind; /** List of parameter identifiers for function */ - private final List parameters; + private List parameters; /** Token for function start */ private final long token; @@ -61,6 +63,14 @@ /** Opaque node for parser end state, see {@link Parser} */ private Object endParserState; + private HashSet parameterBoundNames; + private IdentNode duplicateParameterBinding; + private boolean simpleParameterList = true; + + private Module module; + + private int debugFlags; + /** * @param token The token for the function * @param ident External function name @@ -155,6 +165,10 @@ return parameters; } + void setParameters(List parameters) { + this.parameters = parameters; + } + /** * Set last token * @param token New last token @@ -194,4 +208,70 @@ public int getId() { return isProgram() ? -1 : Token.descPosition(token); } + + /** + * Returns the debug flags for this function. + * + * @return the debug flags + */ + int getDebugFlags() { + return debugFlags; + } + + /** + * Sets a debug flag for this function. + * + * @param debugFlag the debug flag + */ + void setDebugFlag(final int debugFlag) { + debugFlags |= debugFlag; + } + + public boolean isMethod() { + return getFlag(FunctionNode.ES6_IS_METHOD) != 0; + } + + public boolean isClassConstructor() { + return getFlag(FunctionNode.ES6_IS_CLASS_CONSTRUCTOR) != 0; + } + + public boolean isSubclassConstructor() { + return getFlag(FunctionNode.ES6_IS_SUBCLASS_CONSTRUCTOR) != 0; + } + + boolean addParameterBinding(final IdentNode bindingIdentifier) { + if (Parser.isArguments(bindingIdentifier)) { + setFlag(FunctionNode.DEFINES_ARGUMENTS); + } + + if (parameterBoundNames == null) { + parameterBoundNames = new HashSet<>(); + } + if (parameterBoundNames.add(bindingIdentifier.getName())) { + return true; + } else { + duplicateParameterBinding = bindingIdentifier; + return false; + } + } + + public IdentNode getDuplicateParameterBinding() { + return duplicateParameterBinding; + } + + public boolean isSimpleParameterList() { + return simpleParameterList; + } + + public void setSimpleParameterList(final boolean simpleParameterList) { + this.simpleParameterList = simpleParameterList; + } + + public Module getModule() { + return module; + } + + public void setModule(final Module module) { + this.module = module; + } } diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextModuleNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextModuleNode.java Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2015, 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.nashorn.internal.parser; + +import java.util.ArrayList; +import java.util.List; + +import jdk.nashorn.internal.ir.Module; +import jdk.nashorn.internal.ir.Module.ExportEntry; +import jdk.nashorn.internal.ir.Module.ImportEntry; + +/** + * ParserContextNode that represents a module. + */ +class ParserContextModuleNode extends ParserContextBaseNode { + + /** Module name. */ + private final String name; + + private List requestedModules = new ArrayList<>(); + private List importEntries = new ArrayList<>(); + private List localExportEntries = new ArrayList<>(); + private List indirectExportEntries = new ArrayList<>(); + private List starExportEntries = new ArrayList<>(); + + /** + * Constructor. + * + * @param name name of the module + */ + ParserContextModuleNode(final String name) { + this.name = name; + } + + /** + * Returns the name of the module. + * + * @return name of the module + */ + public String getModuleName() { + return name; + } + + public void addModuleRequest(final String moduleRequest) { + requestedModules.add(moduleRequest); + } + + public void addImportEntry(final ImportEntry importEntry) { + importEntries.add(importEntry); + } + + public void addLocalExportEntry(final ExportEntry exportEntry) { + localExportEntries.add(exportEntry); + } + + public void addIndirectExportEntry(final ExportEntry exportEntry) { + indirectExportEntries.add(exportEntry); + } + + public void addStarExportEntry(final ExportEntry exportEntry) { + starExportEntries.add(exportEntry); + } + + public Module createModule() { + return new Module(requestedModules, importEntries, localExportEntries, indirectExportEntries, starExportEntries); + } +} diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/TokenType.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/TokenType.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/TokenType.java Tue May 03 12:25:20 2016 -0700 @@ -82,7 +82,7 @@ ASSIGN (BINARY, "=", 2, false), EQ (BINARY, "==", 9, true), EQ_STRICT (BINARY, "===", 9, true), - BIND (BINARY, "=>", 9, true), + ARROW (BINARY, "=>", 2, true), GT (BINARY, ">", 10, true), GE (BINARY, ">=", 10, true), SAR (BINARY, ">>", 11, true), @@ -100,6 +100,7 @@ OR (BINARY, "||", 4, true), RBRACE (BRACKET, "}"), BIT_NOT (UNARY, "~", 14, false), + ELLIPSIS (UNARY, "..."), // ECMA 7.6.1.1 Keywords, 7.6.1.2 Future Reserved Words. // All other Java keywords are commented out. @@ -190,7 +191,10 @@ COMMALEFT (IR, null), DECPOSTFIX (IR, null), - INCPOSTFIX (IR, null); + INCPOSTFIX (IR, null), + SPREAD_ARGUMENT(IR, null), + SPREAD_ARRAY (IR, null), + YIELD_STAR (IR, null); /** Next token kind in token lookup table. */ private TokenType next; @@ -251,7 +255,6 @@ return kind == BINARY && (!noIn || this != IN) && precedence != 0; } - public int getLength() { assert name != null : "Token name not set"; return name.length(); diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java Tue May 03 12:25:20 2016 -0700 @@ -1403,11 +1403,11 @@ return null; } - if (env._print_ast || functionNode.getFlag(FunctionNode.IS_PRINT_AST)) { + if (env._print_ast || functionNode.getDebugFlag(FunctionNode.DEBUG_PRINT_AST)) { getErr().println(new ASTWriter(functionNode)); } - if (env._print_parse || functionNode.getFlag(FunctionNode.IS_PRINT_PARSE)) { + if (env._print_parse || functionNode.getDebugFlag(FunctionNode.DEBUG_PRINT_PARSE)) { getErr().println(new PrintVisitor(functionNode, true, false)); } } diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/DtoaBuffer.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/DtoaBuffer.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/DtoaBuffer.java Tue May 03 12:25:20 2016 -0700 @@ -176,6 +176,8 @@ buffer.append('0'); } buffer.append(chars, 0, length); + } else { + decimalPoint = 1; } } else if (decimalPoint >= length) { // large integer, add trailing zeroes diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties Tue May 03 12:25:20 2016 -0700 @@ -63,6 +63,27 @@ parser.error.missing.const.assignment=Missing assignment to constant "{0}" parser.error.unterminated.template.expression=Expected } after expression in template literal +# ES6 mode error messages +parser.error.multiple.constructors=Class contains more than one constructor +parser.error.generator.constructor=Class constructor must not be a generator +parser.error.accessor.constructor=Class constructor must not be an accessor +parser.error.static.prototype.method=Static class method must not be named 'prototype' +parser.error.missing.destructuring.assignment=Missing assignment in destructuring declaration +parser.error.let.binding.for='let' is not a valid binding name in a for loop +parser.error.invalid.export=invalid export declaration +parser.error.expected.binding=expected BindingIdentifier or BindingPattern +parser.error.multiple.proto.key=property name __proto__ appears more than once in object literal +parser.error.new.target.in.function=new.target expression is only allowed in functions +parser.error.expected.target=expected 'target' +parser.error.invalid.super=invalid use of keyword super +parser.error.expected.arrow.parameter=expected arrow function parameter list +parser.error.invalid.arrow.parameter=invalid arrow function parameter +parser.error.expected.named.import=expected NameSpaceImport or NamedImports +parser.error.expected.import=expected ImportClause or ModuleSpecifier +parser.error.expected.as=expected 'as' +parser.error.expected.binding.identifier=expected BindingIdentifier +parser.error.expected.from=expected 'from' + # strict mode error messages parser.error.strict.no.with="with" statement cannot be used in strict mode parser.error.strict.name="{0}" cannot be used as {1} in strict mode diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java Tue May 03 12:25:20 2016 -0700 @@ -438,6 +438,9 @@ final File file = new File(fileName); final ScriptFunction script = context.compileScript(sourceFor(fileName, file), global); if (script == null || errors.getNumberOfErrors() != 0) { + if (context.getEnv()._parse_only && !errors.hasErrors()) { + continue; // No error, continue to consume all files in list + } return COMPILATION_ERROR; } diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/test/script/basic/JDK-8155025.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8155025.js Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,37 @@ +/* + * 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. + * + * 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. + */ + +/** + * JDK-8155025: 0.001.toFixed(2) should return "0.00" not "0" + * + * @test + * @run + */ + +for (var i = 0, zeros=""; i <= 9; i++, zeros += "0") { + var n = Number("0." + zeros + "1"); + for (var j = 1; j <= i + 3; j++) { + print(n + ".toFixed(" + j + ")=" + n.toFixed(j)); + } +} + diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/test/script/basic/JDK-8155025.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8155025.js.EXPECTED Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,75 @@ +0.1.toFixed(1)=0.1 +0.1.toFixed(2)=0.10 +0.1.toFixed(3)=0.100 +0.01.toFixed(1)=0.0 +0.01.toFixed(2)=0.01 +0.01.toFixed(3)=0.010 +0.01.toFixed(4)=0.0100 +0.001.toFixed(1)=0.0 +0.001.toFixed(2)=0.00 +0.001.toFixed(3)=0.001 +0.001.toFixed(4)=0.0010 +0.001.toFixed(5)=0.00100 +0.0001.toFixed(1)=0.0 +0.0001.toFixed(2)=0.00 +0.0001.toFixed(3)=0.000 +0.0001.toFixed(4)=0.0001 +0.0001.toFixed(5)=0.00010 +0.0001.toFixed(6)=0.000100 +0.00001.toFixed(1)=0.0 +0.00001.toFixed(2)=0.00 +0.00001.toFixed(3)=0.000 +0.00001.toFixed(4)=0.0000 +0.00001.toFixed(5)=0.00001 +0.00001.toFixed(6)=0.000010 +0.00001.toFixed(7)=0.0000100 +0.000001.toFixed(1)=0.0 +0.000001.toFixed(2)=0.00 +0.000001.toFixed(3)=0.000 +0.000001.toFixed(4)=0.0000 +0.000001.toFixed(5)=0.00000 +0.000001.toFixed(6)=0.000001 +0.000001.toFixed(7)=0.0000010 +0.000001.toFixed(8)=0.00000100 +1e-7.toFixed(1)=0.0 +1e-7.toFixed(2)=0.00 +1e-7.toFixed(3)=0.000 +1e-7.toFixed(4)=0.0000 +1e-7.toFixed(5)=0.00000 +1e-7.toFixed(6)=0.000000 +1e-7.toFixed(7)=0.0000001 +1e-7.toFixed(8)=0.00000010 +1e-7.toFixed(9)=0.000000100 +1e-8.toFixed(1)=0.0 +1e-8.toFixed(2)=0.00 +1e-8.toFixed(3)=0.000 +1e-8.toFixed(4)=0.0000 +1e-8.toFixed(5)=0.00000 +1e-8.toFixed(6)=0.000000 +1e-8.toFixed(7)=0.0000000 +1e-8.toFixed(8)=0.00000001 +1e-8.toFixed(9)=0.000000010 +1e-8.toFixed(10)=0.0000000100 +1e-9.toFixed(1)=0.0 +1e-9.toFixed(2)=0.00 +1e-9.toFixed(3)=0.000 +1e-9.toFixed(4)=0.0000 +1e-9.toFixed(5)=0.00000 +1e-9.toFixed(6)=0.000000 +1e-9.toFixed(7)=0.0000000 +1e-9.toFixed(8)=0.00000000 +1e-9.toFixed(9)=0.000000001 +1e-9.toFixed(10)=0.0000000010 +1e-9.toFixed(11)=0.00000000100 +1e-10.toFixed(1)=0.0 +1e-10.toFixed(2)=0.00 +1e-10.toFixed(3)=0.000 +1e-10.toFixed(4)=0.0000 +1e-10.toFixed(5)=0.00000 +1e-10.toFixed(6)=0.000000 +1e-10.toFixed(7)=0.0000000 +1e-10.toFixed(8)=0.00000000 +1e-10.toFixed(9)=0.000000000 +1e-10.toFixed(10)=0.0000000001 +1e-10.toFixed(11)=0.00000000010 +1e-10.toFixed(12)=0.000000000100 diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/test/script/basic/es6/parser-es6.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/es6/parser-es6.js Tue May 03 12:25:20 2016 -0700 @@ -0,0 +1,94 @@ +/* + * 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. + * + * 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. + */ + +/** + * JDK-8134503: support ES6 parsing in Nashorn + * + * @test + * @option --language=es6 + * @option --parse-only + */ + + +[].map(v => v + 1); + +class A extends B.C { + constructor(a, b) { + super(a, b); + } + someMethod(c) { + super.someMethod(); + } + get g() { + return this.g; + } + set s(t) { + this.t = t; + } + static m() { + return k; + } +} + +var obj = { + __proto__: theProtoObj, + handler, + r() { + return super.m(); + }, + [ '__' + (() => 'x')() ]: 1, + *q (x, y) { + yield 1; + } +}; + +var [a, , b] = [1, 2, 3]; + +var { x: a, y: { z: b }, w: c } = abc(); + +var {a, b, c} = abc(); + +var o = { x, y }; + +function g({name: x}) { + return x; +} + +foo(a, ...b); + +var c = [ ...s ]; + +var [a] = []; + +var [a = 1] = []; + + +var f = { + [Symbol.iterator]: function*() { + var cur = 1; + for (;;) { + yield cur; + } + } +}; + diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/test/script/basic/yield.js --- a/nashorn/test/script/basic/yield.js Tue May 03 09:48:02 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/* - * 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 - * 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. - */ - - -/** - * Check yield keyword is parsed and yield statement does nothing (yet). - * - * @test - * @run - */ - -function func() { - yield 2; -} diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/test/script/error/NASHORN-154/function_mult_params_in_strict.js.EXPECTED --- a/nashorn/test/script/error/NASHORN-154/function_mult_params_in_strict.js.EXPECTED Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/test/script/error/NASHORN-154/function_mult_params_in_strict.js.EXPECTED Tue May 03 12:25:20 2016 -0700 @@ -1,3 +1,3 @@ -test/script/error/NASHORN-154/function_mult_params_in_strict.js:38:14 strict mode function cannot have duplicate parameter name "x" +test/script/error/NASHORN-154/function_mult_params_in_strict.js:38:17 strict mode function cannot have duplicate parameter name "x" function func(x, x) {} - ^ + ^ diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/test/script/nosecurity/parserapi.js.EXPECTED --- a/nashorn/test/script/nosecurity/parserapi.js.EXPECTED Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/test/script/nosecurity/parserapi.js.EXPECTED Tue May 03 12:25:20 2016 -0700 @@ -100,7 +100,7 @@ "startPosition": "1181", "properties": [ { - "endPosition": "1185", + "endPosition": "1187", "kind": "PROPERTY", "value": { "endPosition": "1187", @@ -2386,7 +2386,7 @@ "startPosition": "1139", "properties": [ { - "endPosition": "1143", + "endPosition": "1146", "kind": "PROPERTY", "value": { "endPosition": "1146", @@ -2403,7 +2403,7 @@ } }, { - "endPosition": "1150", + "endPosition": "1152", "kind": "PROPERTY", "value": { "endPosition": "1152", @@ -2443,7 +2443,7 @@ "startPosition": "1160", "properties": [ { - "endPosition": "1166", + "endPosition": "1169", "kind": "PROPERTY", "value": { "endPosition": "1169", @@ -2460,7 +2460,7 @@ } }, { - "endPosition": "1175", + "endPosition": "1177", "kind": "PROPERTY", "value": { "endPosition": "1177", @@ -2914,7 +2914,7 @@ "startPosition": "1178", "properties": [ { - "endPosition": "1182", + "endPosition": "1184", "kind": "PROPERTY", "value": { "endPosition": "1184", @@ -3395,7 +3395,7 @@ "startPosition": "1200", "properties": [ { - "endPosition": "1206", + "endPosition": "1214", "kind": "PROPERTY", "value": { "endPosition": "1214", @@ -4709,11 +4709,11 @@ , { "fileName": "parsernegativetests/strict_repeatparam.js", - "code": "ident (1119, 1)", - "columnNumber": "14", + "code": "ident (1122, 1)", + "columnNumber": "17", "kind": "ERROR", - "position": "1119", - "message": "parsernegativetests/strict_repeatparam.js:31:14 strict mode function cannot have duplicate parameter name \"x\"\nfunction func(x, x) {}\n ^", + "position": "1122", + "message": "parsernegativetests/strict_repeatparam.js:31:17 strict mode function cannot have duplicate parameter name \"x\"\nfunction func(x, x) {}\n ^", "lineNumber": "31" } , diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/test/script/nosecurity/parserapi_strict.js.EXPECTED --- a/nashorn/test/script/nosecurity/parserapi_strict.js.EXPECTED Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/test/script/nosecurity/parserapi_strict.js.EXPECTED Tue May 03 12:25:20 2016 -0700 @@ -6,10 +6,10 @@ with({}) {} ^ -repeat_param.js:2:15 strict mode function cannot have duplicate parameter name "x" +repeat_param.js:2:18 strict mode function cannot have duplicate parameter name "x" function func(x, x) {} - ^ + ^ repeat_prop.js:2:22 Property "foo" already defined var obj = { foo: 34, foo: 'hello' }; diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/test/script/nosecurity/treeapi/array_literal.js.EXPECTED --- a/nashorn/test/script/nosecurity/treeapi/array_literal.js.EXPECTED Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/test/script/nosecurity/treeapi/array_literal.js.EXPECTED Tue May 03 12:25:20 2016 -0700 @@ -74,7 +74,7 @@ "properties": [ { "getter": "null", - "endPosition": "74", + "endPosition": "76", "kind": "PROPERTY", "setter": "null", "value": { diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/test/script/nosecurity/treeapi/objectLiteral.js.EXPECTED --- a/nashorn/test/script/nosecurity/treeapi/objectLiteral.js.EXPECTED Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/test/script/nosecurity/treeapi/objectLiteral.js.EXPECTED Tue May 03 12:25:20 2016 -0700 @@ -6,7 +6,7 @@ "properties": [ { "getter": "null", - "endPosition": "8", + "endPosition": "12", "kind": "PROPERTY", "setter": "null", "value": { @@ -38,7 +38,7 @@ "properties": [ { "getter": "null", - "endPosition": "34", + "endPosition": "37", "kind": "PROPERTY", "setter": "null", "value": { @@ -57,7 +57,7 @@ }, { "getter": "null", - "endPosition": "41", + "endPosition": "43", "kind": "PROPERTY", "setter": "null", "value": { @@ -83,7 +83,7 @@ "properties": [ { "getter": "null", - "endPosition": "57", + "endPosition": "60", "kind": "PROPERTY", "setter": "null", "value": { @@ -102,7 +102,7 @@ }, { "getter": "null", - "endPosition": "66", + "endPosition": "68", "kind": "PROPERTY", "setter": "null", "value": { diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/test/script/nosecurity/treeapi/property.js.EXPECTED --- a/nashorn/test/script/nosecurity/treeapi/property.js.EXPECTED Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/test/script/nosecurity/treeapi/property.js.EXPECTED Tue May 03 12:25:20 2016 -0700 @@ -1,7 +1,7 @@ [ { "getter": "null", - "endPosition": "17", + "endPosition": "22", "kind": "PROPERTY", "setter": "null", "value": { @@ -20,7 +20,7 @@ }, { "getter": "null", - "endPosition": "31", + "endPosition": "38", "kind": "PROPERTY", "setter": "null", "value": { @@ -45,7 +45,7 @@ }, { "getter": "null", - "endPosition": "46", + "endPosition": "61", "kind": "PROPERTY", "setter": "null", "value": { @@ -72,7 +72,7 @@ }, { "getter": "null", - "endPosition": "69", + "endPosition": "72", "kind": "PROPERTY", "setter": "null", "value": { diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/test/script/nosecurity/treeapi/throw.js.EXPECTED --- a/nashorn/test/script/nosecurity/treeapi/throw.js.EXPECTED Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/test/script/nosecurity/treeapi/throw.js.EXPECTED Tue May 03 12:25:20 2016 -0700 @@ -80,7 +80,7 @@ "properties": [ { "getter": "null", - "endPosition": "97", + "endPosition": "105", "kind": "PROPERTY", "setter": "null", "value": { diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/test/script/nosecurity/treeapi/with.js.EXPECTED --- a/nashorn/test/script/nosecurity/treeapi/with.js.EXPECTED Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/test/script/nosecurity/treeapi/with.js.EXPECTED Tue May 03 12:25:20 2016 -0700 @@ -49,7 +49,7 @@ "properties": [ { "getter": "null", - "endPosition": "34", + "endPosition": "39", "kind": "PROPERTY", "setter": "null", "value": { diff -r 8ff4232c93c2 -r 74cd426ebb3d nashorn/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java --- a/nashorn/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java Tue May 03 09:48:02 2016 -0700 +++ b/nashorn/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java Tue May 03 12:25:20 2016 -0700 @@ -194,9 +194,9 @@ pb.redirectError(errorFileHandle); final Process process = pb.start(); - process.waitFor(); + final int exitCode = process.waitFor(); - if (errorFileHandle.length() > 0) { + if (exitCode != 0 || errorFileHandle.length() > 0) { if (expectRunFailure) { return; }