+
+// Addresses of functions to be referenced using static linking.
+void* funcs[] = {
+ //string.h
+ &strlen,
+ &strcat,
+ //math.h
+ &abs,
+ &fabs,
+ &fabsf,
+ &fabsl,
+ &fmod,
+ &fmodf,
+ &fmodl,
+ &remainder,
+ &remainderf,
+ &remainderl,
+ &remquo,
+ &remquof,
+ &remquol,
+ &fma,
+ &fmaf,
+ &fmal,
+ &fmax,
+ &fmaxf,
+ &fmaxl,
+ &fmin,
+ &fminf,
+ &fminl,
+ &fdim,
+ &fdimf,
+ &fdiml,
+ &nan,
+ &nanf,
+ &nanl,
+ &exp,
+ &expf,
+ &expl,
+ &exp2,
+ &exp2f,
+ &exp2l,
+ &expm1,
+ &expm1f,
+ &expm1l,
+ &log,
+ &logf,
+ &logl,
+ &log10,
+ &log10f,
+ &log10l,
+ &log2,
+ &log2f,
+ &log2l,
+ &log1p,
+ &log1pf,
+ &log1pl,
+ &pow,
+ &powf,
+ &powl,
+ &sqrt,
+ &sqrtf,
+ &sqrtl,
+ &cbrt,
+ &cbrtf,
+ &cbrtl,
+ &hypot,
+ &hypotf,
+ &hypotl,
+ &sin,
+ &sinf,
+ &sinl,
+ &cos,
+ &cosf,
+ &cosl,
+ &tan,
+ &tanf,
+ &tanl,
+ &asin,
+ &asinf,
+ &asinl,
+ &acos,
+ &acosf,
+ &acosl,
+ &atan,
+ &atanf,
+ &atanl,
+ &atan2,
+ &atan2f,
+ &atan2l,
+ &sinh,
+ &sinhf,
+ &sinhl,
+ &cosh,
+ &coshf,
+ &coshl,
+ &tanh,
+ &tanhf,
+ &tanhl,
+ &asinh,
+ &asinhf,
+ &asinhl,
+ &acosh,
+ &acoshf,
+ &acoshl,
+ &atanh,
+ &atanhf,
+ &atanhl,
+ &erf,
+ &erff,
+ &erfl,
+ &erfc,
+ &erfcf,
+ &erfcl,
+ &tgamma,
+ &tgammaf,
+ &tgammal,
+ &lgamma,
+ &lgammaf,
+ &lgammal,
+ &ceil,
+ &ceilf,
+ &ceill,
+ &floor,
+ &floorf,
+ &floorl,
+ &trunc,
+ &truncf,
+ &truncl,
+ &round,
+ &roundf,
+ &roundl,
+ &lround,
+ &lroundf,
+ &lroundl,
+ &llround,
+ &llroundf,
+ &llroundl,
+ &nearbyint,
+ &nearbyintf,
+ &nearbyintl,
+ &rintf,
+ &rintl,
+ &lrint,
+ &lrintf,
+ &lrintl,
+ &llrint,
+ &llrintf,
+ &llrintl,
+ &frexpf,
+ &ldexpf,
+ &modff,
+ &scalbn,
+ &scalbnf,
+ &scalbnl,
+ &scalbln,
+ &scalblnf,
+ &scalblnl,
+ &ilogb,
+ &ilogbf,
+ &ilogbl,
+ &logb,
+ &logbf,
+ &logbl,
+ &nextafter,
+ &nextafterf,
+ &nextafterl,
+ &nexttoward,
+ &nexttowardf,
+ &nexttowardl,
+ ©sign,
+ ©signf,
+ ©signl,
+ &isnan
+};
diff --git a/src/java.base/share/classes/java/lang/Double.java b/src/java.base/share/classes/java/lang/Double.java
index 14ddcd0de2a5e..580f8b26cab22 100644
--- a/src/java.base/share/classes/java/lang/Double.java
+++ b/src/java.base/share/classes/java/lang/Double.java
@@ -234,7 +234,7 @@
* equivalent of 15 to 17 digits of decimal precision. (The
* equivalent precision varies according to the different relative
* densities of binary and decimal values at different points along the
- * real number line).
+ * real number line.)
*
* This representation hazard of decimal fractions is one reason to
* use caution when storing monetary values as {@code float} or {@code
@@ -316,7 +316,7 @@
*
* {@snippet lang="java" :
* double d = 0.0;
- * while(d != 1.0) { // Surprising infinite loop
+ * while (d != 1.0) { // Surprising infinite loop
* d += 0.1; // Sum never _exactly_ equals 1.0
* }
* }
@@ -325,7 +325,7 @@
*
* {@snippet lang="java" :
* double d = 0.0;
- * for(int i = 0; i < 10; i++) {
+ * for (int i = 0; i < 10; i++) {
* d += 0.1;
* } // Value of d is equal to Math.nextDown(1.0).
* }
@@ -335,7 +335,7 @@
*
* {@snippet lang="java" :
* double d = 0.0;
- * while(d <= 1.0) {
+ * while (d <= 1.0) {
* d += 0.1;
* } // Value of d approximately 1.0999999999999999
* }
diff --git a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
index 6810fa6222d1a..79dc47e68169c 100644
--- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
+++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
@@ -26,17 +26,29 @@
package java.lang.runtime;
import java.lang.Enum.EnumDesc;
+import java.lang.constant.ClassDesc;
+import java.lang.constant.ConstantDescs;
+import java.lang.constant.MethodTypeDesc;
import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.lang.reflect.AccessFlag;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
+import java.util.function.BiPredicate;
import java.util.stream.Stream;
import jdk.internal.access.SharedSecrets;
+import jdk.internal.classfile.Classfile;
+import jdk.internal.classfile.Label;
+import jdk.internal.classfile.instruction.SwitchCase;
import jdk.internal.vm.annotation.Stable;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
import static java.util.Objects.requireNonNull;
/**
@@ -54,26 +66,16 @@ private SwitchBootstraps() {}
private static final Object SENTINEL = new Object();
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
- private static final MethodHandle INSTANCEOF_CHECK;
- private static final MethodHandle INTEGER_EQ_CHECK;
- private static final MethodHandle OBJECT_EQ_CHECK;
- private static final MethodHandle ENUM_EQ_CHECK;
private static final MethodHandle NULL_CHECK;
private static final MethodHandle IS_ZERO;
private static final MethodHandle CHECK_INDEX;
private static final MethodHandle MAPPED_ENUM_LOOKUP;
+ private static final MethodTypeDesc TYPES_SWITCH_DESCRIPTOR =
+ MethodTypeDesc.ofDescriptor("(Ljava/lang/Object;ILjava/util/function/BiPredicate;Ljava/util/List;)I");
+
static {
try {
- INSTANCEOF_CHECK = MethodHandles.permuteArguments(LOOKUP.findVirtual(Class.class, "isInstance",
- MethodType.methodType(boolean.class, Object.class)),
- MethodType.methodType(boolean.class, Object.class, Class.class), 1, 0);
- INTEGER_EQ_CHECK = LOOKUP.findStatic(SwitchBootstraps.class, "integerEqCheck",
- MethodType.methodType(boolean.class, Object.class, Integer.class));
- OBJECT_EQ_CHECK = LOOKUP.findStatic(Objects.class, "equals",
- MethodType.methodType(boolean.class, Object.class, Object.class));
- ENUM_EQ_CHECK = LOOKUP.findStatic(SwitchBootstraps.class, "enumEqCheck",
- MethodType.methodType(boolean.class, Object.class, EnumDesc.class, MethodHandles.Lookup.class, ResolvedEnumLabel.class));
NULL_CHECK = LOOKUP.findStatic(Objects.class, "isNull",
MethodType.methodType(boolean.class, Object.class));
IS_ZERO = LOOKUP.findStatic(SwitchBootstraps.class, "isZero",
@@ -155,7 +157,9 @@ public static CallSite typeSwitch(MethodHandles.Lookup lookup,
labels = labels.clone();
Stream.of(labels).forEach(SwitchBootstraps::verifyLabel);
- MethodHandle target = createMethodHandleSwitch(lookup, labels);
+ MethodHandle target = generateInnerClass(lookup, labels);
+
+ target = withIndexCheck(target, labels.length);
return new ConstantCallSite(target);
}
@@ -173,79 +177,6 @@ private static void verifyLabel(Object label) {
}
}
- /*
- * Construct test chains for labels inside switch, to handle switch repeats:
- * switch (idx) {
- * case 0 -> if (selector matches label[0]) return 0; else if (selector matches label[1]) return 1; else ...
- * case 1 -> if (selector matches label[1]) return 1; else ...
- * ...
- * }
- */
- private static MethodHandle createRepeatIndexSwitch(MethodHandles.Lookup lookup, Object[] labels) {
- MethodHandle def = MethodHandles.dropArguments(MethodHandles.constant(int.class, labels.length), 0, Object.class);
- MethodHandle[] testChains = new MethodHandle[labels.length];
- List labelsList = List.of(labels).reversed();
-
- for (int i = 0; i < labels.length; i++) {
- MethodHandle test = def;
- int idx = labels.length - 1;
- List currentLabels = labelsList.subList(0, labels.length - i);
-
- for (int j = 0; j < currentLabels.size(); j++, idx--) {
- Object currentLabel = currentLabels.get(j);
- if (j + 1 < currentLabels.size() && currentLabels.get(j + 1) == currentLabel) continue;
- MethodHandle currentTest;
- if (currentLabel instanceof Class>) {
- currentTest = INSTANCEOF_CHECK;
- } else if (currentLabel instanceof Integer) {
- currentTest = INTEGER_EQ_CHECK;
- } else if (currentLabel instanceof EnumDesc) {
- currentTest = MethodHandles.insertArguments(ENUM_EQ_CHECK, 2, lookup, new ResolvedEnumLabel());
- } else {
- currentTest = OBJECT_EQ_CHECK;
- }
- test = MethodHandles.guardWithTest(MethodHandles.insertArguments(currentTest, 1, currentLabel),
- MethodHandles.dropArguments(MethodHandles.constant(int.class, idx), 0, Object.class),
- test);
- }
- testChains[i] = MethodHandles.dropArguments(test, 0, int.class);
- }
-
- return MethodHandles.tableSwitch(MethodHandles.dropArguments(def, 0, int.class), testChains);
- }
-
- /*
- * Construct code that maps the given selector and repeat index to a case label number:
- * if (selector == null) return -1;
- * else return "createRepeatIndexSwitch(labels)"
- */
- private static MethodHandle createMethodHandleSwitch(MethodHandles.Lookup lookup, Object[] labels) {
- MethodHandle mainTest;
- MethodHandle def = MethodHandles.dropArguments(MethodHandles.constant(int.class, labels.length), 0, Object.class);
- if (labels.length > 0) {
- mainTest = createRepeatIndexSwitch(lookup, labels);
- } else {
- mainTest = MethodHandles.dropArguments(def, 0, int.class);
- }
- MethodHandle body =
- MethodHandles.guardWithTest(MethodHandles.dropArguments(NULL_CHECK, 0, int.class),
- MethodHandles.dropArguments(MethodHandles.constant(int.class, -1), 0, int.class, Object.class),
- mainTest);
- MethodHandle switchImpl =
- MethodHandles.permuteArguments(body, MethodType.methodType(int.class, Object.class, int.class), 1, 0);
- return withIndexCheck(switchImpl, labels.length);
- }
-
- private static boolean integerEqCheck(Object value, Integer constant) {
- if (value instanceof Number input && constant.intValue() == input.intValue()) {
- return true;
- } else if (value instanceof Character input && constant.intValue() == input.charValue()) {
- return true;
- }
-
- return false;
- }
-
private static boolean isZero(int value) {
return value == 0;
}
@@ -330,16 +261,16 @@ public static CallSite enumSwitch(MethodHandles.Lookup lookup,
//If all labels are enum constants, construct an optimized handle for repeat index 0:
//if (selector == null) return -1
//else if (idx == 0) return mappingArray[selector.ordinal()]; //mapping array created lazily
- //else return "createRepeatIndexSwitch(labels)"
+ //else return "typeSwitch(labels)"
MethodHandle body =
MethodHandles.guardWithTest(MethodHandles.dropArguments(NULL_CHECK, 0, int.class),
MethodHandles.dropArguments(MethodHandles.constant(int.class, -1), 0, int.class, Object.class),
MethodHandles.guardWithTest(MethodHandles.dropArguments(IS_ZERO, 1, Object.class),
- createRepeatIndexSwitch(lookup, labels),
+ generateInnerClass(lookup, labels),
MethodHandles.insertArguments(MAPPED_ENUM_LOOKUP, 1, lookup, enumClass, labels, new EnumMap())));
target = MethodHandles.permuteArguments(body, MethodType.methodType(int.class, Object.class, int.class), 1, 0);
} else {
- target = createMethodHandleSwitch(lookup, labels);
+ target = generateInnerClass(lookup, labels);
}
target = target.asType(invocationType);
@@ -360,7 +291,7 @@ private static > Object convertEnumConstants(MethodHandles.Loo
}
return label;
} else if (labelClass == String.class) {
- return EnumDesc.of(enumClassTemplate.describeConstable().get(), (String) label);
+ return EnumDesc.of(enumClassTemplate.describeConstable().orElseThrow(), (String) label);
} else {
throw new IllegalArgumentException("label with illegal type found: " + labelClass +
", expected label of type either String or Class");
@@ -389,45 +320,225 @@ private static > int mappedEnumLookup(T value, MethodHandles.L
return enumMap.map[value.ordinal()];
}
- private static boolean enumEqCheck(Object value, EnumDesc> label, MethodHandles.Lookup lookup, ResolvedEnumLabel resolvedEnum) {
- if (resolvedEnum.resolvedEnum == null) {
- Object resolved;
-
- try {
- if (!(value instanceof Enum> enumValue)) {
- return false;
- }
+ private static MethodHandle withIndexCheck(MethodHandle target, int labelsCount) {
+ MethodHandle checkIndex = MethodHandles.insertArguments(CHECK_INDEX, 1, labelsCount + 1);
- Class> clazz = label.constantType().resolveConstantDesc(lookup);
+ return MethodHandles.filterArguments(target, 1, checkIndex);
+ }
- if (enumValue.getDeclaringClass() != clazz) {
- return false;
- }
+ private static final class ResolvedEnumLabels implements BiPredicate {
- resolved = label.resolveConstantDesc(lookup);
- } catch (IllegalArgumentException | ReflectiveOperationException ex) {
- resolved = SENTINEL;
- }
+ private final MethodHandles.Lookup lookup;
+ private final EnumDesc>[] enumDescs;
+ @Stable
+ private Object[] resolvedEnum;
- resolvedEnum.resolvedEnum = resolved;
+ public ResolvedEnumLabels(MethodHandles.Lookup lookup, EnumDesc>[] enumDescs) {
+ this.lookup = lookup;
+ this.enumDescs = enumDescs;
+ this.resolvedEnum = new Object[enumDescs.length];
}
- return value == resolvedEnum.resolvedEnum;
- }
+ @Override
+ public boolean test(Integer labelIndex, Object value) {
+ Object result = resolvedEnum[labelIndex];
- private static MethodHandle withIndexCheck(MethodHandle target, int labelsCount) {
- MethodHandle checkIndex = MethodHandles.insertArguments(CHECK_INDEX, 1, labelsCount + 1);
+ if (result == null) {
+ try {
+ if (!(value instanceof Enum> enumValue)) {
+ return false;
+ }
- return MethodHandles.filterArguments(target, 1, checkIndex);
- }
+ EnumDesc> label = enumDescs[labelIndex];
+ Class> clazz = label.constantType().resolveConstantDesc(lookup);
- private static final class ResolvedEnumLabel {
- @Stable
- public Object resolvedEnum;
+ if (enumValue.getDeclaringClass() != clazz) {
+ return false;
+ }
+
+ result = label.resolveConstantDesc(lookup);
+ } catch (IllegalArgumentException | ReflectiveOperationException ex) {
+ result = SENTINEL;
+ }
+
+ resolvedEnum[labelIndex] = result;
+ }
+
+ return result == value;
+ }
}
private static final class EnumMap {
@Stable
public int[] map;
}
+
+ /*
+ * Construct test chains for labels inside switch, to handle switch repeats:
+ * switch (idx) {
+ * case 0 -> if (selector matches label[0]) return 0;
+ * case 1 -> if (selector matches label[1]) return 1;
+ * ...
+ * }
+ */
+ @SuppressWarnings("removal")
+ private static MethodHandle generateInnerClass(MethodHandles.Lookup caller, Object[] labels) {
+ List> enumDescs = new ArrayList<>();
+ List> extraClassLabels = new ArrayList<>();
+
+ byte[] classBytes = Classfile.of().build(ClassDesc.of(typeSwitchClassName(caller.lookupClass())), clb -> {
+ clb.withFlags(AccessFlag.FINAL, AccessFlag.SUPER, AccessFlag.SYNTHETIC)
+ .withMethodBody("typeSwitch",
+ TYPES_SWITCH_DESCRIPTOR,
+ Classfile.ACC_FINAL | Classfile.ACC_PUBLIC | Classfile.ACC_STATIC,
+ cb -> {
+ cb.aload(0);
+ Label nonNullLabel = cb.newLabel();
+ cb.if_nonnull(nonNullLabel);
+ cb.iconst_m1();
+ cb.ireturn();
+ cb.labelBinding(nonNullLabel);
+ if (labels.length == 0) {
+ cb.constantInstruction(0)
+ .ireturn();
+ return ;
+ }
+ cb.iload(1);
+ Label dflt = cb.newLabel();
+ record Element(Label target, Label next, Object caseLabel) {}
+ List cases = new ArrayList<>();
+ List switchCases = new ArrayList<>();
+ Object lastLabel = null;
+ for (int idx = labels.length - 1; idx >= 0; idx--) {
+ Object currentLabel = labels[idx];
+ Label target = cb.newLabel();
+ Label next;
+ if (lastLabel == null) {
+ next = dflt;
+ } else if (lastLabel.equals(currentLabel)) {
+ next = cases.getLast().next();
+ } else {
+ next = cases.getLast().target();
+ }
+ lastLabel = currentLabel;
+ cases.add(new Element(target, next, currentLabel));
+ switchCases.add(SwitchCase.of(idx, target));
+ }
+ cases = cases.reversed();
+ switchCases = switchCases.reversed();
+ cb.tableswitch(0, labels.length - 1, dflt, switchCases);
+ for (int idx = 0; idx < cases.size(); idx++) {
+ Element element = cases.get(idx);
+ Label next = element.next();
+ cb.labelBinding(element.target());
+ if (element.caseLabel() instanceof Class> classLabel) {
+ Optional classLabelConstableOpt = classLabel.describeConstable();
+ if (classLabelConstableOpt.isPresent()) {
+ cb.aload(0);
+ cb.instanceof_(classLabelConstableOpt.orElseThrow());
+ cb.ifeq(next);
+ } else {
+ cb.aload(3);
+ cb.constantInstruction(extraClassLabels.size());
+ cb.invokeinterface(ConstantDescs.CD_List,
+ "get",
+ MethodTypeDesc.of(ConstantDescs.CD_Object,
+ ConstantDescs.CD_int));
+ cb.checkcast(ConstantDescs.CD_Class);
+ cb.aload(0);
+ cb.invokevirtual(ConstantDescs.CD_Class,
+ "isInstance",
+ MethodTypeDesc.of(ConstantDescs.CD_boolean,
+ ConstantDescs.CD_Object));
+ cb.ifeq(next);
+ extraClassLabels.add(classLabel);
+ }
+ } else if (element.caseLabel() instanceof EnumDesc> enumLabel) {
+ int enumIdx = enumDescs.size();
+ enumDescs.add(enumLabel);
+ cb.aload(2);
+ cb.constantInstruction(enumIdx);
+ cb.invokestatic(ConstantDescs.CD_Integer,
+ "valueOf",
+ MethodTypeDesc.of(ConstantDescs.CD_Integer,
+ ConstantDescs.CD_int));
+ cb.aload(0);
+ cb.invokeinterface(BiPredicate.class.describeConstable().orElseThrow(),
+ "test",
+ MethodTypeDesc.of(ConstantDescs.CD_boolean,
+ ConstantDescs.CD_Object,
+ ConstantDescs.CD_Object));
+ cb.ifeq(next);
+ } else if (element.caseLabel() instanceof String stringLabel) {
+ cb.ldc(stringLabel);
+ cb.aload(0);
+ cb.invokevirtual(ConstantDescs.CD_Object,
+ "equals",
+ MethodTypeDesc.of(ConstantDescs.CD_boolean,
+ ConstantDescs.CD_Object));
+ cb.ifeq(next);
+ } else if (element.caseLabel() instanceof Integer integerLabel) {
+ Label compare = cb.newLabel();
+ Label notNumber = cb.newLabel();
+ cb.aload(0);
+ cb.instanceof_(ConstantDescs.CD_Number);
+ cb.ifeq(notNumber);
+ cb.aload(0);
+ cb.checkcast(ConstantDescs.CD_Number);
+ cb.invokevirtual(ConstantDescs.CD_Number,
+ "intValue",
+ MethodTypeDesc.of(ConstantDescs.CD_int));
+ cb.goto_(compare);
+ cb.labelBinding(notNumber);
+ cb.aload(0);
+ cb.instanceof_(ConstantDescs.CD_Character);
+ cb.ifeq(next);
+ cb.aload(0);
+ cb.checkcast(ConstantDescs.CD_Character);
+ cb.invokevirtual(ConstantDescs.CD_Character,
+ "charValue",
+ MethodTypeDesc.of(ConstantDescs.CD_char));
+ cb.labelBinding(compare);
+ cb.ldc(integerLabel);
+ cb.if_icmpne(next);
+ } else {
+ throw new InternalError("Unsupported label type: " +
+ element.caseLabel().getClass());
+ }
+ cb.constantInstruction(idx);
+ cb.ireturn();
+ }
+ cb.labelBinding(dflt);
+ cb.constantInstruction(cases.size());
+ cb.ireturn();
+ });
+ });
+
+ try {
+ // this class is linked at the indy callsite; so define a hidden nestmate
+ MethodHandles.Lookup lookup;
+ lookup = caller.defineHiddenClass(classBytes, true, NESTMATE, STRONG);
+ MethodHandle typeSwitch = lookup.findStatic(lookup.lookupClass(),
+ "typeSwitch",
+ MethodType.methodType(int.class,
+ Object.class,
+ int.class,
+ BiPredicate.class,
+ List.class));
+ return MethodHandles.insertArguments(typeSwitch, 2, new ResolvedEnumLabels(caller, enumDescs.toArray(EnumDesc[]::new)),
+ List.copyOf(extraClassLabels));
+ } catch (Throwable t) {
+ throw new IllegalArgumentException(t);
+ }
+ }
+
+ //based on src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java:
+ private static String typeSwitchClassName(Class> targetClass) {
+ String name = targetClass.getName();
+ if (targetClass.isHidden()) {
+ // use the original class name
+ name = name.replace('/', '_');
+ }
+ return name + "$$TypeSwitch";
+ }
}
diff --git a/src/java.base/share/classes/java/net/DatagramSocket.java b/src/java.base/share/classes/java/net/DatagramSocket.java
index 26220feac2c1a..9e94d1d8b6209 100644
--- a/src/java.base/share/classes/java/net/DatagramSocket.java
+++ b/src/java.base/share/classes/java/net/DatagramSocket.java
@@ -604,6 +604,12 @@ public SocketAddress getRemoteSocketAddress() {
/**
* Returns the address of the endpoint this socket is bound to.
+ * If the socket was initially bound to the wildcard address and
+ * is now {@link #isConnected connected}, then the address returned
+ * may be the local address selected as the source address for
+ * datagrams sent on this socket instead of the wildcard address.
+ * When {@link #disconnect()} is called, the bound address reverts
+ * to the wildcard address.
*
* @return a {@code SocketAddress} representing the local endpoint of this
* socket, or {@code null} if it is closed or not bound yet.
@@ -714,6 +720,12 @@ public void receive(DatagramPacket p) throws IOException {
/**
* Gets the local address to which the socket is bound.
+ *
If the socket was initially bound to the wildcard address and
+ * is now {@link #isConnected connected}, then the address returned
+ * may be the local address selected as the source address for
+ * datagrams sent on the socket instead of the wildcard address.
+ * When {@link #disconnect()} is called, the bound address reverts
+ * to the wildcard address.
*
*
If there is a security manager, its
* {@code checkConnect} method is first called
diff --git a/src/java.base/share/classes/java/net/URLDecoder.java b/src/java.base/share/classes/java/net/URLDecoder.java
index 84d74a7be305b..e2070458e2b17 100644
--- a/src/java.base/share/classes/java/net/URLDecoder.java
+++ b/src/java.base/share/classes/java/net/URLDecoder.java
@@ -98,6 +98,8 @@ private URLDecoder() {}
* default charset. Instead, use the decode(String,String) method
* to specify the encoding.
* @return the newly decoded {@code String}
+ * @throws IllegalArgumentException if the implementation encounters malformed
+ * escape sequences
*/
@Deprecated
public static String decode(String s) {
@@ -113,9 +115,6 @@ public static String decode(String s) {
* except that it will {@linkplain Charset#forName look up the charset}
* using the given encoding name.
*
- * @implNote This implementation will throw an {@link java.lang.IllegalArgumentException}
- * when illegal strings are encountered.
- *
* @param s the {@code String} to decode
* @param enc The name of a supported
* character
@@ -124,6 +123,8 @@ public static String decode(String s) {
* @throws UnsupportedEncodingException
* If character encoding needs to be consulted, but
* named character encoding is not supported
+ * @throws IllegalArgumentException if the implementation encounters malformed
+ * escape sequences
* @see URLEncoder#encode(java.lang.String, java.lang.String)
* @since 1.4
*/
@@ -144,8 +145,10 @@ public static String decode(String s, String enc) throws UnsupportedEncodingExce
* Decodes an {@code application/x-www-form-urlencoded} string using
* a specific {@linkplain Charset Charset}.
* The supplied charset is used to determine
- * what characters are represented by any consecutive sequences of the
- * form "{@code %xy} ".
+ * what characters are represented by any consecutive escape sequences of
+ * the form "{@code %xy} ". Erroneous bytes are replaced with the
+ * supplied {@code Charset}'s {@linkplain java.nio.charset.CharsetDecoder##cae
+ * replacement value}.
*
* Note: The
@@ -153,15 +156,12 @@ public static String decode(String s, String enc) throws UnsupportedEncodingExce
* UTF-8 should be used. Not doing so may introduce
* incompatibilities.
*
- * @implNote This implementation will throw an {@link java.lang.IllegalArgumentException}
- * when illegal strings are encountered.
- *
* @param s the {@code String} to decode
* @param charset the given charset
* @return the newly decoded {@code String}
* @throws NullPointerException if {@code s} or {@code charset} is {@code null}
- * @throws IllegalArgumentException if the implementation encounters illegal
- * characters
+ * @throws IllegalArgumentException if the implementation encounters malformed
+ * escape sequences
*
* @spec https://www.w3.org/TR/html4 HTML 4.01 Specification
* @see URLEncoder#encode(java.lang.String, Charset)
diff --git a/src/java.base/share/classes/java/net/URLEncoder.java b/src/java.base/share/classes/java/net/URLEncoder.java
index f8fd79e6b5e3f..3c02dd50e6e9a 100644
--- a/src/java.base/share/classes/java/net/URLEncoder.java
+++ b/src/java.base/share/classes/java/net/URLEncoder.java
@@ -200,11 +200,15 @@ public static String encode(String s, String enc)
* This method uses the supplied charset to obtain the bytes for unsafe
* characters.
*
- * Note: The
* World Wide Web Consortium Recommendation states that
- * UTF-8 should be used. Not doing so may introduce incompatibilities.
- *
+ * UTF-8 should be used. Not doing so may introduce incompatibilities.
* @param s {@code String} to be translated.
* @param charset the given charset
* @return the translated {@code String}.
diff --git a/src/java.base/share/classes/java/nio/channels/DatagramChannel.java b/src/java.base/share/classes/java/nio/channels/DatagramChannel.java
index 1d0a838f65d5c..1b0e7c0f35a50 100644
--- a/src/java.base/share/classes/java/nio/channels/DatagramChannel.java
+++ b/src/java.base/share/classes/java/nio/channels/DatagramChannel.java
@@ -645,6 +645,13 @@ public final long write(ByteBuffer[] srcs) throws IOException {
/**
* {@inheritDoc}
*
+ * If the channel's socket was initially bound to the wildcard address and
+ * is now {@link #isConnected connected}, then the address returned
+ * may be the local address selected as the source address for
+ * datagrams sent via this channel instead of the wildcard address.
+ * When {@link #disconnect} is called, the bound address reverts
+ * to the wildcard address.
+ *
* If there is a security manager set, its {@code checkConnect} method is
* called with the local address and {@code -1} as its arguments to see
* if the operation is allowed. If the operation is not allowed,
diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java
index 926b773827ca8..e446df8fd3e0c 100644
--- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java
+++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java
@@ -55,20 +55,6 @@
public class AtomicLong extends Number implements java.io.Serializable {
private static final long serialVersionUID = 1927816293512124184L;
- /**
- * Records whether the underlying JVM supports lockless
- * compareAndSet for longs. While the intrinsic compareAndSetLong
- * method works in either case, some constructions should be
- * handled at Java level to avoid locking user-visible locks.
- */
- static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
-
- /**
- * Returns whether underlying JVM supports lockless CompareAndSet
- * for longs. Called only once and cached in VM_SUPPORTS_LONG_CAS.
- */
- private static native boolean VMSupportsCS8();
-
/*
* This class intended to be implemented using VarHandles, but there
* are unresolved cyclic startup dependencies.
diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
index ae5440ef5f2a0..0e496dbd6a55d 100644
--- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
+++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
@@ -90,10 +90,7 @@ public abstract class AtomicLongFieldUpdater {
public static AtomicLongFieldUpdater newUpdater(Class tclass,
String fieldName) {
Class> caller = Reflection.getCallerClass();
- if (AtomicLong.VM_SUPPORTS_LONG_CAS)
- return new CASUpdater(tclass, fieldName, caller);
- else
- return new LockedUpdater(tclass, fieldName, caller);
+ return new CASUpdater(tclass, fieldName, caller);
}
/**
@@ -515,126 +512,6 @@ public final long addAndGet(T obj, long delta) {
}
}
- private static final class LockedUpdater extends AtomicLongFieldUpdater {
- private static final Unsafe U = Unsafe.getUnsafe();
- private final long offset;
- /**
- * if field is protected, the subclass constructing updater, else
- * the same as tclass
- */
- private final Class> cclass;
- /** class holding the field */
- private final Class tclass;
-
- @SuppressWarnings("removal")
- LockedUpdater(final Class tclass, final String fieldName,
- final Class> caller) {
- final Field field;
- final int modifiers;
- try {
- field = AccessController.doPrivileged(
- new PrivilegedExceptionAction() {
- public Field run() throws NoSuchFieldException {
- return tclass.getDeclaredField(fieldName);
- }
- });
- modifiers = field.getModifiers();
- sun.reflect.misc.ReflectUtil.ensureMemberAccess(
- caller, tclass, null, modifiers);
- ClassLoader cl = tclass.getClassLoader();
- ClassLoader ccl = caller.getClassLoader();
- if ((ccl != null) && (ccl != cl) &&
- ((cl == null) || !isAncestor(cl, ccl))) {
- sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
- }
- } catch (PrivilegedActionException pae) {
- throw new RuntimeException(pae.getException());
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
-
- if (field.getType() != long.class)
- throw new IllegalArgumentException("Must be long type");
-
- if (!Modifier.isVolatile(modifiers))
- throw new IllegalArgumentException("Must be volatile type");
-
- // Access to protected field members is restricted to receivers only
- // of the accessing class, or one of its subclasses, and the
- // accessing class must in turn be a subclass (or package sibling)
- // of the protected member's defining class.
- // If the updater refers to a protected field of a declaring class
- // outside the current package, the receiver argument will be
- // narrowed to the type of the accessing class.
- this.cclass = (Modifier.isProtected(modifiers) &&
- tclass.isAssignableFrom(caller) &&
- !isSamePackage(tclass, caller))
- ? caller : tclass;
- this.tclass = tclass;
- this.offset = U.objectFieldOffset(field);
- }
-
- /**
- * Checks that target argument is instance of cclass. On
- * failure, throws cause.
- */
- private final void accessCheck(T obj) {
- if (!cclass.isInstance(obj))
- throw accessCheckException(obj);
- }
-
- /**
- * Returns access exception if accessCheck failed due to
- * protected access, else ClassCastException.
- */
- private final RuntimeException accessCheckException(T obj) {
- if (cclass == tclass)
- return new ClassCastException();
- else
- return new RuntimeException(
- new IllegalAccessException(
- "Class " +
- cclass.getName() +
- " can not access a protected member of class " +
- tclass.getName() +
- " using an instance of " +
- obj.getClass().getName()));
- }
-
- public final boolean compareAndSet(T obj, long expect, long update) {
- accessCheck(obj);
- synchronized (this) {
- long v = U.getLong(obj, offset);
- if (v != expect)
- return false;
- U.putLong(obj, offset, update);
- return true;
- }
- }
-
- public final boolean weakCompareAndSet(T obj, long expect, long update) {
- return compareAndSet(obj, expect, update);
- }
-
- public final void set(T obj, long newValue) {
- accessCheck(obj);
- synchronized (this) {
- U.putLong(obj, offset, newValue);
- }
- }
-
- public final void lazySet(T obj, long newValue) {
- set(obj, newValue);
- }
-
- public final long get(T obj) {
- accessCheck(obj);
- synchronized (this) {
- return U.getLong(obj, offset);
- }
- }
- }
-
/**
* Returns true if the second classloader can be found in the first
* classloader's delegation chain.
diff --git a/src/java.base/share/classes/java/util/zip/ZipInputStream.java b/src/java.base/share/classes/java/util/zip/ZipInputStream.java
index 9e265fd668edc..e583bc289347f 100644
--- a/src/java.base/share/classes/java/util/zip/ZipInputStream.java
+++ b/src/java.base/share/classes/java/util/zip/ZipInputStream.java
@@ -51,12 +51,12 @@
* {@code ZipInputStream} read methods such
* as {@link #read(byte[], int, int) read} or {@link #readAllBytes() readAllBytes()}.
* For example:
- * {@snippet :
+ * {@snippet lang="java" :
* Path jar = Path.of("foo.jar");
* try (InputStream is = Files.newInputStream(jar);
* ZipInputStream zis = new ZipInputStream(is)) {
* ZipEntry ze;
- * while((ze= zis.getNextEntry()) != null) {
+ * while ((ze = zis.getNextEntry()) != null) {
* var bytes = zis.readAllBytes();
* System.out.printf("Entry: %s, bytes read: %s%n", ze.getName(),
* bytes.length);
diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java
index 27e68b9ecf9b4..3ae4b25923255 100644
--- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java
+++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java
@@ -1394,7 +1394,7 @@ Type toArray() {
}
Type getComponent() {
- if (sym.isArray()) {
+ if (isArray()) {
var comp = sym.componentType();
if (comp.isPrimitive()) {
return switch (comp.descriptorString().charAt(0)) {
diff --git a/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java b/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java
index 7ac94ef6b93e2..aef62be815160 100644
--- a/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java
+++ b/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java
@@ -55,8 +55,7 @@ public abstract sealed class MemorySessionImpl
implements Scope
permits ConfinedSession, GlobalSession, SharedSession {
static final int OPEN = 0;
- static final int CLOSING = -1;
- static final int CLOSED = -2;
+ static final int CLOSED = -1;
static final VarHandle STATE;
static final int MAX_FORKS = Integer.MAX_VALUE;
diff --git a/src/java.base/share/classes/jdk/internal/foreign/SharedSession.java b/src/java.base/share/classes/jdk/internal/foreign/SharedSession.java
index 8d54a1e2d8af4..1569589ef9b81 100644
--- a/src/java.base/share/classes/jdk/internal/foreign/SharedSession.java
+++ b/src/java.base/share/classes/jdk/internal/foreign/SharedSession.java
@@ -77,17 +77,13 @@ public void release0() {
}
void justClose() {
- int prevState = (int) STATE.compareAndExchange(this, OPEN, CLOSING);
+ int prevState = (int) STATE.compareAndExchange(this, OPEN, CLOSED);
if (prevState < 0) {
throw alreadyClosed();
} else if (prevState != OPEN) {
throw alreadyAcquired(prevState);
}
- boolean success = SCOPED_MEMORY_ACCESS.closeScope(this);
- STATE.setVolatile(this, success ? CLOSED : OPEN);
- if (!success) {
- throw alreadyAcquired(1);
- }
+ SCOPED_MEMORY_ACCESS.closeScope(this, ALREADY_CLOSED);
}
/**
diff --git a/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template b/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template
index 2c7f52352aaee..0147b3bacd86e 100644
--- a/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template
+++ b/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template
@@ -83,11 +83,11 @@ public class ScopedMemoryAccess {
registerNatives();
}
- public boolean closeScope(MemorySessionImpl session) {
- return closeScope0(session);
+ public void closeScope(MemorySessionImpl session, ScopedAccessError error) {
+ closeScope0(session, error);
}
- native boolean closeScope0(MemorySessionImpl session);
+ native void closeScope0(MemorySessionImpl session, ScopedAccessError error);
private ScopedMemoryAccess() {}
diff --git a/src/java.base/share/classes/sun/net/www/MessageHeader.java b/src/java.base/share/classes/sun/net/www/MessageHeader.java
index 5542193b9a990..6c6d18453ca7c 100644
--- a/src/java.base/share/classes/sun/net/www/MessageHeader.java
+++ b/src/java.base/share/classes/sun/net/www/MessageHeader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -554,7 +554,7 @@ public void mergeHeader(InputStream is) throws java.io.IOException {
}
public synchronized String toString() {
- String result = super.toString() + nkeys + " pairs: ";
+ String result = super.toString() + " " + nkeys + " pairs: ";
for (int i = 0; i < keys.length && i < nkeys; i++) {
result += "{"+keys[i]+": "+values[i]+"}";
}
diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java b/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java
index 1021503318ec6..7881a1480c990 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java
@@ -152,7 +152,7 @@ private static HashMap getCache() {
@Override
protected boolean useAuthCache() {
- return super.useAuthCache() && cacheSPNEGO;
+ return false;
}
/**
diff --git a/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java b/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java
index 943af4d2333aa..4b615ca9af111 100644
--- a/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java
+++ b/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java
@@ -23,7 +23,6 @@
* questions.
*/
-
package sun.security.pkcs10;
import java.io.PrintStream;
@@ -174,7 +173,7 @@ public PKCS10(byte[] data)
throw new SignatureException("Invalid PKCS #10 signature");
}
} catch (InvalidKeyException e) {
- throw new SignatureException("Invalid key");
+ throw new SignatureException("Invalid key", e);
} catch (InvalidAlgorithmParameterException e) {
throw new SignatureException("Invalid signature parameters", e);
} catch (ProviderException e) {
diff --git a/src/java.base/share/native/libfallbackLinker/fallbackLinker.c b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c
index 5c7531dcd75ba..39d869adb639e 100644
--- a/src/java.base/share/native/libfallbackLinker/fallbackLinker.c
+++ b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c
@@ -28,8 +28,8 @@
#include
#include
-#include
#include
+#include
#include
#ifdef _WIN64
#include
diff --git a/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java b/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java
index fd905da80a51c..ee041e215a32a 100644
--- a/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java
+++ b/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java
@@ -74,7 +74,8 @@ public enum SourceVersion {
* 21: pattern matching for switch and record patterns (string
* templates in preview, unnamed patterns and variables in
* preview, unnamed classes and instance main methods in preview)
- * 22: tbd
+ * 22: Unnamed Variables & Patterns (Statements before super(...)
+ * in Preview)
*/
/**
@@ -392,9 +393,9 @@ public enum SourceVersion {
* href="https://docs.oracle.com/javase/specs/jls/se21/html/index.html">
* The Java Language Specification, Java SE 21 Edition
* @see
- * Record Patterns
+ * JEP 440: Record Patterns
* @see
- * Pattern Matching for switch
+ * JEP 441: Pattern Matching for switch
*/
RELEASE_21,
@@ -402,11 +403,16 @@ public enum SourceVersion {
* The version introduced by the Java Platform, Standard Edition
* 22.
*
+ * Additions in this release include unnamed variables and unnamed
+ * patterns.
+ *
* @since 22
*
* @see
* The Java Language Specification, Java SE 22 Edition
+ * @see
+ * JEP 456: Unnamed Variables & Patterns
*/
RELEASE_22,
; // Reduce code churn when appending new constants
diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthProgressBarUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthProgressBarUI.java
index 0adf45df1ff0a..ee50976bbf4f9 100644
--- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthProgressBarUI.java
+++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthProgressBarUI.java
@@ -215,9 +215,12 @@ public void update(Graphics g, JComponent c) {
SynthContext context = getContext(c);
SynthLookAndFeel.update(context, g);
- context.getPainter().paintProgressBarBackground(context,
- g, 0, 0, c.getWidth(), c.getHeight(),
- progressBar.getOrientation());
+
+ if (((JProgressBar) c).isBorderPainted()) {
+ context.getPainter().paintProgressBarBackground(context,
+ g, 0, 0, c.getWidth(), c.getHeight(),
+ progressBar.getOrientation());
+ }
paint(context, g);
}
diff --git a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java
index bb60b38265465..15247608e1b4b 100644
--- a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java
+++ b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java
@@ -2965,6 +2965,17 @@ ImageIcon getImage(URL base) {
}
return image;
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(svalue);
+ }
+
+ @Override
+ public boolean equals(Object val) {
+ return val instanceof CSS.BackgroundImage img
+ && Objects.equals(svalue, img.svalue);
+ }
}
/**
diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java b/src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java
index 91526938bff0a..d6d03a9aa8af9 100644
--- a/src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java
@@ -101,7 +101,7 @@ class MultiExchange implements Cancelable {
);
private final List filters;
- ResponseTimerEvent responseTimerEvent;
+ volatile ResponseTimerEvent responseTimerEvent;
volatile boolean cancelled;
AtomicReference interrupted = new AtomicReference<>();
final PushGroup pushGroup;
@@ -231,6 +231,7 @@ public Optional remainingConnectTimeout() {
private void cancelTimer() {
if (responseTimerEvent != null) {
client.cancelTimer(responseTimerEvent);
+ responseTimerEvent = null;
}
}
@@ -457,6 +458,7 @@ private CompletableFuture responseAsyncImpl() {
}
return completedFuture(response);
} else {
+ cancelTimer();
this.response =
new HttpResponseImpl<>(currentreq, response, this.response, null, exch);
Exchange oldExch = exch;
diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/PropertyManager.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/PropertyManager.java
index 92b81685a94e2..da21fb4a511b6 100644
--- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/PropertyManager.java
+++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/PropertyManager.java
@@ -46,7 +46,7 @@
* @author K Venugopal
* @author Sunitha Reddy
*
- * @LastModified: July 2023
+ * @LastModified: Nov 2023
*/
public class PropertyManager {
@@ -150,10 +150,7 @@ private void initConfigurableReaderProperties() {
// Initialize Catalog features
supportedProps.put(XMLConstants.USE_CATALOG, JdkXmlUtils.USE_CATALOG_DEFAULT);
- for (CatalogFeatures.Feature f : CatalogFeatures.Feature.values()) {
- supportedProps.put(f.getPropertyName(), null);
- }
-
+ JdkXmlUtils.initCatalogFeatures(supportedProps);
supportedProps.put(JdkConstants.CDATA_CHUNK_SIZE, JdkConstants.CDATA_CHUNK_SIZE_DEFAULT);
}
diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java
index 8f55114af337a..b77108b1e0eba 100644
--- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java
+++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java
@@ -74,7 +74,7 @@
* @author Eric Ye, IBM
* @author Sunitha Reddy, SUN Microsystems
*
- * @LastModified: July 2023
+ * @LastModified: Nov 2023
*/
public class XMLDocumentFragmentScannerImpl
extends XMLScanner
@@ -343,6 +343,13 @@ public class XMLDocumentFragmentScannerImpl
*/
protected String fAccessExternalDTD = EXTERNAL_ACCESS_DEFAULT;
+ /**
+ * Properties to determine whether to use a user-specified Catalog:
+ * Feature USE_CATALOG, Resolve and Catalog File
+ */
+ protected boolean fUseCatalog = true;
+ protected String fCatalogFile;
+
/**
* standard uri conformant (strict uri).
* http://apache.org/xml/features/standard-uri-conformant
diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentScannerImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentScannerImpl.java
index f859a499d29b7..52bfcd7aeec90 100644
--- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentScannerImpl.java
+++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentScannerImpl.java
@@ -43,9 +43,11 @@
import java.io.CharConversionException;
import java.io.EOFException;
import java.io.IOException;
+import javax.xml.XMLConstants;
import javax.xml.stream.events.XMLEvent;
import jdk.xml.internal.JdkConstants;
import jdk.xml.internal.JdkProperty.State;
+import jdk.xml.internal.JdkXmlUtils;
import jdk.xml.internal.SecuritySupport;
import jdk.xml.internal.XMLSecurityManager.Limit;
@@ -69,7 +71,7 @@
* Refer to the table in unit-test javax.xml.stream.XMLStreamReaderTest.SupportDTD for changes
* related to property SupportDTD.
* @author Joe Wang, Sun Microsystems
- * @LastModified: July 2023
+ * @LastModified: Nov 2023
*/
public class XMLDocumentScannerImpl
extends XMLDocumentFragmentScannerImpl{
@@ -281,6 +283,9 @@ public void reset(PropertyManager propertyManager) {
fLoadExternalDTD = !((Boolean)propertyManager.getProperty(
Constants.ZEPHYR_PROPERTY_PREFIX + Constants.IGNORE_EXTERNAL_DTD));
+ fUseCatalog = (Boolean)propertyManager.getProperty(XMLConstants.USE_CATALOG);
+ fCatalogFile = (String)propertyManager.getProperty(JdkXmlUtils.CATALOG_FILES);
+
setScannerState(XMLEvent.START_DOCUMENT);
setDriver(fXMLDeclDriver);
fSeenInternalSubset = false;
@@ -327,6 +332,8 @@ public void reset(XMLComponentManager componentManager)
// xerces features
fLoadExternalDTD = componentManager.getFeature(LOAD_EXTERNAL_DTD, true);
+ fUseCatalog = componentManager.getFeature(XMLConstants.USE_CATALOG, true);
+ fCatalogFile = (String)componentManager.getProperty(JdkXmlUtils.CATALOG_FILES);
fNamespaces = componentManager.getFeature(NAMESPACES, true);
fSeenInternalSubset = false;
diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java
index d127f7c8ec1bb..22de24ea8f058 100644
--- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java
+++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java
@@ -56,6 +56,7 @@
import javax.xml.catalog.CatalogResolver;
import javax.xml.stream.XMLInputFactory;
import javax.xml.transform.Source;
+import jdk.xml.internal.JdkCatalog;
import jdk.xml.internal.JdkConstants;
import jdk.xml.internal.JdkProperty;
import jdk.xml.internal.JdkXmlUtils;
@@ -93,7 +94,7 @@
* @author K.Venugopal SUN Microsystems
* @author Neeraj Bajaj SUN Microsystems
* @author Sunitha Reddy SUN Microsystems
- * @LastModified: July 2023
+ * @LastModified: Nov 2023
*/
public class XMLEntityManager implements XMLComponent, XMLEntityResolver {
@@ -265,9 +266,6 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver {
/** Debug switching readers for encodings. */
private static final boolean DEBUG_ENCODINGS = false;
- // should be diplayed trace resolving messages
- private static final boolean DEBUG_RESOLVER = false ;
-
//
// Data
//
@@ -355,6 +353,7 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver {
/** Security Manager */
protected XMLSecurityManager fSecurityManager = null;
+ XMLSecurityPropertyManager fSecurityPropertyMgr;
protected XMLLimitAnalyzer fLimitAnalyzer = null;
@@ -418,8 +417,11 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver {
/** indicate whether Catalog should be used for resolving external resources */
private boolean fUseCatalog = true;
+ // user-specified Catalog Resolver
CatalogFeatures fCatalogFeatures;
CatalogResolver fCatalogResolver;
+ // the default JDK Catalog Resolver
+ CatalogResolver fDefCR;
private String fCatalogFile;
private String fDefer;
@@ -434,11 +436,16 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver {
* If this constructor is used to create the object, reset() should be invoked on this object
*/
public XMLEntityManager() {
+ this(null, new XMLSecurityManager(true));
+ }
+
+ public XMLEntityManager(XMLSecurityPropertyManager securityPropertyMgr, XMLSecurityManager securityManager) {
//for entity managers not created by parsers
- fSecurityManager = new XMLSecurityManager(true);
+ fSecurityManager = securityManager;
+ fSecurityPropertyMgr = securityPropertyMgr;
fEntityStorage = new XMLEntityStorage(this) ;
setScannerVersion(Constants.XML_VERSION_1_0);
- } // ()
+ }
/** Default constructor. */
public XMLEntityManager(PropertyManager propertyManager) {
@@ -653,7 +660,11 @@ public String setupCurrentEntity(boolean reference, String name, XMLInputSource
URL location = new URL(expandedSystemId);
URLConnection connect = location.openConnection();
if (!(connect instanceof HttpURLConnection)) {
- stream = connect.getInputStream();
+ if (expandedSystemId.startsWith("jrt:/java.xml")) {
+ stream = SecuritySupport.getInputStream(connect);
+ } else {
+ stream = connect.getInputStream();
+ }
}
else {
boolean followRedirects = true;
@@ -1012,73 +1023,126 @@ public StaxXMLInputSource resolveEntityAsPerStax(XMLResourceIdentifier resourceI
ri = fResourceIdentifier;
}
ri.setValues(publicId, literalSystemId, baseSystemId, expandedSystemId);
- if(DEBUG_RESOLVER){
- System.out.println("BEFORE Calling resolveEntity") ;
- }
fISCreatedByResolver = false;
- //either of Stax or Xerces would be null
- if(fStaxEntityResolver != null){
+ // Step 1: custom resolver, either StAX or Entity
+ if (fStaxEntityResolver != null) {
staxInputSource = fStaxEntityResolver.resolveEntity(ri);
- if(staxInputSource != null) {
+ } else if (fEntityResolver != null) {
+ xmlInputSource = fEntityResolver.resolveEntity(ri);
+ if (xmlInputSource != null) {
+ //wrap it in StaxXMLInputSource
fISCreatedByResolver = true;
+ staxInputSource = new StaxXMLInputSource(xmlInputSource, fISCreatedByResolver);
}
}
- if(fEntityResolver != null){
- xmlInputSource = fEntityResolver.resolveEntity(ri);
- if(xmlInputSource != null) {
- fISCreatedByResolver = true;
+ // Step 2: custom catalog if specified
+ if (staxInputSource == null && (fUseCatalog && fCatalogFile != null)) {
+ if (fCatalogResolver == null) {
+ fCatalogFeatures = JdkXmlUtils.getCatalogFeatures(fDefer, fCatalogFile, fPrefer, fResolve);
+ fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures);
}
- }
- if(xmlInputSource != null){
- //wrap this XMLInputSource to StaxInputSource
- staxInputSource = new StaxXMLInputSource(xmlInputSource, fISCreatedByResolver);
+ staxInputSource = resolveWithCatalogStAX(fCatalogResolver, fCatalogFile, publicId, literalSystemId);
}
- if (staxInputSource == null && fUseCatalog) {
- if (fCatalogFeatures == null) {
- fCatalogFeatures = JdkXmlUtils.getCatalogFeatures(fDefer, fCatalogFile, fPrefer, fResolve);
- }
- fCatalogFile = fCatalogFeatures.get(Feature.FILES);
- if (fCatalogFile != null) {
- try {
- if (fCatalogResolver == null) {
- fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures);
- }
- InputSource is = fCatalogResolver.resolveEntity(publicId, literalSystemId);
- if (is != null && !is.isEmpty()) {
- staxInputSource = new StaxXMLInputSource(new XMLInputSource(is, true), true);
- }
- } catch (CatalogException e) {
- fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,"CatalogException",
- new Object[]{SecuritySupport.sanitizePath(fCatalogFile)},
- XMLErrorReporter.SEVERITY_FATAL_ERROR, e );
- }
- }
+ // Step 3: use the default JDK Catalog Resolver if Step 2's resolve is continue
+ if (staxInputSource == null && JdkXmlUtils.isResolveContinue(fCatalogFeatures)) {
+ initJdkCatalogResolver();
+
+ staxInputSource = resolveWithCatalogStAX(fDefCR, JdkCatalog.JDKCATALOG, publicId, literalSystemId);
}
- // do default resolution
- //this works for both stax & Xerces, if staxInputSource is null,
- //it means parser need to revert to default resolution
- if (staxInputSource == null) {
- // REVISIT: when systemId is null, I think we should return null.
- // is this the right solution? -SG
- //if (systemId != null)
+ // Step 4: default resolution if not resolved by a resolver and the RESOLVE
+ // feature is set to 'continue'
+ if (staxInputSource != null) {
+ fISCreatedByResolver = true;
+ } else if (JdkXmlUtils.isResolveContinue(fCatalogFeatures) &&
+ fSecurityManager.is(Limit.JDKCATALOG_RESOLVE, JdkConstants.CONTINUE)) {
staxInputSource = new StaxXMLInputSource(
new XMLInputSource(publicId, literalSystemId, baseSystemId, true), false);
- }else if(staxInputSource.hasXMLStreamOrXMLEventReader()){
- //Waiting for the clarification from EG. - nb
}
- if (DEBUG_RESOLVER) {
- System.err.println("XMLEntityManager.resolveEntity(" + publicId + ")");
- System.err.println(" = " + xmlInputSource);
+ return staxInputSource;
+
+ }
+
+ private void initJdkCatalogResolver() {
+ if (fDefCR == null) {
+ fDefCR = fSecurityManager.getJDKCatalogResolver();
}
+ }
- return staxInputSource;
+ /**
+ * Resolves the external resource using the Catalog specified and returns
+ * a StaxXMLInputSource.
+ */
+ private StaxXMLInputSource resolveWithCatalogStAX(CatalogResolver cr, String cFile,
+ String publicId, String systemId) {
+ InputSource is = resolveWithCatalog(cr, cFile, publicId, systemId);
+ // note that empty source isn't considered resolved
+ if (is != null) {
+ return new StaxXMLInputSource(new XMLInputSource(is, true), true);
+ }
+ return null;
+ }
+ /**
+ * Resolves the external resource using the Catalog specified and returns
+ * a InputSource.
+ */
+ private InputSource resolveWithCatalog(CatalogResolver cr, String cFile,
+ String publicId, String systemId) {
+ if (cr != null) {
+ try {
+ return cr.resolveEntity(publicId, systemId);
+ } catch (CatalogException e) {
+ fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,"CatalogException",
+ new Object[]{SecuritySupport.sanitizePath(cFile)},
+ XMLErrorReporter.SEVERITY_FATAL_ERROR, e );
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Resolves the external resource using the Catalog specified and returns
+ * a XMLInputSource. Since the Resolve method can be called from various processors,
+ * this method attempts to resolve the resource as an EntityResolver first
+ * and then URIResolver if no match is found.
+ */
+ private XMLInputSource resolveEntityOrURI(CatalogResolver cr, String publicId, String systemId, String base) {
+ XMLInputSource xis = resolveEntity(cr, publicId, systemId, base);
+
+ if (xis != null) {
+ return xis;
+ } else if (systemId != null) {
+ Source source = null;
+ try {
+ source = cr.resolve(systemId, base);
+ } catch (CatalogException e) {
+ throw new XNIException(e);
+ }
+ if (source != null && !source.isEmpty()) {
+ return new XMLInputSource(publicId, source.getSystemId(), base, true);
+ }
+ }
+ return null;
+ }
+
+ private XMLInputSource resolveEntity(CatalogResolver cr, String publicId, String systemId, String base) {
+ InputSource is = null;
+ try {
+ if (publicId != null || systemId != null) {
+ is = cr.resolveEntity(publicId, systemId);
+ }
+ } catch (CatalogException e) {}
+
+ if (is != null && !is.isEmpty()) {
+ return new XMLInputSource(is, true);
+ }
+ return null;
}
/**
@@ -1128,7 +1192,7 @@ public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier) th
if (needExpand)
expandedSystemId = expandSystemId(literalSystemId, baseSystemId,false);
- // give the entity resolver a chance
+ // Step 1: custom Entity resolver
XMLInputSource xmlInputSource = null;
if (fEntityResolver != null) {
@@ -1137,63 +1201,30 @@ public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier) th
xmlInputSource = fEntityResolver.resolveEntity(resourceIdentifier);
}
- if (xmlInputSource == null && fUseCatalog) {
- if (fCatalogFeatures == null) {
+ // Step 2: custom catalog if specified
+ if ((publicId != null || literalSystemId != null || resourceIdentifier.getNamespace() !=null)
+ && xmlInputSource == null && (fUseCatalog && fCatalogFile != null)) {
+ if (fCatalogResolver == null) {
fCatalogFeatures = JdkXmlUtils.getCatalogFeatures(fDefer, fCatalogFile, fPrefer, fResolve);
+ fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures);
}
- fCatalogFile = fCatalogFeatures.get(Feature.FILES);
- if (fCatalogFile != null) {
- /*
- since the method can be called from various processors, both
- EntityResolver and URIResolver are used to attempt to find
- a match
- */
- InputSource is = null;
- try {
- if (fCatalogResolver == null) {
- fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures);
- }
- String pid = (publicId != null? publicId : resourceIdentifier.getNamespace());
- if (pid != null || literalSystemId != null) {
- is = fCatalogResolver.resolveEntity(pid, literalSystemId);
- }
- } catch (CatalogException e) {}
-
- if (is != null && !is.isEmpty()) {
- xmlInputSource = new XMLInputSource(is, true);
- } else if (literalSystemId != null) {
- if (fCatalogResolver == null) {
- fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures);
- }
-
- Source source = null;
- try {
- source = fCatalogResolver.resolve(literalSystemId, baseSystemId);
- } catch (CatalogException e) {
- throw new XNIException(e);
- }
- if (source != null && !source.isEmpty()) {
- xmlInputSource = new XMLInputSource(publicId, source.getSystemId(), baseSystemId, true);
- }
- }
- }
+ String pid = (publicId != null? publicId : resourceIdentifier.getNamespace());
+ xmlInputSource = resolveEntityOrURI(fCatalogResolver, pid, literalSystemId, baseSystemId);
}
- // do default resolution
- // REVISIT: what's the correct behavior if the user provided an entity
- // resolver (fEntityResolver != null), but resolveEntity doesn't return
- // an input source (xmlInputSource == null)?
- // do we do default resolution, or do we just return null? -SG
- if (xmlInputSource == null) {
- // REVISIT: when systemId is null, I think we should return null.
- // is this the right solution? -SG
- //if (systemId != null)
- xmlInputSource = new XMLInputSource(publicId, literalSystemId, baseSystemId, false);
+ // Step 3: use the default JDK Catalog Resolver if Step 2's resolve is continue
+ if ((publicId != null || literalSystemId != null)
+ && xmlInputSource == null && JdkXmlUtils.isResolveContinue(fCatalogFeatures)) {
+ initJdkCatalogResolver();
+ // unlike a custom catalog, the JDK Catalog only contains entity references
+ xmlInputSource = resolveEntity(fDefCR, publicId, literalSystemId, baseSystemId);
}
- if (DEBUG_RESOLVER) {
- System.err.println("XMLEntityManager.resolveEntity(" + publicId + ")");
- System.err.println(" = " + xmlInputSource);
+ // Step 4: default resolution if not resolved by a resolver and the RESOLVE
+ // feature is set to 'continue'
+ if ((xmlInputSource == null) && JdkXmlUtils.isResolveContinue(fCatalogFeatures) &&
+ fSecurityManager.is(Limit.JDKCATALOG_RESOLVE, JdkConstants.CONTINUE)) {
+ xmlInputSource = new XMLInputSource(publicId, literalSystemId, baseSystemId, false);
}
return xmlInputSource;
@@ -1411,7 +1442,7 @@ public void startEntity(boolean isGE, String name,
fSecurityManager.debugPrint(fLimitAnalyzer);
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,"EntityExpansionLimit",
new Object[]{fSecurityManager.getLimitValueByIndex(entityExpansionIndex)},
- XMLErrorReporter.SEVERITY_FATAL_ERROR );
+ XMLErrorReporter.SEVERITY_FATAL_ERROR );
// is there anything better to do than reset the counter?
// at least one can envision debugging applications where this might
// be useful...
diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/dv/SchemaDVFactory.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/dv/SchemaDVFactory.java
index f81b7cf41ee33..85d4ac0a50c9e 100644
--- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/dv/SchemaDVFactory.java
+++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/dv/SchemaDVFactory.java
@@ -53,7 +53,7 @@ public abstract class SchemaDVFactory {
* @exception DVFactoryException cannot create an instance of the specified
* class name or the default class name
*/
- public static synchronized final SchemaDVFactory getInstance() throws DVFactoryException {
+ public static final SchemaDVFactory getInstance() throws DVFactoryException {
return getInstance(DEFAULT_FACTORY_CLASS);
} //getInstance(): SchemaDVFactory
@@ -66,7 +66,7 @@ public static synchronized final SchemaDVFactory getInstance() throws DVFactoryE
* @exception DVFactoryException cannot create an instance of the specified
* class name or the default class name
*/
- public static synchronized final SchemaDVFactory getInstance(String factoryClass) throws DVFactoryException {
+ public static final SchemaDVFactory getInstance(String factoryClass) throws DVFactoryException {
try {
// if the class name is not specified, use the default one
@@ -78,7 +78,7 @@ public static synchronized final SchemaDVFactory getInstance(String factoryClass
}
// can't create a new object of this class
- protected SchemaDVFactory(){}
+ protected SchemaDVFactory() {}
/**
* Get a built-in simple type of the given name
diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/SAXParserImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/SAXParserImpl.java
index c0ae87c08a64d..e5dd8a3c25e4c 100644
--- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/SAXParserImpl.java
+++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/SAXParserImpl.java
@@ -63,7 +63,7 @@
* @author Rajiv Mordani
* @author Edwin Goei
*
- * @LastModified: July 2023
+ * @LastModified: Nov 2023
*/
@SuppressWarnings("deprecation")
public class SAXParserImpl extends javax.xml.parsers.SAXParser
@@ -402,7 +402,7 @@ public JAXPSAXParser() {
JAXPSAXParser(SAXParserImpl saxParser, XMLSecurityPropertyManager securityPropertyMgr,
XMLSecurityManager securityManager) {
- super();
+ super(null, null, securityPropertyMgr, securityManager);
fSAXParser = saxParser;
fSecurityManager = securityManager;
fSecurityPropertyMgr = securityPropertyMgr;
diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java
index bb1eb23284d81..f36d2bd14bbbd 100644
--- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java
+++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java
@@ -162,9 +162,6 @@ public XMLSchemaFactory() {
// use catalog
fXMLSchemaLoader.setFeature(XMLConstants.USE_CATALOG, JdkXmlUtils.USE_CATALOG_DEFAULT);
- for (Feature f : Feature.values()) {
- fXMLSchemaLoader.setProperty(f.getPropertyName(), null);
- }
fXMLSchemaLoader.setProperty(JdkConstants.CDATA_CHUNK_SIZE, JdkConstants.CDATA_CHUNK_SIZE_DEFAULT);
fXmlFeatures = new JdkXmlFeatures(fSecurityManager.isSecureProcessing());
diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/SAXParser.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/SAXParser.java
index e8fd5424265d0..a71c8cc56d12d 100644
--- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/SAXParser.java
+++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/SAXParser.java
@@ -41,7 +41,7 @@
* @author Arnaud Le Hors, IBM
* @author Andy Clark, IBM
*
- * @LastModified: Sep 2023
+ * @LastModified: Nov 2023
*/
public class SAXParser
extends AbstractSAXParser {
@@ -91,21 +91,21 @@ public class SAXParser
*/
public SAXParser(XMLParserConfiguration config) {
super(config);
- initSecurityManager();
+ initSecurityManager(null, null);
} // (XMLParserConfiguration)
/**
* Constructs a SAX parser using the dtd/xml schema parser configuration.
*/
public SAXParser() {
- this(null, null);
+ this(null, null, null, null);
} // ()
/**
* Constructs a SAX parser using the specified symbol table.
*/
public SAXParser(SymbolTable symbolTable) {
- this(symbolTable, null);
+ this(symbolTable, null, null, null);
} // (SymbolTable)
/**
@@ -113,6 +113,11 @@ public SAXParser(SymbolTable symbolTable) {
* grammar pool.
*/
public SAXParser(SymbolTable symbolTable, XMLGrammarPool grammarPool) {
+ this(symbolTable, grammarPool, null, null);
+ }
+
+ public SAXParser(SymbolTable symbolTable, XMLGrammarPool grammarPool,
+ XMLSecurityPropertyManager securityPropertyMgr, XMLSecurityManager securityManager) {
super(new XIncludeAwareParserConfiguration());
// set features
@@ -128,7 +133,7 @@ public SAXParser(SymbolTable symbolTable, XMLGrammarPool grammarPool) {
fConfiguration.setProperty(XMLGRAMMAR_POOL, grammarPool);
}
- initSecurityManager();
+ initSecurityManager(securityPropertyMgr, securityManager);
} // (SymbolTable,XMLGrammarPool)
/**
@@ -172,25 +177,4 @@ public void setProperty(String name, Object value)
}
}
}
-
- /**
- * Initiates the SecurityManager. This becomes necessary when the SAXParser
- * is constructed directly by, for example, XMLReaderFactory rather than
- * through SAXParserFactory.
- */
- private void initSecurityManager() {
- try {
- if (securityManager == null) {
- securityManager = new XMLSecurityManager(true);
- super.setProperty(Constants.SECURITY_MANAGER, securityManager);
- }
-
- if (securityPropertyManager == null) {
- securityPropertyManager = new XMLSecurityPropertyManager();
- super.setProperty(JdkConstants.XML_SECURITY_PROPERTY_MANAGER, securityPropertyManager);
- }
- } catch (SAXException e) {
- Utils.dPrint(() -> e.getMessage());
- }
- }
} // class SAXParser
diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XIncludeAwareParserConfiguration.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XIncludeAwareParserConfiguration.java
index 07a5498a7120d..cbe555adcd120 100644
--- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XIncludeAwareParserConfiguration.java
+++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XIncludeAwareParserConfiguration.java
@@ -1,6 +1,5 @@
/*
- * reserved comment block
- * DO NOT REMOVE OR ALTER!
+ * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
@@ -25,6 +24,7 @@
import com.sun.org.apache.xerces.internal.util.FeatureState;
import com.sun.org.apache.xerces.internal.util.NamespaceSupport;
import com.sun.org.apache.xerces.internal.util.SymbolTable;
+import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager;
import com.sun.org.apache.xerces.internal.xinclude.XIncludeHandler;
import com.sun.org.apache.xerces.internal.xinclude.XIncludeNamespaceSupport;
import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
@@ -33,6 +33,7 @@
import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource;
+import jdk.xml.internal.XMLSecurityManager;
/**
* This class is the configuration used to parse XML 1.0 and XML 1.1 documents
@@ -40,6 +41,7 @@
*
* @author Michael Glavassevich, IBM
*
+ * @LastModified: Nov 2023
*/
public class XIncludeAwareParserConfiguration extends XML11Configuration {
@@ -88,7 +90,7 @@ public class XIncludeAwareParserConfiguration extends XML11Configuration {
/** Default constructor. */
public XIncludeAwareParserConfiguration() {
- this(null, null, null);
+ this(null, null, null, null, null);
} // ()
/**
@@ -97,7 +99,7 @@ public XIncludeAwareParserConfiguration() {
* @param symbolTable The symbol table to use.
*/
public XIncludeAwareParserConfiguration(SymbolTable symbolTable) {
- this(symbolTable, null, null);
+ this(symbolTable, null, null, null, null);
} // (SymbolTable)
/**
@@ -111,7 +113,7 @@ public XIncludeAwareParserConfiguration(SymbolTable symbolTable) {
public XIncludeAwareParserConfiguration(
SymbolTable symbolTable,
XMLGrammarPool grammarPool) {
- this(symbolTable, grammarPool, null);
+ this(symbolTable, grammarPool, null, null, null);
} // (SymbolTable,XMLGrammarPool)
/**
@@ -123,11 +125,15 @@ public XIncludeAwareParserConfiguration(
* @param grammarPool The grammar pool to use.
* @param parentSettings The parent settings.
*/
- public XIncludeAwareParserConfiguration(
- SymbolTable symbolTable,
- XMLGrammarPool grammarPool,
+ public XIncludeAwareParserConfiguration(SymbolTable symbolTable, XMLGrammarPool grammarPool,
XMLComponentManager parentSettings) {
- super(symbolTable, grammarPool, parentSettings);
+ this(symbolTable, grammarPool, parentSettings, null, null);
+ }
+
+ public XIncludeAwareParserConfiguration(SymbolTable symbolTable, XMLGrammarPool grammarPool,
+ XMLComponentManager parentSettings, XMLSecurityPropertyManager securityPropertyMgr,
+ XMLSecurityManager securityManager) {
+ super(symbolTable, grammarPool, parentSettings, securityPropertyMgr, securityManager);
final String[] recognizedFeatures = {
ALLOW_UE_AND_NOTATION_EVENTS,
diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XML11Configuration.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XML11Configuration.java
index b85e0f319d0c7..d4db3be16cf9f 100644
--- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XML11Configuration.java
+++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XML11Configuration.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
@@ -46,6 +46,7 @@
import com.sun.org.apache.xerces.internal.util.ParserConfigurationSettings;
import com.sun.org.apache.xerces.internal.util.PropertyState;
import com.sun.org.apache.xerces.internal.util.SymbolTable;
+import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager;
import com.sun.org.apache.xerces.internal.xni.XMLDTDContentModelHandler;
import com.sun.org.apache.xerces.internal.xni.XMLDTDHandler;
import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
@@ -70,6 +71,7 @@
import javax.xml.catalog.CatalogFeatures;
import jdk.xml.internal.JdkConstants;
import jdk.xml.internal.JdkXmlUtils;
+import jdk.xml.internal.XMLSecurityManager;
/**
* This class is the configuration used to parse XML 1.0 and XML 1.1 documents.
@@ -78,7 +80,7 @@
* @author Neil Graham, IBM
* @author Michael Glavassevich, IBM
*
- * @LastModified: May 2021
+ * @LastModified: Nov 2023
*/
public class XML11Configuration extends ParserConfigurationSettings
implements XMLPullParserConfiguration, XML11Configurable {
@@ -432,7 +434,7 @@ public class XML11Configuration extends ParserConfigurationSettings
/** Default constructor. */
public XML11Configuration() {
- this(null, null, null);
+ this(null, null, null, null, null);
} // ()
/**
@@ -441,7 +443,7 @@ public XML11Configuration() {
* @param symbolTable The symbol table to use.
*/
public XML11Configuration(SymbolTable symbolTable) {
- this(symbolTable, null, null);
+ this(symbolTable, null, null, null, null);
} // (SymbolTable)
/**
@@ -456,7 +458,7 @@ public XML11Configuration(SymbolTable symbolTable) {
* @param grammarPool The grammar pool to use.
*/
public XML11Configuration(SymbolTable symbolTable, XMLGrammarPool grammarPool) {
- this(symbolTable, grammarPool, null);
+ this(symbolTable, grammarPool, null, null, null);
} // (SymbolTable,XMLGrammarPool)
/**
@@ -471,10 +473,14 @@ public XML11Configuration(SymbolTable symbolTable, XMLGrammarPool grammarPool) {
* @param grammarPool The grammar pool to use.
* @param parentSettings The parent settings.
*/
- public XML11Configuration(
- SymbolTable symbolTable,
- XMLGrammarPool grammarPool,
- XMLComponentManager parentSettings) {
+ public XML11Configuration(SymbolTable symbolTable, XMLGrammarPool grammarPool,
+ XMLComponentManager parentSettings) {
+ this(symbolTable, grammarPool, parentSettings, null, null);
+ }
+
+ public XML11Configuration(SymbolTable symbolTable, XMLGrammarPool grammarPool,
+ XMLComponentManager parentSettings, XMLSecurityPropertyManager securityPropertyMgr,
+ XMLSecurityManager securityManager) {
super(parentSettings);
@@ -592,7 +598,7 @@ public XML11Configuration(
fProperties.put(XMLGRAMMAR_POOL, fGrammarPool);
}
- fEntityManager = new XMLEntityManager();
+ fEntityManager = new XMLEntityManager(securityPropertyMgr, securityManager);
fProperties.put(ENTITY_MANAGER, fEntityManager);
addCommonComponent(fEntityManager);
@@ -640,11 +646,6 @@ public XML11Configuration(
// REVISIT: What is the right thing to do? -Ac
}
- // Initialize Catalog features
- for( CatalogFeatures.Feature f : CatalogFeatures.Feature.values()) {
- fProperties.put(f.getPropertyName(), null);
- }
-
setProperty(JdkConstants.CDATA_CHUNK_SIZE, JdkConstants.CDATA_CHUNK_SIZE_DEFAULT);
fConfigUpdated = false;
diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XMLParser.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XMLParser.java
index 0bd3dab5678d0..01d1c904b5e94 100644
--- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XMLParser.java
+++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XMLParser.java
@@ -28,7 +28,9 @@
import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
import com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration;
import jdk.xml.internal.JdkConstants;
+import jdk.xml.internal.Utils;
import jdk.xml.internal.XMLSecurityManager;
+import org.xml.sax.SAXException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXNotRecognizedException;
@@ -48,7 +50,7 @@
*
* @author Arnaud Le Hors, IBM
* @author Andy Clark, IBM
- * @LastModified: July 2023
+ * @LastModified: Nov 2023
*/
public abstract class XMLParser {
@@ -127,20 +129,29 @@ protected XMLParser(XMLParserConfiguration config) {
public void parse(XMLInputSource inputSource)
throws XNIException, IOException {
// null indicates that the parser is called directly, initialize them
- if (securityManager == null) {
- securityManager = new XMLSecurityManager(true);
- fConfiguration.setProperty(Constants.SECURITY_MANAGER, securityManager);
- }
- if (securityPropertyManager == null) {
- securityPropertyManager = new XMLSecurityPropertyManager();
- fConfiguration.setProperty(JdkConstants.XML_SECURITY_PROPERTY_MANAGER, securityPropertyManager);
- }
-
+ initSecurityManager(null, null);
reset();
fConfiguration.parse(inputSource);
} // parse(XMLInputSource)
+ /**
+ * Initiates the SecurityManager. This becomes necessary when the Parser
+ * is constructed directly by, for example, XMLReaderFactory rather than
+ * through SAXParserFactory.
+ */
+ void initSecurityManager(XMLSecurityPropertyManager spm, XMLSecurityManager sm) {
+ if (securityManager == null) {
+ securityManager = sm != null ? sm : new XMLSecurityManager(true);
+ }
+ fConfiguration.setProperty(Constants.SECURITY_MANAGER, securityManager);
+
+ if (securityPropertyManager == null) {
+ securityPropertyManager = spm != null ? spm : new XMLSecurityPropertyManager();
+ }
+ fConfiguration.setProperty(JdkConstants.XML_SECURITY_PROPERTY_MANAGER, securityPropertyManager);
+ }
+
//
// Protected methods
//
diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/util/ParserConfigurationSettings.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/util/ParserConfigurationSettings.java
index 377bb108750be..caa4ade1df84c 100644
--- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/util/ParserConfigurationSettings.java
+++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/util/ParserConfigurationSettings.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
@@ -28,6 +28,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import jdk.xml.internal.JdkXmlUtils;
/**
* This class implements the basic operations for managing parser
@@ -42,7 +43,7 @@
*
* @author Andy Clark, IBM
*
- * @LastModified: Apr 2019
+ * @LastModified: Nov 2023
*/
public class ParserConfigurationSettings
implements XMLComponentManager {
@@ -97,6 +98,8 @@ public ParserConfigurationSettings(XMLComponentManager parent) {
// save parent
fParentSettings = parent;
+ // Initialize Catalog features
+ JdkXmlUtils.initCatalogFeatures(fProperties);
} // (XMLComponentManager)
//
diff --git a/src/java.xml/share/classes/javax/xml/catalog/Util.java b/src/java.xml/share/classes/javax/xml/catalog/Util.java
index 3bf6e31f98bba..496626d64fa20 100644
--- a/src/java.xml/share/classes/javax/xml/catalog/Util.java
+++ b/src/java.xml/share/classes/javax/xml/catalog/Util.java
@@ -165,7 +165,7 @@ static boolean isFileUriExist(URI uri, boolean openJarFile) {
case SCHEME_FILE:
String path = uri.getPath();
File f1 = new File(path);
- if (f1.isFile()) {
+ if (SecuritySupport.isFile(f1)) {
return true;
}
break;
diff --git a/src/java.xml/share/classes/jdk/xml/internal/JdkCatalog.java b/src/java.xml/share/classes/jdk/xml/internal/JdkCatalog.java
new file mode 100644
index 0000000000000..518903073998d
--- /dev/null
+++ b/src/java.xml/share/classes/jdk/xml/internal/JdkCatalog.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.xml.internal;
+
+import java.net.URI;
+import javax.xml.catalog.Catalog;
+import javax.xml.catalog.CatalogFeatures;
+import javax.xml.catalog.CatalogManager;
+
+/**
+ * Represents the built-in Catalog that hosts the DTDs for the Java platform.
+ */
+public class JdkCatalog {
+ public static final String JDKCATALOG = "/jdk/xml/internal/jdkcatalog/JDKCatalog.xml";
+ private static final String JDKCATALOG_URL = SecuritySupport.getResource(JDKCATALOG).toExternalForm();
+ public static Catalog catalog;
+
+ public static void init(String resolve) {
+ if (catalog == null) {
+ CatalogFeatures cf = JdkXmlUtils.getCatalogFeatures(null, JDKCATALOG_URL, null, resolve);
+ catalog = CatalogManager.catalog(cf, URI.create(JDKCATALOG_URL));
+ }
+ }
+}
diff --git a/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java b/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java
index da373c3813f78..412eddb5ab0a2 100644
--- a/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java
+++ b/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java
@@ -291,6 +291,7 @@ public final class JdkConstants {
/**
* System Property for the DTD property
+ * @since 22
*/
public static final String DTD_PROPNAME = "jdk.xml.dtd.support";
@@ -299,6 +300,17 @@ public final class JdkConstants {
public static final int IGNORE = 1;
public static final int DENY = 2;
+ /**
+ * System Property for the JDKCatalog' RESOLVE property
+ * @since 22
+ */
+ public static final String JDKCATALOG_RESOLVE = "jdk.xml.jdkcatalog.resolve";
+
+ // Catalog Resolve Integer mappings for String values
+ public static final int CONTINUE = 0;
+ //public static final int IGNORE = 1; // same as that of DTD
+ public static final int STRICT = 2;
+
/**
* Values for a feature
*/
diff --git a/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java b/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java
index 5acfdf6cbe82d..7ee98622317f1 100644
--- a/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java
+++ b/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java
@@ -31,6 +31,7 @@
import com.sun.org.apache.xerces.internal.util.ParserConfigurationSettings;
import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
+import java.util.Map;
import javax.xml.XMLConstants;
import javax.xml.catalog.CatalogFeatures;
import javax.xml.catalog.CatalogFeatures.Feature;
@@ -156,6 +157,31 @@ public static String getCatalogFeature(CatalogFeatures features, String name) {
return null;
}
+ /**
+ * Initialize catalog features, including setting the default values and reading
+ * from the JAXP configuration file and System Properties.
+ *
+ * @param properties the Map object that holds the properties
+ */
+ public static void initCatalogFeatures(Map properties) {
+ CatalogFeatures cf = getCatalogFeatures();
+ for( CatalogFeatures.Feature f : CatalogFeatures.Feature.values()) {
+ properties.put(f.getPropertyName(), cf.get(f));
+ }
+ }
+
+ /**
+ * Creates an instance of a CatalogFeatures with default settings.
+ * Note: the CatalogFeatures is initialized with settings in the following
+ * order:
+ * Default values -> values in the config -> values set with System Properties
+ *
+ * @return an instance of a CatalogFeatures
+ */
+ public static CatalogFeatures getCatalogFeatures() {
+ return CatalogFeatures.builder().build();
+ }
+
/**
* Creates an instance of a CatalogFeatures.
*
diff --git a/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java b/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java
index 0a4f10cea4f6d..cfb2a264f7b36 100644
--- a/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java
+++ b/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java
@@ -30,6 +30,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
+import java.net.URLConnection;
import java.nio.file.Paths;
import java.security.AccessController;
import java.security.CodeSource;
@@ -266,6 +267,18 @@ public static boolean isFileExists(final File f) {
-> f.exists()));
}
+ /**
+ * Tests whether the input is file.
+ *
+ * @param f the file to be tested
+ * @return true if the input is file, false otherwise
+ */
+ @SuppressWarnings("removal")
+ public static boolean isFile(final File f) {
+ return (AccessController.doPrivileged((PrivilegedAction) ()
+ -> f.isFile()));
+ }
+
/**
* Creates and returns a new FileInputStream from a file.
* @param file the specified file
@@ -283,6 +296,23 @@ public static FileInputStream getFileInputStream(final File file)
}
}
+ /**
+ * Returns an InputStream from a URLConnection.
+ * @param uc the URLConnection
+ * @return the InputStream
+ * @throws IOException if an I/O error occurs while creating the input stream
+ */
+ @SuppressWarnings("removal")
+ public static InputStream getInputStream(final URLConnection uc)
+ throws IOException {
+ try {
+ return AccessController.doPrivileged((PrivilegedExceptionAction) ()
+ -> uc.getInputStream());
+ } catch (PrivilegedActionException e) {
+ throw (IOException) e.getException();
+ }
+ }
+
/**
* Returns the resource as a stream.
* @param name the resource name
@@ -294,6 +324,17 @@ public static InputStream getResourceAsStream(final String name) {
SecuritySupport.class.getResourceAsStream("/"+name));
}
+ /**
+ * Returns the resource by the name.
+ * @param name the resource name
+ * @return the resource
+ */
+ @SuppressWarnings("removal")
+ public static URL getResource(final String name) {
+ return AccessController.doPrivileged((PrivilegedAction) () ->
+ SecuritySupport.class.getResource(name));
+ }
+
/**
* Gets a resource bundle using the specified base name, the default locale, and the caller's class loader.
* @param bundle the base name of the resource bundle, a fully qualified class name
diff --git a/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java b/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java
index 07493c1b32569..d343b99cf14ac 100644
--- a/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java
+++ b/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java
@@ -32,6 +32,9 @@
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
+import javax.xml.catalog.CatalogManager;
+import javax.xml.catalog.CatalogResolver;
+import javax.xml.catalog.CatalogResolver.NotFoundAction;
import javax.xml.stream.XMLInputFactory;
import jdk.xml.internal.JdkProperty.State;
import jdk.xml.internal.JdkProperty.ImplPropMap;
@@ -67,15 +70,30 @@ public final class XMLSecurityManager {
DTD_MAP = Collections.unmodifiableMap(map);
}
+ // Valid values for Catalog Resolve, and mappings between the string and
+ // interger values
+ static final Map CR_MAP;
+ // Source Level JDK 8
+ static {
+ Map map = new HashMap<>();
+ map.put("continue", 0);
+ map.put("ignore", 1);
+ map.put("strict", 2);
+ CR_MAP = Collections.unmodifiableMap(map);
+ }
+
// Value converter for properties of type Boolean
private static final BooleanMapper BOOLMAPPER = new BooleanMapper();
// Value converter for properties of type Integer
private static final IntegerMapper INTMAPPER = new IntegerMapper();
- // DTD value map
+ // DTD value mapper
private static final StringMapper DTDMAPPER = new StringMapper(DTD_MAP);
+ // Catalog Resolve value mapper
+ private static final StringMapper CRMAPPER = new StringMapper(CR_MAP);
+
/**
* Limits managed by the security manager
*/
@@ -109,6 +127,8 @@ public static enum Limit {
JdkConstants.ALLOW, JdkConstants.ALLOW, Processor.PARSER, DTDMAPPER),
XERCES_DISALLOW_DTD("disallowDTD", DISALLOW_DTD, null, null, 0, 0, Processor.PARSER, BOOLMAPPER),
STAX_SUPPORT_DTD("supportDTD", XMLInputFactory.SUPPORT_DTD, null, null, 1, 1, Processor.PARSER, BOOLMAPPER),
+ JDKCATALOG_RESOLVE("JDKCatalogResolve", JdkConstants.JDKCATALOG_RESOLVE, JdkConstants.JDKCATALOG_RESOLVE, null,
+ JdkConstants.CONTINUE, JdkConstants.CONTINUE, Processor.PARSER, CRMAPPER),
;
final String key;
@@ -266,6 +286,48 @@ public XMLSecurityManager(boolean secureProcessing) {
//read system properties or the config file (jaxp.properties by default)
readSystemProperties();
+ // prepare the JDK Catalog
+ prepareCatalog();
+ }
+
+ /**
+ * Flag indicating whether the JDK Catalog has been initialized
+ */
+ static volatile boolean jdkcatalogInitialized = false;
+ private final Object lock = new Object();
+
+ private void prepareCatalog() {
+ if (!jdkcatalogInitialized) {
+ synchronized (lock) {
+ if (!jdkcatalogInitialized) {
+ jdkcatalogInitialized = true;
+ String resolve = getLimitValueAsString(Limit.JDKCATALOG_RESOLVE);
+ JdkCatalog.init(resolve);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the JDKCatalogResolver with the current setting of the RESOLVE
+ * property.
+ *
+ * @return the JDKCatalogResolver
+ */
+ public CatalogResolver getJDKCatalogResolver() {
+ String resolve = getLimitValueAsString(Limit.JDKCATALOG_RESOLVE);
+ return CatalogManager.catalogResolver(JdkCatalog.catalog, toActionType(resolve));
+ }
+
+ // convert the string value of the RESOLVE property to the corresponding
+ // action type
+ private NotFoundAction toActionType(String resolve) {
+ for (NotFoundAction type : NotFoundAction.values()) {
+ if (type.toString().equals(resolve)) {
+ return type;
+ }
+ }
+ return null;
}
/**
diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/JDKCatalog.xml b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/JDKCatalog.xml
new file mode 100644
index 0000000000000..3919dd4981d75
--- /dev/null
+++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/JDKCatalog.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/java/dtd/preferences.dtd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/java/dtd/preferences.dtd
new file mode 100644
index 0000000000000..27166e805c699
--- /dev/null
+++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/java/dtd/preferences.dtd
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/java/dtd/properties.dtd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/java/dtd/properties.dtd
new file mode 100644
index 0000000000000..4a22bd53d8569
--- /dev/null
+++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/java/dtd/properties.dtd
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/java.xml/share/classes/module-info.java b/src/java.xml/share/classes/module-info.java
index bb15ee81b513c..0ed2ebfbe2641 100644
--- a/src/java.xml/share/classes/module-info.java
+++ b/src/java.xml/share/classes/module-info.java
@@ -402,6 +402,11 @@
* @implNote
*
*
*
+ * JDK built-in Catalog
+ * The JDK has a built-in catalog that hosts the following DTDs defined by the Java Platform:
+ *
+ * DTD for {@link java.util.prefs.Preferences java.util.prefs.Preferences}, preferences.dtd
+ * DTD for {@link java.util.Properties java.util.Properties}, properties.dtd
+ *
+ *
+ * The catalog is loaded once when the first JAXP processor factory is created.
+ *
+ *
External Resource Resolution Process with the built-in Catalog
+ * The JDK creates a {@link javax.xml.catalog.CatalogResolver CatalogResolver}
+ * with the built-in catalog when needed. This CatalogResolver is used as the
+ * default external resource resolver.
+ *
+ * XML processors may use resolvers (such as {@link org.xml.sax.EntityResolver EntityResolver},
+ * {@link javax.xml.stream.XMLResolver XMLResolver}, and {@link javax.xml.catalog.CatalogResolver CatalogResolver})
+ * to handle external references. In the absence of the user-defined resolvers,
+ * the JDK XML processors fall back to the default CatalogResolver to attempt to
+ * find a resolution before making a connection to fetch the resources. The fall-back
+ * also takes place if a user-defined resolver exists but allows the process to
+ * continue when unable to resolve the resource.
+ *
+ * If the default CatalogResolver is unable to locate a resource, it may signal
+ * the XML processors to continue processing, or skip the resource, or
+ * throw a CatalogException. The behavior is configured with the
+ * {@code jdk.xml.jdkcatalog.resolve} property.
+ *
*
Implementation Specific Properties
* In addition to the standard JAXP Properties ,
* the JDK implementation supports a number of implementation specific properties
@@ -752,7 +784,7 @@
* {@systemProperty jdk.xml.enableExtensionFunctions}
* Determines if XSLT and XPath extension functions are to be allowed.
*
- * yes
+ * yes
* Boolean
*
* true or false. True indicates that extension functions are allowed; False otherwise.
@@ -842,6 +874,40 @@
* Method 1
* 22
*
+ *
+ * {@systemProperty jdk.xml.jdkcatalog.resolve}
+ * Instructs the JDK default CatalogResolver to act in accordance with the setting
+ * of this property when unable to resolve an external reference with the built-in Catalog.
+ * The options are:
+ *
+ *
+ * {@code continue} -- Indicates that the processing should continue
+ *
+ *
+ * {@code ignore} -- Indicates that the reference is skipped
+ *
+ *
+ * {@code strict} -- Indicates that the resolver should throw a CatalogException
+ *
+ *
+ *
+ * String
+ *
+ * {@code continue, ignore, and strict}. Values are case-insensitive.
+ *
+ * continue
+ * No
+ * Yes
+ *
+ * DOM
+ * SAX
+ * StAX
+ * Validation
+ * Transform
+ *
+ * Method 1
+ * 22
+ *
*
*
*
diff --git a/src/java.xml/share/conf/jaxp.properties b/src/java.xml/share/conf/jaxp.properties
index b43b5a3e8d178..53074816cb977 100644
--- a/src/java.xml/share/conf/jaxp.properties
+++ b/src/java.xml/share/conf/jaxp.properties
@@ -47,11 +47,11 @@
# the default property values. The format is:
# system-property-name=value
#
-# For example, the FILES property in CatalogFeatures has an associated system
-# property called javax.xml.catalog.files. An entry for the FILES property in the
-# configuration file would therefore use javax.xml.catalog.files as the key, that
+# For example, the RESOLVE property in CatalogFeatures has an associated system
+# property called javax.xml.catalog.resolve. An entry for the RESOLVE property in the
+# configuration file would therefore use javax.xml.catalog.resolve as the key, that
# is:
-# javax.xml.catalog.files=strict
+# javax.xml.catalog.resolve=strict
#
#
# Extension Functions:
@@ -128,6 +128,24 @@ jdk.xml.overrideDefaultParser=false
#
# javax.xml.useCatalog=true
#
+# Implementation Specific Properties - jdkcatalog.resolve
+#
+# This property instructs the JDK default CatalogResolver to act in accordance with
+# the setting when unable to resolve an external reference with the built-in Catalog.
+# The options are:
+# continue -- indicates that the processing should continue
+# ignore -- indicates that the reference is skipped
+# strict -- indicates that the resolver should throw a CatalogException
+#
+# The following setting would cause the resolve to throw a CatalogException when
+# unable to resolve an external reference:
+# jdk.xml.jdkcatalog.resolve=strict
+#
+# Implementation Specific Properties - DTD
+#
+# This property instructs the parsers to: deny, ignore or allow DTD processing.
+# The following setting would cause the parser to reject DTD by throwing an exception.
+# jdk.xml.dtd.support=deny
#
# Implementation Specific Properties - Limits
#
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java
index de37229f68f29..92b2a773ffa98 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -349,12 +349,7 @@ public enum LintCategory {
RESTRICTED("restricted");
LintCategory(String option) {
- this(option, false);
- }
-
- LintCategory(String option, boolean hidden) {
this.option = option;
- this.hidden = hidden;
map.put(option, this);
}
@@ -363,7 +358,6 @@ static LintCategory get(String option) {
}
public final String option;
- public final boolean hidden;
}
/**
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java
index cdbd57047ff8f..ad859bb45077f 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java
@@ -211,6 +211,7 @@ public boolean isPreview(Feature feature) {
return switch (feature) {
case STRING_TEMPLATES -> true;
case UNNAMED_CLASSES -> true;
+ case SUPER_INIT -> true;
//Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing).
//When real preview features will be added, this method can be implemented to return 'true'
//for those selected features, and 'false' for all the others.
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java
index 017400adcfca9..0290f8bc75214 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java
@@ -248,6 +248,7 @@ public enum Feature {
UNNAMED_CLASSES(JDK21, Fragments.FeatureUnnamedClasses, DiagKind.PLURAL),
WARN_ON_ILLEGAL_UTF8(MIN, JDK21),
UNNAMED_VARIABLES(JDK22, Fragments.FeatureUnnamedVariables, DiagKind.PLURAL),
+ SUPER_INIT(JDK22, Fragments.FeatureSuperInit, DiagKind.NORMAL),
;
enum DiagKind {
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
index 33a751aac7e44..b69c2156e1d86 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
@@ -937,6 +937,8 @@ public void visitClassDef(JCClassDecl tree) {
Optional localCacheContext =
Optional.ofNullable(env.info.attributionMode.isSpeculative ?
argumentAttr.withLocalCacheContext() : null);
+ boolean ctorProloguePrev = env.info.ctorPrologue;
+ env.info.ctorPrologue = false;
try {
// Local and anonymous classes have not been entered yet, so we need to
// do it now.
@@ -959,12 +961,10 @@ public void visitClassDef(JCClassDecl tree) {
// make sure class has been completed:
c.complete();
- // If this class appears as an anonymous class
- // in a superclass constructor call
- // disable implicit outer instance from being passed.
+ // If this class appears as an anonymous class in a constructor
+ // prologue, disable implicit outer instance from being passed.
// (This would be an illegal access to "this before super").
- if (env.info.isSelfCall &&
- env.tree.hasTag(NEWCLASS)) {
+ if (ctorProloguePrev && env.tree.hasTag(NEWCLASS)) {
c.flags_field |= NOOUTERTHIS;
}
attribClass(tree.pos(), c);
@@ -972,6 +972,7 @@ public void visitClassDef(JCClassDecl tree) {
}
} finally {
localCacheContext.ifPresent(LocalCacheContext::leave);
+ env.info.ctorPrologue = ctorProloguePrev;
}
}
@@ -981,6 +982,8 @@ public void visitMethodDef(JCMethodDecl tree) {
Lint lint = env.info.lint.augment(m);
Lint prevLint = chk.setLint(lint);
+ boolean ctorProloguePrev = env.info.ctorPrologue;
+ env.info.ctorPrologue = false;
MethodSymbol prevMethod = chk.setMethod(m);
try {
deferredLintHandler.flush(tree.pos());
@@ -1044,6 +1047,9 @@ public void visitMethodDef(JCMethodDecl tree) {
chk.validate(tree.recvparam, newEnv);
}
+ // Is this method a constructor?
+ boolean isConstructor = TreeInfo.isConstructor(tree);
+
if (env.enclClass.sym.isRecord() && tree.sym.owner.kind == TYP) {
// lets find if this method is an accessor
Optional extends RecordComponent> recordComponent = env.enclClass.sym.getRecordComponents().stream()
@@ -1071,14 +1077,11 @@ public void visitMethodDef(JCMethodDecl tree) {
}
}
- if (tree.name == names.init) {
+ if (isConstructor) {
// if this a constructor other than the canonical one
if ((tree.sym.flags_field & RECORD) == 0) {
- JCMethodInvocation app = TreeInfo.firstConstructorCall(tree);
- if (app == null ||
- TreeInfo.name(app.meth) != names._this ||
- !checkFirstConstructorStat(app, tree, false)) {
- log.error(tree, Errors.FirstStatementMustBeCallToAnotherConstructor(env.enclClass.sym));
+ if (!TreeInfo.hasConstructorCall(tree, names._this)) {
+ log.error(tree, Errors.NonCanonicalConstructorInvokeAnotherConstructor(env.enclClass.sym));
}
} else {
// but if it is the canonical:
@@ -1104,11 +1107,7 @@ public void visitMethodDef(JCMethodDecl tree) {
);
}
- JCMethodInvocation app = TreeInfo.firstConstructorCall(tree);
- if (app != null &&
- (TreeInfo.name(app.meth) == names._this ||
- TreeInfo.name(app.meth) == names._super) &&
- checkFirstConstructorStat(app, tree, false)) {
+ if (TreeInfo.hasAnyConstructorCall(tree)) {
log.error(tree, Errors.InvalidCanonicalConstructorInRecord(
Fragments.Canonical, env.enclClass.sym.name,
Fragments.CanonicalMustNotContainExplicitConstructorInvocation));
@@ -1186,16 +1185,14 @@ public void visitMethodDef(JCMethodDecl tree) {
// Add an implicit super() call unless an explicit call to
// super(...) or this(...) is given
// or we are compiling class java.lang.Object.
- if (tree.name == names.init && owner.type != syms.objectType) {
- JCBlock body = tree.body;
- if (body.stats.isEmpty() ||
- TreeInfo.getConstructorInvocationName(body.stats, names) == names.empty) {
- JCStatement supCall = make.at(body.pos).Exec(make.Apply(List.nil(),
+ if (isConstructor && owner.type != syms.objectType) {
+ if (!TreeInfo.hasAnyConstructorCall(tree)) {
+ JCStatement supCall = make.at(tree.body.pos).Exec(make.Apply(List.nil(),
make.Ident(names._super), make.Idents(List.nil())));
- body.stats = body.stats.prepend(supCall);
+ tree.body.stats = tree.body.stats.prepend(supCall);
} else if ((env.enclClass.sym.flags() & ENUM) != 0 &&
(tree.mods.flags & GENERATEDCONSTR) == 0 &&
- TreeInfo.isSuperCall(body.stats.head)) {
+ TreeInfo.hasConstructorCall(tree, names._super)) {
// enum constructors are not allowed to call super
// directly, so make sure there aren't any super calls
// in enum constructors, except in the compiler
@@ -1225,6 +1222,9 @@ public void visitMethodDef(JCMethodDecl tree) {
annotate.queueScanTreeAndTypeAnnotate(tree.body, localEnv, m, null);
annotate.flush();
+ // Start of constructor prologue
+ localEnv.info.ctorPrologue = isConstructor;
+
// Attribute method body.
attribStat(tree.body, localEnv);
}
@@ -1234,6 +1234,7 @@ public void visitMethodDef(JCMethodDecl tree) {
} finally {
chk.setLint(prevLint);
chk.setMethod(prevMethod);
+ env.info.ctorPrologue = ctorProloguePrev;
}
}
@@ -2518,21 +2519,15 @@ public void visitApply(JCMethodInvocation tree) {
ListBuffer argtypesBuf = new ListBuffer<>();
if (isConstructorCall) {
- // We are seeing a ...this(...) or ...super(...) call.
- // Check that this is the first statement in a constructor.
- checkFirstConstructorStat(tree, env.enclMethod, true);
-
- // Record the fact
- // that this is a constructor call (using isSelfCall).
- localEnv.info.isSelfCall = true;
// Attribute arguments, yielding list of argument types.
- localEnv.info.constructorArgs = true;
KindSelector kind = attribArgs(KindSelector.MTH, tree.args, localEnv, argtypesBuf);
- localEnv.info.constructorArgs = false;
argtypes = argtypesBuf.toList();
typeargtypes = attribTypes(tree.typeargs, localEnv);
+ // Done with this()/super() parameters. End of constructor prologue.
+ env.info.ctorPrologue = false;
+
// Variable `site' points to the class in which the called
// constructor is defined.
Type site = env.enclClass.sym.type;
@@ -2661,26 +2656,6 @@ Type adjustMethodReturnType(Symbol msym, Type qualifierType, Name methodName, Li
}
}
- /** Check that given application node appears as first statement
- * in a constructor call.
- * @param tree The application node
- * @param enclMethod The enclosing method of the application.
- * @param error Should an error be issued?
- */
- boolean checkFirstConstructorStat(JCMethodInvocation tree, JCMethodDecl enclMethod, boolean error) {
- if (enclMethod != null && enclMethod.name == names.init) {
- JCBlock body = enclMethod.body;
- if (body.stats.head.hasTag(EXEC) &&
- ((JCExpressionStatement) body.stats.head).expr == tree)
- return true;
- }
- if (error) {
- log.error(tree.pos(),
- Errors.CallMustBeFirstStmtInCtor(TreeInfo.name(tree.meth)));
- }
- return false;
- }
-
/** Obtain a method type with given argument types.
*/
Type newMethodTemplate(Type restype, List argtypes, List typeargtypes) {
@@ -4353,16 +4328,6 @@ public void visitIdent(JCIdent tree) {
checkAssignable(tree.pos(), v, null, env);
}
- // In a constructor body,
- // if symbol is a field or instance method, check that it is
- // not accessed before the supertype constructor is called.
- if (symEnv.info.isSelfCall &&
- sym.kind.matches(KindSelector.VAL_MTH) &&
- sym.owner.kind == TYP &&
- (sym.flags() & STATIC) == 0) {
- chk.earlyRefError(tree.pos(), sym.kind == VAR ?
- sym : thisSym(tree.pos(), env));
- }
Env env1 = env;
if (sym.kind != ERR && sym.kind != TYP &&
sym.owner != null && sym.owner != env1.enclClass.sym) {
@@ -4474,18 +4439,7 @@ public void visitSelect(JCFieldAccess tree) {
}
if (isType(sitesym)) {
- if (sym.name == names._this || sym.name == names._super) {
- // If `C' is the currently compiled class, check that
- // `C.this' does not appear in an explicit call to a constructor
- // also make sure that `super` is not used in constructor invocations
- if (env.info.isSelfCall &&
- ((sym.name == names._this &&
- site.tsym == env.enclClass.sym) ||
- sym.name == names._super && env.info.constructorArgs &&
- (sitesym.isInterface() || site.tsym == env.enclClass.sym))) {
- chk.earlyRefError(tree.pos(), sym);
- }
- } else {
+ if (sym.name != names._this && sym.name != names._super) {
// Check if type-qualified fields or methods are static (JLS)
if ((sym.flags() & STATIC) == 0 &&
sym.name != names._super &&
@@ -5674,6 +5628,9 @@ private void attribClassBody(Env env, ClassSymbol c) {
}
}
+ // Check for proper placement of super()/this() calls.
+ chk.checkSuperInitCalls(tree);
+
// Check for cycles among non-initial constructors.
chk.checkCyclicConstructors(tree);
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java
index 2a96b11316c01..b8afce009a591 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java
@@ -49,13 +49,9 @@ public class AttrContext {
*/
int staticLevel = 0;
- /** Is this an environment for a this(...) or super(...) call?
+ /** Are we in the 'prologue' part of a constructor, prior to an explicit this()/super()?
*/
- boolean isSelfCall = false;
-
- /** are we analyzing the arguments for a constructor invocation?
- */
- boolean constructorArgs = false;
+ boolean ctorPrologue = false;
/** Are we evaluating the selector of a `super' or type name?
*/
@@ -136,8 +132,7 @@ AttrContext dup(WriteableScope scope) {
AttrContext info = new AttrContext();
info.scope = scope;
info.staticLevel = staticLevel;
- info.isSelfCall = isSelfCall;
- info.constructorArgs = constructorArgs;
+ info.ctorPrologue = ctorPrologue;
info.selectSuper = selectSuper;
info.pendingResolutionPhase = pendingResolutionPhase;
info.lint = lint;
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
index d05d5e2f51095..ead1ecf3c633f 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
@@ -359,15 +359,6 @@ Type typeTagError(DiagnosticPosition pos, JCDiagnostic required, Object found) {
return types.createErrorType(found instanceof Type type ? type : syms.errType);
}
- /** Report an error that symbol cannot be referenced before super
- * has been called.
- * @param pos Position to be used for error reporting.
- * @param sym The referenced symbol.
- */
- void earlyRefError(DiagnosticPosition pos, Symbol sym) {
- log.error(pos, Errors.CantRefBeforeCtorCalled(sym));
- }
-
/** Report duplicate declaration error.
*/
void duplicateError(DiagnosticPosition pos, Symbol sym) {
@@ -3934,10 +3925,11 @@ void checkCyclicConstructors(JCClassDecl tree) {
// enter each constructor this-call into the map
for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
- JCMethodInvocation app = TreeInfo.firstConstructorCall(l.head);
- if (app == null) continue;
- JCMethodDecl meth = (JCMethodDecl) l.head;
- if (TreeInfo.name(app.meth) == names._this) {
+ if (!TreeInfo.isConstructor(l.head))
+ continue;
+ JCMethodDecl meth = (JCMethodDecl)l.head;
+ JCMethodInvocation app = TreeInfo.findConstructorCall(meth);
+ if (app != null && TreeInfo.name(app.meth) == names._this) {
callMap.put(meth.sym, TreeInfo.symbol(app.meth));
} else {
meth.sym.flags_field |= ACYCLIC;
@@ -3970,6 +3962,128 @@ private void checkCyclicConstructor(JCClassDecl tree, Symbol ctor,
}
}
+/* *************************************************************************
+ * Verify the proper placement of super()/this() calls.
+ *
+ * - super()/this() may only appear in constructors
+ * - There must be at most one super()/this() call per constructor
+ * - The super()/this() call, if any, must be a top-level statement in the
+ * constructor, i.e., not nested inside any other statement or block
+ * - There must be no return statements prior to the super()/this() call
+ **************************************************************************/
+
+ void checkSuperInitCalls(JCClassDecl tree) {
+ new SuperThisChecker().check(tree);
+ }
+
+ private class SuperThisChecker extends TreeScanner {
+
+ // Match this scan stack: 1=JCMethodDecl, 2=JCExpressionStatement, 3=JCMethodInvocation
+ private static final int MATCH_SCAN_DEPTH = 3;
+
+ private boolean constructor; // is this method a constructor?
+ private boolean firstStatement; // at the first statement in method?
+ private JCReturn earlyReturn; // first return prior to the super()/init(), if any
+ private Name initCall; // whichever of "super" or "init" we've seen already
+ private int scanDepth; // current scan recursion depth in method body
+
+ public void check(JCClassDecl classDef) {
+ scan(classDef.defs);
+ }
+
+ @Override
+ public void visitMethodDef(JCMethodDecl tree) {
+ Assert.check(!constructor);
+ Assert.check(earlyReturn == null);
+ Assert.check(initCall == null);
+ Assert.check(scanDepth == 1);
+
+ // Initialize state for this method
+ constructor = TreeInfo.isConstructor(tree);
+ try {
+
+ // Scan method body
+ if (tree.body != null) {
+ firstStatement = true;
+ for (List l = tree.body.stats; l.nonEmpty(); l = l.tail) {
+ scan(l.head);
+ firstStatement = false;
+ }
+ }
+
+ // Verify no 'return' seen prior to an explicit super()/this() call
+ if (constructor && earlyReturn != null && initCall != null)
+ log.error(earlyReturn.pos(), Errors.ReturnBeforeSuperclassInitialized);
+ } finally {
+ firstStatement = false;
+ constructor = false;
+ earlyReturn = null;
+ initCall = null;
+ }
+ }
+
+ @Override
+ public void scan(JCTree tree) {
+ scanDepth++;
+ try {
+ super.scan(tree);
+ } finally {
+ scanDepth--;
+ }
+ }
+
+ @Override
+ public void visitApply(JCMethodInvocation apply) {
+ do {
+
+ // Is this a super() or this() call?
+ Name methodName = TreeInfo.name(apply.meth);
+ if (methodName != names._super && methodName != names._this)
+ break;
+
+ // super()/this() calls must only appear in a constructor
+ if (!constructor) {
+ log.error(apply.pos(), Errors.CallMustOnlyAppearInCtor);
+ break;
+ }
+
+ // super()/this() calls must be a top level statement
+ if (scanDepth != MATCH_SCAN_DEPTH) {
+ log.error(apply.pos(), Errors.CtorCallsNotAllowedHere);
+ break;
+ }
+
+ // super()/this() calls must not appear more than once
+ if (initCall != null) {
+ log.error(apply.pos(), Errors.RedundantSuperclassInit);
+ break;
+ }
+
+ // If super()/this() isn't first, require "statements before super()" feature
+ if (!firstStatement)
+ preview.checkSourceLevel(apply.pos(), Feature.SUPER_INIT);
+
+ // We found a legitimate super()/this() call; remember it
+ initCall = methodName;
+ } while (false);
+
+ // Proceed
+ super.visitApply(apply);
+ }
+
+ @Override
+ public void visitReturn(JCReturn tree) {
+ if (constructor && initCall == null && earlyReturn == null)
+ earlyReturn = tree; // we have seen a return but not (yet) a super()/this()
+ super.visitReturn(tree);
+ }
+
+ @Override
+ public void visitClassDef(JCClassDecl tree) {
+ // don't descend any further
+ }
+ }
+
/* *************************************************************************
* Miscellaneous
**************************************************************************/
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java
index e09ed62d62bb0..90d482438417f 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java
@@ -206,7 +206,6 @@ public Env classEnv(JCClassDecl tree, Env env) {
env.dup(tree, env.info.dup(WriteableScope.create(tree.sym)));
localEnv.enclClass = tree;
localEnv.outer = env;
- localEnv.info.isSelfCall = false;
localEnv.info.lint = null; // leave this to be filled in by Attr,
// when annotations have been processed
localEnv.info.isAnonymousDiamond = TreeInfo.isDiamond(env.tree);
@@ -259,7 +258,6 @@ public Env moduleEnv(JCModuleDecl tree, Env env) {
env.dup(tree, env.info.dup(WriteableScope.create(tree.sym)));
localEnv.enclClass = predefClassDef;
localEnv.outer = env;
- localEnv.info.isSelfCall = false;
localEnv.info.lint = null; // leave this to be filled in by Attr,
// when annotations have been processed
return localEnv;
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
index 4a23bda0ea875..d07b3a41ccb7c 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
@@ -32,6 +32,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
+import java.util.function.Consumer;
import com.sun.source.tree.CaseTree;
import com.sun.source.tree.LambdaExpressionTree.BodyKind;
@@ -386,6 +387,13 @@ private JumpKind(Tag treeTag) {
*/
ListBuffer pendingExits;
+ /** A class whose initializers we are scanning. Because initializer
+ * scans can be triggered out of sequence when visiting certain nodes
+ * (e.g., super()), we protect against infinite loops that could be
+ * triggered by incorrect code (e.g., super() inside initializer).
+ */
+ JCClassDecl initScanClass;
+
/** A pending exit. These are the statements return, break, and
* continue. In addition, exception-throwing expressions or
* statements are put here when not known to be caught. This
@@ -471,6 +479,24 @@ protected void scanSyntheticBreak(TreeMaker make, JCTree swtch) {
scan(brk);
}
}
+
+ // Do something with all static or non-static field initializers and initialization blocks.
+ // Note: This method also sends nested class definitions to the handler.
+ protected void forEachInitializer(JCClassDecl classDef, boolean isStatic, Consumer super JCTree> handler) {
+ if (classDef == initScanClass) // avoid infinite loops
+ return;
+ JCClassDecl initScanClassPrev = initScanClass;
+ initScanClass = classDef;
+ try {
+ for (List defs = classDef.defs; defs.nonEmpty(); defs = defs.tail) {
+ JCTree def = defs.head;
+ if (!def.hasTag(METHODDEF) && ((TreeInfo.flags(def) & STATIC) != 0) == isStatic)
+ handler.accept(def);
+ }
+ } finally {
+ initScanClass = initScanClassPrev;
+ }
+ }
}
/**
@@ -536,22 +562,16 @@ public void visitClassDef(JCClassDecl tree) {
try {
// process all the static initializers
- for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
- if (!l.head.hasTag(METHODDEF) &&
- (TreeInfo.flags(l.head) & STATIC) != 0) {
- scanDef(l.head);
- clearPendingExits(false);
- }
- }
+ forEachInitializer(tree, true, def -> {
+ scanDef(def);
+ clearPendingExits(false);
+ });
// process all the instance initializers
- for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
- if (!l.head.hasTag(METHODDEF) &&
- (TreeInfo.flags(l.head) & STATIC) == 0) {
- scanDef(l.head);
- clearPendingExits(false);
- }
- }
+ forEachInitializer(tree, false, def -> {
+ scanDef(def);
+ clearPendingExits(false);
+ });
// process all the methods
for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
@@ -1362,40 +1382,10 @@ public void visitClassDef(JCClassDecl tree) {
try {
// process all the static initializers
- for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
- if (!l.head.hasTag(METHODDEF) &&
- (TreeInfo.flags(l.head) & STATIC) != 0) {
- scan(l.head);
- errorUncaught();
- }
- }
-
- // add intersection of all throws clauses of initial constructors
- // to set of caught exceptions, unless class is anonymous.
- if (!anonymousClass) {
- boolean firstConstructor = true;
- for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
- if (TreeInfo.isInitialConstructor(l.head)) {
- List mthrown =
- ((JCMethodDecl) l.head).sym.type.getThrownTypes();
- if (firstConstructor) {
- caught = mthrown;
- firstConstructor = false;
- } else {
- caught = chk.intersect(mthrown, caught);
- }
- }
- }
- }
-
- // process all the instance initializers
- for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
- if (!l.head.hasTag(METHODDEF) &&
- (TreeInfo.flags(l.head) & STATIC) == 0) {
- scan(l.head);
- errorUncaught();
- }
- }
+ forEachInitializer(tree, true, def -> {
+ scan(def);
+ errorUncaught();
+ });
// in an anonymous class, add the set of thrown exceptions to
// the throws clause of the synthetic constructor and propagate
@@ -1450,7 +1440,7 @@ public void visitMethodDef(JCMethodDecl tree) {
JCVariableDecl def = l.head;
scan(def);
}
- if (TreeInfo.isInitialConstructor(tree))
+ if (TreeInfo.hasConstructorCall(tree, names._super))
caught = chk.union(caught, mthrown);
else if ((tree.sym.flags() & (BLOCK | STATIC)) != BLOCK)
caught = mthrown;
@@ -1751,8 +1741,18 @@ public void visitThrow(JCThrow tree) {
public void visitApply(JCMethodInvocation tree) {
scan(tree.meth);
scan(tree.args);
+
+ // Mark as thrown the exceptions thrown by the method being invoked
for (List l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail)
markThrown(tree, l.head);
+
+ // After super(), scan initializers to uncover any exceptions they throw
+ if (TreeInfo.name(tree.meth) == names._super) {
+ forEachInitializer(classDef, false, def -> {
+ scan(def);
+ errorUncaught();
+ });
+ }
}
public void visitNewClass(JCNewClass tree) {
@@ -2095,11 +2095,11 @@ public AssignAnalyzer() {
uninitsWhenFalse = new Bits(true);
}
- private boolean isInitialConstructor = false;
+ private boolean isConstructor;
@Override
protected void markDead() {
- if (!isInitialConstructor) {
+ if (!isConstructor) {
inits.inclRange(returnadr, nextadr);
} else {
for (int address = returnadr; address < nextadr; address++) {
@@ -2346,13 +2346,10 @@ public void visitClassDef(JCClassDecl tree) {
}
// process all the static initializers
- for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
- if (!l.head.hasTag(METHODDEF) &&
- (TreeInfo.flags(l.head) & STATIC) != 0) {
- scan(l.head);
- clearPendingExits(false);
- }
- }
+ forEachInitializer(tree, true, def -> {
+ scan(def);
+ clearPendingExits(false);
+ });
// verify all static final fields got initailized
for (int i = firstadr; i < nextadr; i++) {
@@ -2376,15 +2373,6 @@ public void visitClassDef(JCClassDecl tree) {
}
}
- // process all the instance initializers
- for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
- if (!l.head.hasTag(METHODDEF) &&
- (TreeInfo.flags(l.head) & STATIC) == 0) {
- scan(l.head);
- clearPendingExits(false);
- }
- }
-
// process all the methods
for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
if (l.head.hasTag(METHODDEF)) {
@@ -2423,13 +2411,16 @@ public void visitMethodDef(JCMethodDecl tree) {
int returnadrPrev = returnadr;
Assert.check(pendingExits.isEmpty());
- boolean lastInitialConstructor = isInitialConstructor;
+ boolean isConstructorPrev = isConstructor;
try {
- isInitialConstructor = TreeInfo.isInitialConstructor(tree);
+ isConstructor = TreeInfo.isConstructor(tree);
- if (!isInitialConstructor) {
+ // We only track field initialization inside constructors
+ if (!isConstructor) {
firstadr = nextadr;
}
+
+ // Mark all method parameters as DA
for (List l = tree.params; l.nonEmpty(); l = l.tail) {
JCVariableDecl def = l.head;
scan(def);
@@ -2445,7 +2436,7 @@ public void visitMethodDef(JCMethodDecl tree) {
boolean isCompactOrGeneratedRecordConstructor = (tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 ||
(tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD);
- if (isInitialConstructor) {
+ if (isConstructor) {
boolean isSynthesized = (tree.sym.flags() &
GENERATEDCONSTR) != 0;
for (int i = firstadr; i < nextadr; i++) {
@@ -2487,7 +2478,7 @@ public void visitMethodDef(JCMethodDecl tree) {
nextadr = nextadrPrev;
firstadr = firstadrPrev;
returnadr = returnadrPrev;
- isInitialConstructor = lastInitialConstructor;
+ isConstructor = isConstructorPrev;
}
} finally {
lint = lintPrev;
@@ -2503,7 +2494,7 @@ private void clearPendingExits(boolean inMethod) {
Assert.check((inMethod && exit.tree.hasTag(RETURN)) ||
log.hasErrorOn(exit.tree.pos()),
exit.tree);
- if (inMethod && isInitialConstructor) {
+ if (inMethod && isConstructor) {
Assert.check(exit instanceof AssignPendingExit);
inits.assign(((AssignPendingExit) exit).exit_inits);
for (int i = firstadr; i < nextadr; i++) {
@@ -2959,6 +2950,28 @@ public void visitThrow(JCThrow tree) {
public void visitApply(JCMethodInvocation tree) {
scanExpr(tree.meth);
scanExprs(tree.args);
+
+ // Handle superclass constructor invocations
+ if (isConstructor) {
+
+ // If super(): at this point all initialization blocks will execute
+ Name name = TreeInfo.name(tree.meth);
+ if (name == names._super) {
+ forEachInitializer(classDef, false, def -> {
+ scan(def);
+ clearPendingExits(false);
+ });
+ }
+
+ // If this(): at this point all final uninitialized fields will get initialized
+ else if (name == names._this) {
+ for (int address = firstadr; address < nextadr; address++) {
+ VarSymbol sym = vardecls[address].sym;
+ if (isFinalUninitializedField(sym) && !sym.isStatic())
+ letInit(tree.pos(), sym);
+ }
+ }
+ }
}
public void visitNewClass(JCNewClass tree) {
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
index c51b1cb2a7e8b..39807aeaa5e18 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
@@ -1859,7 +1859,7 @@ JCExpression makeOuterThis(DiagnosticPosition pos, TypeSymbol c) {
ot = ots.head;
} while (ot.owner != otc);
if (otc.owner.kind != PCK && !otc.hasOuterInstance()) {
- chk.earlyRefError(pos, c);
+ log.error(pos, Errors.NoEnclInstanceOfTypeInScope(c));
Assert.error(); // should have been caught in Attr
return makeNull();
}
@@ -1899,7 +1899,6 @@ JCExpression makeOwnerThisN(DiagnosticPosition pos, Symbol sym, boolean preciseM
List ots = outerThisStack;
if (ots.isEmpty()) {
log.error(pos, Errors.NoEnclInstanceOfTypeInScope(c));
- Assert.error();
return makeNull();
}
VarSymbol ot = ots.head;
@@ -1911,7 +1910,6 @@ JCExpression makeOwnerThisN(DiagnosticPosition pos, Symbol sym, boolean preciseM
ots = ots.tail;
if (ots.isEmpty()) {
log.error(pos, Errors.NoEnclInstanceOfTypeInScope(c));
- Assert.error();
return tree;
}
ot = ots.head;
@@ -2351,17 +2349,19 @@ public void visitClassDef(JCClassDecl tree) {
tree.defs = tree.defs.prepend(l.head);
enterSynthetic(tree.pos(), l.head.sym, currentClass.members());
}
- // If this$n was accessed, add the field definition and
- // update initial constructors to initialize it
+ // If this$n was accessed, add the field definition and prepend
+ // initializer code to any super() invocation to initialize it
if (currentClass.hasOuterInstance() && shouldEmitOuterThis(currentClass)) {
tree.defs = tree.defs.prepend(otdef);
enterSynthetic(tree.pos(), otdef.sym, currentClass.members());
- for (JCTree def : tree.defs) {
- if (TreeInfo.isInitialConstructor(def)) {
- JCMethodDecl mdef = (JCMethodDecl) def;
- mdef.body.stats = mdef.body.stats.prepend(
- initOuterThis(mdef.body.pos, mdef.params.head.sym));
+ for (JCTree def : tree.defs) {
+ if (TreeInfo.isConstructor(def)) {
+ JCMethodDecl mdef = (JCMethodDecl)def;
+ if (TreeInfo.hasConstructorCall(mdef, names._super)) {
+ List initializer = List.of(initOuterThis(mdef.body.pos, mdef.params.head.sym));
+ TreeInfo.mapSuperCalls(mdef.body, supercall -> make.Block(0, initializer.append(supercall)));
+ }
}
}
}
@@ -2826,19 +2826,18 @@ private void visitMethodDefInternal(JCMethodDecl tree) {
tree.params = tree.params.prepend(otdef);
}
- // If this is an initial constructor, i.e., it does not start with
- // this(...), insert initializers for this$n and proxies
- // before (pre-1.4, after) the call to superclass constructor.
- JCStatement selfCall = translate(tree.body.stats.head);
+ // Determine whether this constructor has a super() invocation
+ boolean invokesSuper = TreeInfo.hasConstructorCall(tree, names._super);
- List added = List.nil();
+ // Create initializers for this$n and proxies
+ ListBuffer added = new ListBuffer<>();
if (fvs.nonEmpty()) {
List addedargtypes = List.nil();
for (List l = fvs; l.nonEmpty(); l = l.tail) {
m.capturedLocals =
m.capturedLocals.prepend((VarSymbol)
(proxies.get(l.head)));
- if (TreeInfo.isInitialConstructor(tree)) {
+ if (invokesSuper) {
added = added.prepend(
initField(tree.body.pos, proxies.get(l.head), prevProxies.get(l.head)));
}
@@ -2852,13 +2851,18 @@ private void visitMethodDefInternal(JCMethodDecl tree) {
syms.methodClass);
}
+ // Recursively translate existing local statements
+ tree.body.stats = translate(tree.body.stats);
+
+ // Prepend initializers in front of super() call
+ if (added.nonEmpty()) {
+ List