summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergio Giro <sgiro@google.com>2016-10-27 21:58:31 +0100
committerSergio Giro <sgiro@google.com>2016-10-28 13:19:29 +0100
commit0310e4db77a3eb2f0ff6a98af37f6ebd262014d3 (patch)
tree929a8231d23ec66533d4d27a1f3afcae3df45032
parent6d115cc32bfaa30c29fb57a2bfc2ec8f6ceb6e43 (diff)
sun.security.x509: porting rev/04cda5b7a3c1
Changes to KeyUsageExtension, NetscapeCerTypeExtension and ReasonFlags as to check as to improve checks of array bounds. sun.security.provider.certpath.DistributionPointFetcher has a similar change concerning array bounds, where the opportunity to improve probably was spotted by the fact that it uses ReasonFlags. Since in the classes in sun.security.x509 the logic for toString changed slightly, tests are added that passed both before and after the change, as to check that the outcome is the same. Bug: 29631070 Test: run cts -m CtsLibcoreTestCases Change-Id: I2a2c10c59509063e648f238f82ac71b4f513cd71
-rw-r--r--luni/src/test/java/libcore/sun/security/x509/KeyUsageExtensionTest.java58
-rw-r--r--luni/src/test/java/libcore/sun/security/x509/NetscapeCertTypeExtensionTest.java72
-rw-r--r--luni/src/test/java/libcore/sun/security/x509/ReasonFlagsTest.java49
-rw-r--r--ojluni/src/main/java/sun/security/provider/certpath/DistributionPointFetcher.java26
-rw-r--r--ojluni/src/main/java/sun/security/x509/KeyUsageExtension.java70
-rw-r--r--ojluni/src/main/java/sun/security/x509/NetscapeCertTypeExtension.java52
-rw-r--r--ojluni/src/main/java/sun/security/x509/ReasonFlags.java54
-rw-r--r--support/src/test/java/libcore/sun/security/x509/Utils.java93
8 files changed, 386 insertions, 88 deletions
diff --git a/luni/src/test/java/libcore/sun/security/x509/KeyUsageExtensionTest.java b/luni/src/test/java/libcore/sun/security/x509/KeyUsageExtensionTest.java
new file mode 100644
index 0000000000..07f9626afd
--- /dev/null
+++ b/luni/src/test/java/libcore/sun/security/x509/KeyUsageExtensionTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.sun.security.x509;
+
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.util.function.Function;
+
+import sun.security.x509.KeyUsageExtension;
+
+public class KeyUsageExtensionTest extends TestCase {
+ /**
+ * The logic for toString was changed in rev/04cda5b7a3c1. The expected result is the same
+ * before and after the change.
+ */
+ public void testToString() throws Exception {
+ String prefix = "ObjectId: 2.5.29.15 Criticality=true\n"
+ + "KeyUsage [\n";
+
+ String[] parts = new String[] {
+ " DigitalSignature\n",
+ " Non_repudiation\n",
+ " Key_Encipherment\n",
+ " Data_Encipherment\n",
+ " Key_Agreement\n",
+ " Key_CertSign\n",
+ " Crl_Sign\n",
+ " Encipher_Only\n",
+ " Decipher_Only\n"
+ };
+
+ String suffix = "]\n";
+ Function<byte[], Object> objectCreator = byteArray -> {
+ try {
+ return new KeyUsageExtension(byteArray);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ };
+ Utils.test_toString_bitArrayBasedClass(parts, objectCreator, prefix, suffix);
+ }
+}
diff --git a/luni/src/test/java/libcore/sun/security/x509/NetscapeCertTypeExtensionTest.java b/luni/src/test/java/libcore/sun/security/x509/NetscapeCertTypeExtensionTest.java
new file mode 100644
index 0000000000..f463dee495
--- /dev/null
+++ b/luni/src/test/java/libcore/sun/security/x509/NetscapeCertTypeExtensionTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.sun.security.x509;
+
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.util.function.Function;
+
+import sun.security.x509.NetscapeCertTypeExtension;
+
+public class NetscapeCertTypeExtensionTest extends TestCase {
+ /**
+ * The logic for toString was changed in rev/04cda5b7a3c1. The expected result is the same
+ * before and after the change.
+ */
+ public void testToString() throws Exception {
+ String prefix = "ObjectId: 2.16.840.1.113730.1.1 Criticality=true\n"
+ + "NetscapeCertType [\n";
+
+ String[] parts = new String[] {
+ " SSL client\n",
+ " SSL server\n",
+ " S/MIME\n",
+ " Object Signing\n",
+ "", // Note: byte 4 is reserved.
+ " SSL CA\n",
+ " S/MIME CA\n",
+ " Object Signing CA",
+ };
+
+ String suffix = "]\n";
+ Function<byte[], Object> objectCreator = byteArray -> {
+ try {
+ return new NetscapeCertTypeExtension(byteArray);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ };
+ Utils.test_toString_bitArrayBasedClass(parts, objectCreator, prefix, suffix);
+ }
+} \ No newline at end of file
diff --git a/luni/src/test/java/libcore/sun/security/x509/ReasonFlagsTest.java b/luni/src/test/java/libcore/sun/security/x509/ReasonFlagsTest.java
new file mode 100644
index 0000000000..074f24baef
--- /dev/null
+++ b/luni/src/test/java/libcore/sun/security/x509/ReasonFlagsTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.sun.security.x509;
+
+import junit.framework.TestCase;
+import java.util.function.Function;
+import sun.security.x509.ReasonFlags;
+
+
+public class ReasonFlagsTest extends TestCase {
+ /**
+ * The logic for toString was changed in rev/04cda5b7a3c1. The expected result is the same
+ * before and after the change.
+ */
+ public void testToString() throws Exception {
+ String prefix = "Reason Flags [\n";
+
+ String[] parts = new String[] {
+ " Unused\n",
+ " Key Compromise\n",
+ " CA Compromise\n",
+ " Affiliation_Changed\n",
+ " Superseded\n",
+ " Cessation Of Operation\n",
+ " Certificate Hold\n",
+ " Privilege Withdrawn\n",
+ " AA Compromise\n"
+ };
+
+ String suffix = "]\n";
+ Function<byte[], Object> objectCreator = byteArray -> new ReasonFlags(byteArray);
+ Utils.test_toString_bitArrayBasedClass(parts, objectCreator, prefix, suffix);
+ }
+}
+
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/DistributionPointFetcher.java b/ojluni/src/main/java/sun/security/provider/certpath/DistributionPointFetcher.java
index ecf609b287..34bfbba1a4 100644
--- a/ojluni/src/main/java/sun/security/provider/certpath/DistributionPointFetcher.java
+++ b/ojluni/src/main/java/sun/security/provider/certpath/DistributionPointFetcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -363,7 +363,9 @@ public class DistributionPointFetcher {
}
} else if (crlIssuer.equals(certIssuer) == false) {
if (debug != null) {
- debug.println("crl issuer does not equal cert issuer");
+ debug.println("crl issuer does not equal cert issuer.\n" +
+ "crl issuer: " + crlIssuer + "\n" +
+ "cert issuer: " + certIssuer);
}
return false;
} else {
@@ -541,10 +543,10 @@ public class DistributionPointFetcher {
// set interim reasons mask to the intersection of
// reasons in the DP and onlySomeReasons in the IDP
boolean[] idpReasonFlags = reasons.getFlags();
- for (int i = 0; i < idpReasonFlags.length; i++) {
- if (idpReasonFlags[i] && pointReasonFlags[i]) {
- interimReasonsMask[i] = true;
- }
+ for (int i = 0; i < interimReasonsMask.length; i++) {
+ interimReasonsMask[i] =
+ (i < idpReasonFlags.length && idpReasonFlags[i]) &&
+ (i < pointReasonFlags.length && pointReasonFlags[i]);
}
} else {
// set interim reasons mask to the value of
@@ -558,7 +560,6 @@ public class DistributionPointFetcher {
interimReasonsMask = pointReasonFlags.clone();
} else {
// set interim reasons mask to the special value all-reasons
- interimReasonsMask = new boolean[9];
Arrays.fill(interimReasonsMask, true);
}
}
@@ -567,7 +568,9 @@ public class DistributionPointFetcher {
// not included in the reasons mask
boolean oneOrMore = false;
for (int i = 0; i < interimReasonsMask.length && !oneOrMore; i++) {
- if (!reasonsMask[i] && interimReasonsMask[i]) {
+ if (interimReasonsMask[i] &&
+ !(i < reasonsMask.length && reasonsMask[i]))
+ {
oneOrMore = true;
}
}
@@ -693,10 +696,9 @@ public class DistributionPointFetcher {
}
// update reasonsMask
- for (int i = 0; i < interimReasonsMask.length; i++) {
- if (!reasonsMask[i] && interimReasonsMask[i]) {
- reasonsMask[i] = true;
- }
+ for (int i = 0; i < reasonsMask.length; i++) {
+ reasonsMask[i] = reasonsMask[i] ||
+ (i < interimReasonsMask.length && interimReasonsMask[i]);
}
return true;
}
diff --git a/ojluni/src/main/java/sun/security/x509/KeyUsageExtension.java b/ojluni/src/main/java/sun/security/x509/KeyUsageExtension.java
index a96ce977b0..f59c8ee12a 100644
--- a/ojluni/src/main/java/sun/security/x509/KeyUsageExtension.java
+++ b/ojluni/src/main/java/sun/security/x509/KeyUsageExtension.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -83,7 +83,8 @@ implements CertAttrSet<String> {
* @param position the position in the bit string to check.
*/
private boolean isSet(int position) {
- return bitString[position];
+ return (position < bitString.length) &&
+ bitString[position];
}
/**
@@ -275,41 +276,40 @@ implements CertAttrSet<String> {
* Returns a printable representation of the KeyUsage.
*/
public String toString() {
- String s = super.toString() + "KeyUsage [\n";
+ StringBuilder sb = new StringBuilder();
+ sb.append(super.toString());
+ sb.append("KeyUsage [\n");
- try {
- if (isSet(0)) {
- s += " DigitalSignature\n";
- }
- if (isSet(1)) {
- s += " Non_repudiation\n";
- }
- if (isSet(2)) {
- s += " Key_Encipherment\n";
- }
- if (isSet(3)) {
- s += " Data_Encipherment\n";
- }
- if (isSet(4)) {
- s += " Key_Agreement\n";
- }
- if (isSet(5)) {
- s += " Key_CertSign\n";
- }
- if (isSet(6)) {
- s += " Crl_Sign\n";
- }
- if (isSet(7)) {
- s += " Encipher_Only\n";
- }
- if (isSet(8)) {
- s += " Decipher_Only\n";
- }
- } catch (ArrayIndexOutOfBoundsException ex) {}
-
- s += "]\n";
+ if (isSet(0)) {
+ sb.append(" DigitalSignature\n");
+ }
+ if (isSet(1)) {
+ sb.append(" Non_repudiation\n");
+ }
+ if (isSet(2)) {
+ sb.append(" Key_Encipherment\n");
+ }
+ if (isSet(3)) {
+ sb.append(" Data_Encipherment\n");
+ }
+ if (isSet(4)) {
+ sb.append(" Key_Agreement\n");
+ }
+ if (isSet(5)) {
+ sb.append(" Key_CertSign\n");
+ }
+ if (isSet(6)) {
+ sb.append(" Crl_Sign\n");
+ }
+ if (isSet(7)) {
+ sb.append(" Encipher_Only\n");
+ }
+ if (isSet(8)) {
+ sb.append(" Decipher_Only\n");
+ }
+ sb.append("]\n");
- return (s);
+ return sb.toString();
}
/**
diff --git a/ojluni/src/main/java/sun/security/x509/NetscapeCertTypeExtension.java b/ojluni/src/main/java/sun/security/x509/NetscapeCertTypeExtension.java
index 182e86f8ea..78f731b7d0 100644
--- a/ojluni/src/main/java/sun/security/x509/NetscapeCertTypeExtension.java
+++ b/ojluni/src/main/java/sun/security/x509/NetscapeCertTypeExtension.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -136,7 +136,8 @@ implements CertAttrSet<String> {
* @param position the position in the bit string to check.
*/
private boolean isSet(int position) {
- return bitString[position];
+ return (position < bitString.length) &&
+ bitString[position];
}
/**
@@ -236,27 +237,34 @@ implements CertAttrSet<String> {
* Returns a printable representation of the NetscapeCertType.
*/
public String toString() {
- String s = super.toString() + "NetscapeCertType [\n";
+ StringBuilder sb = new StringBuilder();
+ sb.append(super.toString());
+ sb.append("NetscapeCertType [\n");
- try {
- if (isSet(getPosition(SSL_CLIENT)))
- s += " SSL client\n";
- if (isSet(getPosition(SSL_SERVER)))
- s += " SSL server\n";
- if (isSet(getPosition(S_MIME)))
- s += " S/MIME\n";
- if (isSet(getPosition(OBJECT_SIGNING)))
- s += " Object Signing\n";
- if (isSet(getPosition(SSL_CA)))
- s += " SSL CA\n";
- if (isSet(getPosition(S_MIME_CA)))
- s += " S/MIME CA\n";
- if (isSet(getPosition(OBJECT_SIGNING_CA)))
- s += " Object Signing CA" ;
- } catch (Exception e) { }
-
- s += "]\n";
- return (s);
+ if (isSet(0)) {
+ sb.append(" SSL client\n");
+ }
+ if (isSet(1)) {
+ sb.append(" SSL server\n");
+ }
+ if (isSet(2)) {
+ sb.append(" S/MIME\n");
+ }
+ if (isSet(3)) {
+ sb.append(" Object Signing\n");
+ }
+ if (isSet(5)) {
+ sb.append(" SSL CA\n");
+ }
+ if (isSet(6)) {
+ sb.append(" S/MIME CA\n");
+ }
+ if (isSet(7)) {
+ sb.append(" Object Signing CA");
+ }
+
+ sb.append("]\n");
+ return sb.toString();
}
/**
diff --git a/ojluni/src/main/java/sun/security/x509/ReasonFlags.java b/ojluni/src/main/java/sun/security/x509/ReasonFlags.java
index 4549ad4544..6a42046205 100644
--- a/ojluni/src/main/java/sun/security/x509/ReasonFlags.java
+++ b/ojluni/src/main/java/sun/security/x509/ReasonFlags.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -99,7 +99,8 @@ public class ReasonFlags {
* @param position the position in the bit string to check.
*/
private boolean isSet(int position) {
- return bitString[position];
+ return (position < bitString.length) &&
+ bitString[position];
}
/**
@@ -199,23 +200,38 @@ public class ReasonFlags {
* Returns a printable representation of the ReasonFlags.
*/
public String toString() {
- String s = "Reason Flags [\n";
-
- try {
- if (isSet(0)) s += " Unused\n";
- if (isSet(1)) s += " Key Compromise\n";
- if (isSet(2)) s += " CA Compromise\n";
- if (isSet(3)) s += " Affiliation_Changed\n";
- if (isSet(4)) s += " Superseded\n";
- if (isSet(5)) s += " Cessation Of Operation\n";
- if (isSet(6)) s += " Certificate Hold\n";
- if (isSet(7)) s += " Privilege Withdrawn\n";
- if (isSet(8)) s += " AA Compromise\n";
- } catch (ArrayIndexOutOfBoundsException ex) {}
-
- s += "]\n";
-
- return (s);
+ StringBuilder sb = new StringBuilder("Reason Flags [\n");
+
+ if (isSet(0)) {
+ sb.append(" Unused\n");
+ }
+ if (isSet(1)) {
+ sb.append(" Key Compromise\n");
+ }
+ if (isSet(2)) {
+ sb.append(" CA Compromise\n");
+ }
+ if (isSet(3)) {
+ sb.append(" Affiliation_Changed\n");
+ }
+ if (isSet(4)) {
+ sb.append(" Superseded\n");
+ }
+ if (isSet(5)) {
+ sb.append(" Cessation Of Operation\n");
+ }
+ if (isSet(6)) {
+ sb.append(" Certificate Hold\n");
+ }
+ if (isSet(7)) {
+ sb.append(" Privilege Withdrawn\n");
+ }
+ if (isSet(8)) {
+ sb.append(" AA Compromise\n");
+ }
+ sb.append("]\n");
+
+ return sb.toString();
}
/**
diff --git a/support/src/test/java/libcore/sun/security/x509/Utils.java b/support/src/test/java/libcore/sun/security/x509/Utils.java
new file mode 100644
index 0000000000..412a06d1a1
--- /dev/null
+++ b/support/src/test/java/libcore/sun/security/x509/Utils.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.sun.security.x509;
+
+import junit.framework.Assert;
+
+import java.util.function.Function;
+
+public class Utils extends Assert {
+ /**
+ * Many classes in this package can be created using a bit array, and the toString method
+ * depends only on the bit array. The logic for toString was changed in rev/04cda5b7a3c1.
+ * The expected result is the same before and after the change.
+ * @param parts The different parts of the result
+ * @param objectCreator Function to create a new instance of the class tested.
+ * @param prefix prefix in all toString results
+ * @param suffix suffix in all toString results
+ */
+ static void test_toString_bitArrayBasedClass(
+ String[] parts,
+ Function<byte[], Object> objectCreator,
+ String prefix,
+ String suffix) {
+ testWithEachSinglePart(parts, objectCreator, prefix, suffix);
+ testWithAllParts(parts, objectCreator, prefix, suffix);
+ testWithNoParts(parts, objectCreator, prefix, suffix);
+ testWithEveryOtherPart(parts, objectCreator, prefix, suffix);
+ }
+
+ private static void testWithEachSinglePart(
+ String[] parts, Function<byte[], Object> objectCreator, String prefix, String suffix) {
+ int bitCounter = 0, byteCounter = 1;
+ for (int i = 0; i < parts.length; i++) {
+ byte[] ba = new byte[byteCounter];
+ ba[byteCounter - 1] = (byte) (1 << (7 - bitCounter));
+ bitCounter++;
+ if (bitCounter == 8) {
+ bitCounter = 0;
+ byteCounter++;
+ }
+ Object o = objectCreator.apply(ba);
+ assertEquals(prefix + parts[i] + suffix, o.toString());
+ }
+ }
+
+ private static void testWithAllParts(
+ String[] parts, Function<byte[], Object> objectCreator, String prefix, String suffix) {
+ int bitsInAByte = 8;
+ int allOnesLength = (parts.length + bitsInAByte - 1) / bitsInAByte;
+ byte[] allOnes = new byte[allOnesLength];
+
+ for (int i = 0; i < allOnes.length; i++) {
+ allOnes[i] = -1;
+ }
+ assertEquals(prefix + String.join("", parts)
+ + suffix, objectCreator.apply(allOnes).toString());
+ }
+
+ private static void testWithNoParts(
+ String[] parts, Function<byte[], Object> objectCreator, String prefix, String suffix) {
+ // Test with empty array
+ assertEquals(prefix + suffix, objectCreator.apply(new byte[0]).toString());
+
+ // Test with array will all zeros
+ assertEquals(prefix + suffix, objectCreator.apply(new byte[parts.length]).toString());
+ }
+
+ private static void testWithEveryOtherPart(
+ String[] parts, Function<byte[], Object> objectCreator, String prefix, String suffix) {
+ int bitsInAByte = 8;
+ byte[] ba = new byte[(parts.length + bitsInAByte - 1) / bitsInAByte];
+ String expectedResult = new String();
+ for (int i = 0; i < parts.length; i += 2) {
+ ba[i / bitsInAByte] = (byte) 170; // Binary 10101010
+ expectedResult += parts[i];
+ }
+ assertEquals(prefix + expectedResult + suffix, objectCreator.apply(ba).toString());
+ }
+}