Propagate SourceDebugExtension information Add support for propagating SourceDebugExtension information from class files into class annotations in DEX files. Bug: 30109642 Test: dx/tests/run-test 133-source-debug-extension (cherry picked from commmit 8507f7e1c1a2db96af1cb39bb7fd31c48c61c1d2) Change-Id: I2b98437c5af417a87a70ee9d98ad6bc75f6c7836
diff --git a/dx/src/com/android/dx/cf/attrib/AttSourceDebugExtension.java b/dx/src/com/android/dx/cf/attrib/AttSourceDebugExtension.java new file mode 100644 index 0000000..3dd4fec --- /dev/null +++ b/dx/src/com/android/dx/cf/attrib/AttSourceDebugExtension.java
@@ -0,0 +1,62 @@ +/* + * Copyright (C) 2007 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 com.android.dx.cf.attrib; + +import com.android.dx.rop.cst.CstString; + +/** + * Attribute class for standard {@code SourceDebugExtension} attributes. + */ +public final class AttSourceDebugExtension extends BaseAttribute { + /** {@code non-null;} attribute name for attributes of this type */ + public static final String ATTRIBUTE_NAME = "SourceDebugExtension"; + + /** {@code non-null;} Contents of SMAP */ + private final CstString smapString; + + /** + * Constructs an instance. + * + * @param smapString {@code non-null;} the SMAP data from the class file. + */ + public AttSourceDebugExtension(CstString smapString) { + super(ATTRIBUTE_NAME); + + if (smapString == null) { + throw new NullPointerException("smapString == null"); + } + + this.smapString = smapString; + } + + /** {@inheritDoc} */ + @Override + public int byteLength() { + // Add 6 for the standard attribute header: the attribute name + // index (2 bytes) and the attribute length (4 bytes). + return 6 + smapString.getUtf8Size(); + } + + /** + * Gets the SMAP data of this instance. + * + * @return {@code non-null;} the SMAP data. + */ + public CstString getSmapString() { + return smapString; + } +}
diff --git a/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java b/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java index 992809a..710ba57 100644 --- a/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java +++ b/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java
@@ -31,6 +31,7 @@ import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations; import com.android.dx.cf.attrib.AttRuntimeVisibleParameterAnnotations; import com.android.dx.cf.attrib.AttSignature; +import com.android.dx.cf.attrib.AttSourceDebugExtension; import com.android.dx.cf.attrib.AttSourceFile; import com.android.dx.cf.attrib.AttSynthetic; import com.android.dx.cf.attrib.InnerClassList; @@ -103,6 +104,9 @@ if (name == AttSignature.ATTRIBUTE_NAME) { return signature(cf, offset, length, observer); } + if (name == AttSourceDebugExtension.ATTRIBUTE_NAME) { + return sourceDebugExtension(cf, offset, length, observer); + } if (name == AttSourceFile.ATTRIBUTE_NAME) { return sourceFile(cf, offset, length, observer); } @@ -692,6 +696,23 @@ } /** + * Parses a {@code SourceDebugExtesion} attribute. + */ + private Attribute sourceDebugExtension(DirectClassFile cf, int offset, int length, + ParseObserver observer) { + ByteArray bytes = cf.getBytes().slice(offset, offset + length); + CstString smapString = new CstString(bytes); + Attribute result = new AttSourceDebugExtension(smapString); + + if (observer != null) { + String decoded = smapString.getString(); + observer.parsed(bytes, offset, length, "sourceDebugExtension: " + decoded); + } + + return result; + } + + /** * Parses a {@code SourceFile} attribute. */ private Attribute sourceFile(DirectClassFile cf, int offset, int length,
diff --git a/dx/src/com/android/dx/dex/cf/AttributeTranslator.java b/dx/src/com/android/dx/dex/cf/AttributeTranslator.java index 90e32c3..31f19c0 100644 --- a/dx/src/com/android/dx/dex/cf/AttributeTranslator.java +++ b/dx/src/com/android/dx/dex/cf/AttributeTranslator.java
@@ -25,6 +25,7 @@ import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations; import com.android.dx.cf.attrib.AttRuntimeVisibleParameterAnnotations; import com.android.dx.cf.attrib.AttSignature; +import com.android.dx.cf.attrib.AttSourceDebugExtension; import com.android.dx.cf.attrib.InnerClassList; import com.android.dx.cf.direct.DirectClassFile; import com.android.dx.cf.iface.AttributeList; @@ -88,11 +89,16 @@ public static Annotations getAnnotations(AttributeList attribs) { Annotations result = getAnnotations0(attribs); Annotation signature = getSignature(attribs); + Annotation sourceDebugExtension = getSourceDebugExtension(attribs); if (signature != null) { result = Annotations.combine(result, signature); } + if (sourceDebugExtension != null) { + result = Annotations.combine(result, sourceDebugExtension); + } + return result; } @@ -214,6 +220,18 @@ return AnnotationUtils.makeSignature(signature.getSignature()); } + + private static Annotation getSourceDebugExtension(AttributeList attribs) { + AttSourceDebugExtension extension = (AttSourceDebugExtension) + attribs.findFirst(AttSourceDebugExtension.ATTRIBUTE_NAME); + + if (extension == null) { + return null; + } + + return AnnotationUtils.makeSourceDebugExtension(extension.getSmapString()); + } + /** * Gets the {@code EnclosingMethod} attribute out of a given * {@link AttributeList}, if any, translating it to an annotation.
diff --git a/dx/src/com/android/dx/dex/file/AnnotationUtils.java b/dx/src/com/android/dx/dex/file/AnnotationUtils.java index 6441522..109583e 100644 --- a/dx/src/com/android/dx/dex/file/AnnotationUtils.java +++ b/dx/src/com/android/dx/dex/file/AnnotationUtils.java
@@ -62,6 +62,10 @@ private static final CstType SIGNATURE_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/Signature;")); + /** {@code non-null;} type for {@code SourceDebugExtension} annotations */ + private static final CstType SOURCE_DEBUG_EXTENSION_TYPE = + CstType.intern(Type.intern("Ldalvik/annotation/SourceDebugExtension;")); + /** {@code non-null;} type for {@code Throws} annotations */ private static final CstType THROWS_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/Throws;")); @@ -220,6 +224,20 @@ } /** + * Constructs a standard {@code SourceDebugExtension} annotation. + * + * @param smapString {@code non-null;} the SMAP string associated with + * @return {@code non-null;} the annotation + */ + public static Annotation makeSourceDebugExtension(CstString smapString) { + Annotation result = new Annotation(SOURCE_DEBUG_EXTENSION_TYPE, SYSTEM); + + result.put(new NameValuePair(VALUE_STRING, smapString)); + result.setImmutable(); + return result; + } + + /** * Constructs a standard {@code Throws} annotation. * * @param types {@code non-null;} the list of thrown types
diff --git a/dx/tests/133-source-debug-extension/expected.txt b/dx/tests/133-source-debug-extension/expected.txt new file mode 100644 index 0000000..04d943c --- /dev/null +++ b/dx/tests/133-source-debug-extension/expected.txt
@@ -0,0 +1,4 @@ +1c1 +< SourceDebugExtension +--- +> (Ldalvik/annotation/SourceDebugExtension;
diff --git a/dx/tests/133-source-debug-extension/info.txt b/dx/tests/133-source-debug-extension/info.txt new file mode 100644 index 0000000..3bec03c --- /dev/null +++ b/dx/tests/133-source-debug-extension/info.txt
@@ -0,0 +1,7 @@ +This test checks that SourceDebugExtension information propagates from +the class file to the DEX file emitted by dx. It extracts SMAP information +from the class file and DEX file and checks the strings match. + +NB dalvik/dexdump does not have support for DEX annotations so the test +just checks that the SMAP string appears in the DEX file and assumes +it is packaged within an annotation.
diff --git a/dx/tests/133-source-debug-extension/run b/dx/tests/133-source-debug-extension/run new file mode 100755 index 0000000..4c12360 --- /dev/null +++ b/dx/tests/133-source-debug-extension/run
@@ -0,0 +1,30 @@ +#!/bin/bash +# +# Copyright (C) 2017 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. + +# Test runs in temporary directory created by test-run. + +test_class=HelloKt.class +test_dex=HelloKt.dex + +# Generate DEX file +dx --dex --output=$test_dex $test_class + +# Extract for SMAP string in DEX and class file. +for i in $test_class $test_dex; do + strings $i | sed -n -e "/SourceDebugExtension/ p" -e "/SMAP/,/23/ p" > $i.output +done + +diff $test_class.output $test_dex.output