summaryrefslogtreecommitdiffstats
path: root/tools/layoutlib
diff options
context:
space:
mode:
authorDeepanshu Gupta <deepanshu@google.com>2014-07-14 15:50:49 -0700
committerDeepanshu Gupta <deepanshu@google.com>2015-01-09 17:43:12 -0800
commitdbd64f7c904287f9e1bf5bb22385aace85274cab (patch)
treee04e4c421d1a890aeea0a25ee2799179df053771 /tools/layoutlib
parent51fb7754fc9a232a225b31b37219f4b0dcba6711 (diff)
downloadframeworks_base-dbd64f7c904287f9e1bf5bb22385aace85274cab.zip
frameworks_base-dbd64f7c904287f9e1bf5bb22385aace85274cab.tar.gz
frameworks_base-dbd64f7c904287f9e1bf5bb22385aace85274cab.tar.bz2
Support Locale.toLanguageTag on Java 6
Change-Id: I255e79e2c288cd24b350b7c26128bbbb0b2cb9a3 (cherry picked from commit 5cd9dde5a2a77c5095f985186d8f03147fd22870)
Diffstat (limited to 'tools/layoutlib')
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java36
-rw-r--r--tools/layoutlib/create/README.txt13
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java5
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java88
4 files changed, 129 insertions, 13 deletions
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java
new file mode 100644
index 0000000..607e628
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 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.layoutlib.bridge.android;
+
+import java.util.Locale;
+
+import com.ibm.icu.util.ULocale;
+
+/**
+ * This class provides an alternate implementation for {@code java.util.Locale#toLanguageTag}
+ * which is only available after Java 6.
+ *
+ * The create tool re-writes references to the above mentioned method to this one. Hence it's
+ * imperative that this class is not deleted unless the create tool is modified.
+ */
+@SuppressWarnings("UnusedDeclaration")
+public class AndroidLocale {
+
+ public static String toLanguageTag(Locale locale) {
+ return ULocale.forLocale(locale).toLanguageTag();
+ }
+}
diff --git a/tools/layoutlib/create/README.txt b/tools/layoutlib/create/README.txt
index 2e83f35..678e64f 100644
--- a/tools/layoutlib/create/README.txt
+++ b/tools/layoutlib/create/README.txt
@@ -39,7 +39,8 @@ The ASM library is used to do the bytecode modification using its visitor patter
The layoutlib_create is *NOT* generic. There is no configuration file. Instead all the configuration
is done in the main() method and the CreateInfo structure is expected to change with the Android
-platform as new classes are added, changed or removed.
+platform as new classes are added, changed or removed. Some configuration that may be platform
+dependent is also present elsewhere in code.
The resulting JAR is used by layoutlib_bridge (a.k.a. "the bridge"), also part of the platform, that
provides all the necessary missing implementation for rendering graphics in Eclipse.
@@ -95,7 +96,7 @@ The generator is constructed from a CreateInfo struct that acts as a config file
- specific classes to refactor.
Each of these are specific strategies we use to be able to modify the Android code to fit within the
-Eclipse renderer. These strategies are explained beow.
+Eclipse renderer. These strategies are explained below.
The core method of the generator is transform(): it takes an input ASM ClassReader and modifies it
to produce a byte array suitable for the final JAR file.
@@ -130,9 +131,11 @@ the StackMapTable correctly and Java 7 VM enforces that classes with version gre
valid StackMapTable. As a side benefit of this, we can continue to support Java 6 because Java 7 on
Mac has horrible font rendering support.
-ReplaceMethodCallsAdapter replaces calls to certain methods. Currently, it only rewrites calls to
-specialized versions of java.lang.System.arraycopy(), which are not part of the Desktop VM to call
-the more general method java.lang.System.arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V.
+ReplaceMethodCallsAdapter replaces calls to certain methods. This is different from the
+DelegateMethodAdapter since it doesn't preserve the original copy of the method and more importantly
+changes the calls to a method in each class instead of changing the implementation of the method.
+This is useful for methods in the Java namespace where we cannot add delegates. The configuration
+for this is not done through the CreateInfo class, but done in the ReplaceMethodAdapter.
The ClassAdapters are chained together to achieve the desired output. (Look at section 2.2.7
Transformation chains in the asm user guide, link in the References.) The order of execution of
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
index e414bbc..017bf99 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
@@ -726,9 +726,8 @@ public class AsmAnalyzer {
considerDesc(desc);
- // Check if method is a specialized version of java.lang.System.arrayCopy()
- if (owner.equals("java/lang/System") && name.equals("arraycopy")
- && !desc.equals("(Ljava/lang/Object;ILjava/lang/Object;II)V")) {
+ // Check if method needs to replaced by a call to a different method.
+ if (ReplaceMethodCallsAdapter.isReplacementNeeded(owner, name, desc)) {
mReplaceMethodCallClasses.add(mOwnerClass);
}
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
index ae17417..94d5975 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
@@ -20,12 +20,15 @@ import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/**
- * Replaces calls to certain methods that do not exist in the Desktop VM.
+ * Replaces calls to certain methods that do not exist in the Desktop VM. Useful for methods in the
+ * "java" package.
*/
public class ReplaceMethodCallsAdapter extends ClassVisitor {
@@ -37,6 +40,60 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor {
"([CI[CII)V", "([BI[BII)V", "([SI[SII)V", "([II[III)V",
"([JI[JII)V", "([FI[FII)V", "([DI[DII)V", "([ZI[ZII)V"));
+ private static final List<MethodReplacer> METHOD_REPLACERS = new ArrayList<MethodReplacer>(2);
+
+ // Static initialization block to initialize METHOD_REPLACERS.
+ static {
+ // Case 1: java.lang.System.arraycopy()
+ METHOD_REPLACERS.add(new MethodReplacer() {
+ @Override
+ public boolean isNeeded(String owner, String name, String desc) {
+ return owner.equals("java/lang/System") && name.equals("arraycopy") &&
+ ARRAYCOPY_DESCRIPTORS.contains(desc);
+ }
+
+ @Override
+ public void replace(int opcode, String owner, String name, String desc,
+ int[] opcodeOut, String[] output) {
+ assert isNeeded(owner, name, desc) && output.length == 3
+ && opcodeOut.length == 1;
+ opcodeOut[0] = opcode;
+ output[0] = owner;
+ output[1] = name;
+ output[2] = "(Ljava/lang/Object;ILjava/lang/Object;II)V";
+ }
+ });
+
+ // Case 2: java.util.Locale.toLanguageTag()
+ METHOD_REPLACERS.add(new MethodReplacer() {
+ @Override
+ public boolean isNeeded(String owner, String name, String desc) {
+ return owner.equals("java/util/Locale") && name.equals("toLanguageTag") &&
+ "()Ljava/lang/String;".equals(desc);
+ }
+
+ @Override
+ public void replace(int opcode, String owner, String name, String desc,
+ int[] opcodeOut, String[] output) {
+ assert isNeeded(owner, name, desc) && output.length == 3
+ && opcodeOut.length == 1;
+ opcodeOut[0] = Opcodes.INVOKESTATIC;
+ output[0] = "com/android/layoutlib/bridge/android/AndroidLocale";
+ output[1] = name;
+ output[2] = "(Ljava/util/Locale;)Ljava/lang/String;";
+ }
+ });
+ }
+
+ public static boolean isReplacementNeeded(String owner, String name, String desc) {
+ for (MethodReplacer replacer : METHOD_REPLACERS) {
+ if (replacer.isNeeded(owner, name, desc)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public ReplaceMethodCallsAdapter(ClassVisitor cv) {
super(Opcodes.ASM4, cv);
}
@@ -56,13 +113,34 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
// Check if method is a specialized version of java.lang.System.arrayCopy
- if (owner.equals("java/lang/System") && name.equals("arraycopy")) {
-
- if (ARRAYCOPY_DESCRIPTORS.contains(desc)) {
- desc = "(Ljava/lang/Object;ILjava/lang/Object;II)V";
+ for (MethodReplacer replacer : METHOD_REPLACERS) {
+ if (replacer.isNeeded(owner, name, desc)) {
+ String[] output = new String[3];
+ int[] opcodeOut = new int[1];
+ replacer.replace(opcode, owner, name, desc, opcodeOut, output);
+ opcode = opcodeOut[0];
+ owner = output[0];
+ name = output[1];
+ desc = output[2];
+ break;
}
}
super.visitMethodInsn(opcode, owner, name, desc);
}
}
+
+ private interface MethodReplacer {
+ public boolean isNeeded(String owner, String name, String desc);
+
+ /**
+ * This method must update the values of the output arrays with the new values of method
+ * attributes - opcode, owner, name and desc.
+ * @param opcodeOut An array that will contain the new value of the opcode. The size of
+ * the array must be 1.
+ * @param output An array that will contain the new values of the owner, name and desc in
+ * that order. The size of the array must be 3.
+ */
+ public void replace(int opcode, String owner, String name, String desc, int[] opcodeOut,
+ String[] output);
+ }
}