|  | // 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[]"); | 
|  | } | 
|  | } | 
|  | } |