diff options
| -rw-r--r-- | libdexfile/dex/descriptors_names.cc | 28 | ||||
| -rw-r--r-- | libdexfile/dex/dex_file_loader_test.cc | 20 | ||||
| -rw-r--r-- | libdexfile/dex/standard_dex_file.cc | 4 | ||||
| -rw-r--r-- | libdexfile/dex/standard_dex_file.h | 2 | ||||
| -rwxr-xr-x | test/2029-spaces-in-SimpleName/build | 40 | ||||
| -rw-r--r-- | test/2029-spaces-in-SimpleName/classes.dex | bin | 0 -> 808 bytes | |||
| -rw-r--r-- | test/2029-spaces-in-SimpleName/expected.txt | 1 | ||||
| -rw-r--r-- | test/2029-spaces-in-SimpleName/info.txt | 5 | ||||
| -rw-r--r-- | test/2029-spaces-in-SimpleName/src/SpacesInSimpleName.java | 73 | ||||
| -rw-r--r-- | test/Android.run-test.mk | 1 |
10 files changed, 146 insertions, 28 deletions
diff --git a/libdexfile/dex/descriptors_names.cc b/libdexfile/dex/descriptors_names.cc index 1e8eb3349b..44cb7cb588 100644 --- a/libdexfile/dex/descriptors_names.cc +++ b/libdexfile/dex/descriptors_names.cc @@ -165,7 +165,7 @@ std::string DescriptorToName(const char* descriptor) { // Helper for IsValidPartOfMemberNameUtf8(), a bit vector indicating valid low ascii. static constexpr uint32_t DEX_MEMBER_VALID_LOW_ASCII[4] = { 0x00000000, // 00..1f low control characters; nothing valid - 0x03ff2010, // 20..3f digits and symbols; valid: '0'..'9', '$', '-' + 0x03ff2011, // 20..3f space, digits and symbols; valid: ' ', '0'..'9', '$', '-' 0x87fffffe, // 40..5f uppercase etc.; valid: 'A'..'Z', '_' 0x07fffffe // 60..7f lowercase etc.; valid: 'a'..'z' }; @@ -175,12 +175,17 @@ COLD_ATTR static bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) { /* * It's a multibyte encoded character. Decode it and analyze. We - * accept anything that isn't (a) an improperly encoded low value, - * (b) an improper surrogate pair, (c) an encoded '\0', (d) a high - * control character, or (e) a high space, layout, or special - * character (U+00a0, U+2000..U+200f, U+2028..U+202f, - * U+fff0..U+ffff). This is all specified in the dex format - * document. + * accept anything that isn't: + * - an improperly encoded low value + * - an improper surrogate pair + * - an encoded '\0' + * - a C1 control character U+0080..U+009f + * - a format character U+200b..U+200f, U+2028..U+202e + * - a special character U+fff0..U+ffff + * Prior to DEX format version 040, we also excluded some of the Unicode + * space characters: + * - U+00a0, U+2000..U+200a, U+202f + * This is all specified in the dex format document. */ const uint32_t pair = GetUtf16FromUtf8(pUtf8Ptr); @@ -200,8 +205,8 @@ static bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) { // three byte UTF-8 sequence could be one half of a surrogate pair. switch (leading >> 8) { case 0x00: - // It's only valid if it's above the ISO-8859-1 high space (0xa0). - return (leading > 0x00a0); + // It's in the range that has C1 control characters. + return (leading >= 0x00a0); case 0xd8: case 0xd9: case 0xda: @@ -222,11 +227,12 @@ static bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) { return false; case 0x20: case 0xff: - // It's in the range that has spaces, controls, and specials. + // It's in the range that has format characters and specials. switch (leading & 0xfff8) { - case 0x2000: case 0x2008: + return (leading <= 0x200a); case 0x2028: + return (leading == 0x202f); case 0xfff0: case 0xfff8: return false; diff --git a/libdexfile/dex/dex_file_loader_test.cc b/libdexfile/dex/dex_file_loader_test.cc index 8b7ca1776f..30c60b10cb 100644 --- a/libdexfile/dex/dex_file_loader_test.cc +++ b/libdexfile/dex/dex_file_loader_test.cc @@ -336,23 +336,13 @@ TEST_F(DexFileLoaderTest, Version39Accepted) { EXPECT_EQ(39u, header.GetVersion()); } -TEST_F(DexFileLoaderTest, Version40Rejected) { +TEST_F(DexFileLoaderTest, Version40Accepted) { std::vector<uint8_t> dex_bytes; - DecodeDexFile(kRawDex40, &dex_bytes); + std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex40, kLocationString, &dex_bytes)); + ASSERT_TRUE(raw.get() != nullptr); - static constexpr bool kVerifyChecksum = true; - DexFileLoaderErrorCode error_code; - std::string error_msg; - std::vector<std::unique_ptr<const DexFile>> dex_files; - const DexFileLoader dex_file_loader; - ASSERT_FALSE(dex_file_loader.OpenAll(dex_bytes.data(), - dex_bytes.size(), - kLocationString, - /* verify= */ true, - kVerifyChecksum, - &error_code, - &error_msg, - &dex_files)); + const DexFile::Header& header = raw->GetHeader(); + EXPECT_EQ(40u, header.GetVersion()); } TEST_F(DexFileLoaderTest, Version41Rejected) { diff --git a/libdexfile/dex/standard_dex_file.cc b/libdexfile/dex/standard_dex_file.cc index 8bac44e02e..9c4cb45f8b 100644 --- a/libdexfile/dex/standard_dex_file.cc +++ b/libdexfile/dex/standard_dex_file.cc @@ -32,8 +32,10 @@ const uint8_t StandardDexFile::kDexMagicVersions[StandardDexFile::kNumDexVersion {'0', '3', '7', '\0'}, // Dex version 038: Android "O" and beyond. {'0', '3', '8', '\0'}, - // Dex verion 039: Beyond Android "O". + // Dex verion 039: Android "P" and beyond. {'0', '3', '9', '\0'}, + // Dex verion 040: beyond Android "10" (previously known as Android "Q"). + {'0', '4', '0', '\0'}, }; void StandardDexFile::WriteMagic(uint8_t* magic) { diff --git a/libdexfile/dex/standard_dex_file.h b/libdexfile/dex/standard_dex_file.h index 48671c946d..db82a9bf20 100644 --- a/libdexfile/dex/standard_dex_file.h +++ b/libdexfile/dex/standard_dex_file.h @@ -68,7 +68,7 @@ class StandardDexFile : public DexFile { static void WriteCurrentVersion(uint8_t* magic); static const uint8_t kDexMagic[kDexMagicSize]; - static constexpr size_t kNumDexVersions = 4; + static constexpr size_t kNumDexVersions = 5; static const uint8_t kDexMagicVersions[kNumDexVersions][kDexVersionLen]; // Returns true if the byte string points to the magic value. diff --git a/test/2029-spaces-in-SimpleName/build b/test/2029-spaces-in-SimpleName/build new file mode 100755 index 0000000000..9c3cc79e39 --- /dev/null +++ b/test/2029-spaces-in-SimpleName/build @@ -0,0 +1,40 @@ +#!/bin/bash +# +# Copyright 2019 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. + +# Stop on failure and be verbose. +set -e -x + +export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-6.0.jar" + +cd src + +# generate Java bytecode with ASM +${JAVAC:-java} -cp "$ASM_JAR:." SpacesInSimpleName.java +${JAVA:-java} -cp "$ASM_JAR:." SpacesInSimpleName + +# compile Java bytecode to DEX bytecode +# TODO: replace DX with D8 when it adds support for spaces in SimpleName +# ${D8} --min-api 10000 Main.class +$ANDROID_HOST_OUT/bin/dx --dex --output=classes.dex Main.class + +# move the resulting DEX file and cleanup +mv classes.dex ../classes.dex +rm *.class + +cd .. + +# Use API level 10000 for spaces in SimpleName +DESUGAR=false ./default-build "$@" --api-level 10000 diff --git a/test/2029-spaces-in-SimpleName/classes.dex b/test/2029-spaces-in-SimpleName/classes.dex Binary files differnew file mode 100644 index 0000000000..3804ca7080 --- /dev/null +++ b/test/2029-spaces-in-SimpleName/classes.dex diff --git a/test/2029-spaces-in-SimpleName/expected.txt b/test/2029-spaces-in-SimpleName/expected.txt new file mode 100644 index 0000000000..af5626b4a1 --- /dev/null +++ b/test/2029-spaces-in-SimpleName/expected.txt @@ -0,0 +1 @@ +Hello, world! diff --git a/test/2029-spaces-in-SimpleName/info.txt b/test/2029-spaces-in-SimpleName/info.txt new file mode 100644 index 0000000000..106ebeba21 --- /dev/null +++ b/test/2029-spaces-in-SimpleName/info.txt @@ -0,0 +1,5 @@ +Whitespace support in DEX format 040. + +This test uses ASM Java bytecode generator to generate a simple class with +the methods 'main' and some unpronounceable method which name contains all +space characters in the Unicode 'Zs' category. diff --git a/test/2029-spaces-in-SimpleName/src/SpacesInSimpleName.java b/test/2029-spaces-in-SimpleName/src/SpacesInSimpleName.java new file mode 100644 index 0000000000..847da5a8b3 --- /dev/null +++ b/test/2029-spaces-in-SimpleName/src/SpacesInSimpleName.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 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. + */ + +import java.io.*; + +import org.objectweb.asm.*; + +public class SpacesInSimpleName { + public static void main(String args[]) throws Exception { + String methodName = "method_with_spaces_" + + "20 " + + "a0\u00a0" + + "1680\u1680" + + "2000\u2000" + + "2001\u2001" + + "2002\u2002" + + "2003\u2003" + + "2004\u2004" + + "2005\u2005" + + "2006\u2006" + + "2007\u2007" + + "2008\u2008" + + "2009\u2009" + + "200a\u200a" + + "202f\u202f" + + "205f\u205f" + + "3000\u3000"; + + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); + + cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Main", + null, "java/lang/Object", null); + + MethodVisitor mvMain = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, + "main", "([Ljava/lang/String;)V", null, null); + mvMain.visitCode(); + mvMain.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", + "Ljava/io/PrintStream;"); + mvMain.visitLdcInsn("Hello, world!"); + mvMain.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", + "println", "(Ljava/lang/String;)V", false); + mvMain.visitMethodInsn(Opcodes.INVOKESTATIC, "Main", methodName, "()V", false); + mvMain.visitInsn(Opcodes.RETURN); + mvMain.visitMaxs(0, 0); // args are ignored with COMPUTE_MAXS + mvMain.visitEnd(); + MethodVisitor mvSpaces = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, + methodName, "()V", null, null); + mvSpaces.visitCode(); + mvSpaces.visitInsn(Opcodes.RETURN); + mvSpaces.visitMaxs(0, 0); // args are ignored with COMPUTE_MAXS + mvSpaces.visitEnd(); + + cw.visitEnd(); + + byte[] b = cw.toByteArray(); + OutputStream out = new FileOutputStream("Main.class"); + out.write(b, 0, b.length); + out.close(); + } +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 7b9c0836a2..2b8d8ef6f5 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -19,6 +19,7 @@ include art/build/Android.common_test.mk # Dependencies for actually running a run-test. TEST_ART_RUN_TEST_DEPENDENCIES := \ + $(HOST_OUT_EXECUTABLES)/dx \ $(HOST_OUT_EXECUTABLES)/d8 \ $(HOST_OUT_EXECUTABLES)/d8-compat-dx \ $(HOST_OUT_EXECUTABLES)/hiddenapi \ |