| // Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| package com.android.tools.r8.jasmin; |
| |
| import static org.junit.Assert.assertEquals; |
| |
| import com.android.tools.r8.ToolHelper; |
| import com.android.tools.r8.debuginfo.DebugInfoInspector; |
| import com.android.tools.r8.dex.Constants; |
| import com.android.tools.r8.naming.MemberNaming.MethodSignature; |
| import com.android.tools.r8.utils.AndroidApp; |
| import com.android.tools.r8.utils.codeinspector.CodeInspector; |
| import com.google.common.collect.ImmutableList; |
| import java.util.ArrayList; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map.Entry; |
| import org.junit.Test; |
| |
| public class JumboStringTests extends JasminTestBase { |
| |
| // String constants are split into several class files to ensure both the constant-pool and |
| // instruction count are below the class-file limits. |
| private static int CLASSES_COUNT = 10; |
| private static int MIN_STRING_COUNT = Constants.MAX_NON_JUMBO_INDEX + 2; |
| private static int EXTRA_STRINGS_PER_CLASSES_COUNT = MIN_STRING_COUNT % CLASSES_COUNT; |
| private static int STRINGS_PER_CLASSES_COUNT = |
| EXTRA_STRINGS_PER_CLASSES_COUNT + MIN_STRING_COUNT / CLASSES_COUNT; |
| |
| @Test |
| public void test() throws Exception { |
| JasminBuilder builder = new JasminBuilder(); |
| LinkedHashMap<String, MethodSignature> classes = new LinkedHashMap<>(CLASSES_COUNT); |
| for (int i = 0; i < CLASSES_COUNT; i++) { |
| JasminBuilder.ClassBuilder clazz = builder.addClass("Test" + i); |
| List<String> lines = new ArrayList<>(STRINGS_PER_CLASSES_COUNT + 100); |
| lines.addAll( |
| ImmutableList.of( |
| ".limit locals 3", |
| ".limit stack 4", |
| ".var 0 is this LTest; from L0 to L2", |
| ".var 1 is i I from L0 to L2", |
| ".var 2 is strings [Ljava/lang/String; from L1 to L2", |
| "L0:", |
| ".line 1", |
| " ldc " + STRINGS_PER_CLASSES_COUNT, |
| " anewarray java/lang/String", |
| " astore 2", |
| "L1:", |
| ".line 2")); |
| for (int j = 0; j < STRINGS_PER_CLASSES_COUNT; j++) { |
| lines.add(" aload 2"); |
| lines.add(" ldc " + j); |
| lines.add(" ldc \"string" + i + "_" + j + "\""); |
| lines.add(" aastore"); |
| } |
| lines.addAll( |
| ImmutableList.of( |
| "L2:", |
| " .line 3", |
| " aload 2", |
| " iload 1", |
| " aaload", |
| " checkcast java/lang/String", |
| " areturn")); |
| MethodSignature foo = |
| clazz.addVirtualMethod( |
| "foo", ImmutableList.of("I"), "Ljava/lang/String;", lines.toArray(new String[0])); |
| classes.put(clazz.name, foo); |
| } |
| |
| JasminBuilder.ClassBuilder clazz = builder.addClass("Test"); |
| clazz.addMainMethod( |
| ".limit stack 3", |
| ".limit locals 1", |
| " new Test0", |
| " dup", |
| " invokespecial Test0/<init>()V", |
| " ldc 42", |
| " invokevirtual Test0/foo(I)Ljava/lang/String;", |
| " getstatic java/lang/System/out Ljava/io/PrintStream;", |
| " swap", |
| " invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V", |
| " return"); |
| |
| String expected = "string0_42"; |
| assertEquals(expected, runOnJava(builder, clazz.name)); |
| |
| AndroidApp jasminApp = builder.build(); |
| AndroidApp d8App = ToolHelper.runD8(jasminApp); |
| assertEquals(expected, runOnArt(d8App, clazz.name)); |
| |
| CodeInspector inspector = new CodeInspector(d8App); |
| for (Entry<String, MethodSignature> entry : classes.entrySet()) { |
| DebugInfoInspector info = new DebugInfoInspector(inspector, entry.getKey(), entry.getValue()); |
| info.checkStartLine(1); |
| // If jumbo-string processing fails to keep debug info, some methods will have lost 'i' here. |
| info.checkLineHasExactLocals(1, "this", entry.getKey(), "i", "int"); |
| info.checkLineHasExactLocals( |
| 2, "this", entry.getKey(), "i", "int", "strings", "java.lang.String[]"); |
| } |
| } |
| } |