From 72fd8769f97305b1fb1b3d16b3f4215f6035a25a Mon Sep 17 00:00:00 2001 From: Jared Duke <jdduke@google.com> Date: Thu, 19 Oct 2023 23:13:57 +0000 Subject: [PATCH] Remove framework support for compiled views This feature did not ship and is being removed. The performance benefits were unclear and the additional complextiy was non-trivial. Remove associated support in the framework. Bug: 158121974 Test: m Change-Id: I580f7616fe517ad8ef3126d1635fe978d2a1c322 --- core/api/test-current.txt | 4 - core/java/android/view/LayoutInflater.java | 259 ++----- .../com/android/server/pm/DexOptHelper.java | 5 - .../server/pm/InstallPackageHelper.java | 12 - .../java/com/android/server/pm/Installer.java | 8 - .../server/pm/PackageManagerService.java | 6 - .../pm/PackageManagerServiceInjector.java | 8 - .../pm/PackageManagerServiceTestParams.java | 2 - .../server/pm/dex/ArtManagerService.java | 38 - .../android/server/pm/dex/ViewCompiler.java | 65 -- startop/view_compiler/Android.bp | 115 --- startop/view_compiler/OWNERS | 2 - startop/view_compiler/README.md | 53 -- startop/view_compiler/apk_layout_compiler.cc | 179 ----- startop/view_compiler/apk_layout_compiler.h | 35 - startop/view_compiler/dex_builder.cc | 704 ------------------ startop/view_compiler/dex_builder.h | 626 ---------------- .../view_compiler/dex_builder_test/Android.bp | 68 -- .../dex_builder_test/AndroidManifest.xml | 29 - .../dex_builder_test/AndroidTest.xml | 36 - .../dex_builder_test/res/layout/layout1.xml | 17 - .../dex_builder_test/res/layout/layout2.xml | 45 -- .../android/startop/test/DexBuilderTest.java | 213 ------ .../startop/test/LayoutCompilerTest.java | 52 -- .../src/android/startop/test/TestClass.java | 23 - startop/view_compiler/dex_layout_compiler.cc | 208 ------ startop/view_compiler/dex_layout_compiler.h | 123 --- .../view_compiler/dex_testcase_generator.cc | 353 --------- startop/view_compiler/java_lang_builder.cc | 115 --- startop/view_compiler/java_lang_builder.h | 65 -- startop/view_compiler/layout_validation.cc | 42 -- startop/view_compiler/layout_validation.h | 46 -- .../view_compiler/layout_validation_test.cc | 163 ---- startop/view_compiler/main.cc | 172 ----- .../view_compiler/tinyxml_layout_parser.cc | 34 - startop/view_compiler/tinyxml_layout_parser.h | 65 -- startop/view_compiler/util.cc | 38 - startop/view_compiler/util.h | 29 - startop/view_compiler/util_test.cc | 34 - 39 files changed, 65 insertions(+), 4026 deletions(-) delete mode 100644 services/core/java/com/android/server/pm/dex/ViewCompiler.java delete mode 100644 startop/view_compiler/Android.bp delete mode 100644 startop/view_compiler/OWNERS delete mode 100644 startop/view_compiler/README.md delete mode 100644 startop/view_compiler/apk_layout_compiler.cc delete mode 100644 startop/view_compiler/apk_layout_compiler.h delete mode 100644 startop/view_compiler/dex_builder.cc delete mode 100644 startop/view_compiler/dex_builder.h delete mode 100644 startop/view_compiler/dex_builder_test/Android.bp delete mode 100644 startop/view_compiler/dex_builder_test/AndroidManifest.xml delete mode 100644 startop/view_compiler/dex_builder_test/AndroidTest.xml delete mode 100644 startop/view_compiler/dex_builder_test/res/layout/layout1.xml delete mode 100644 startop/view_compiler/dex_builder_test/res/layout/layout2.xml delete mode 100644 startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java delete mode 100644 startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java delete mode 100644 startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java delete mode 100644 startop/view_compiler/dex_layout_compiler.cc delete mode 100644 startop/view_compiler/dex_layout_compiler.h delete mode 100644 startop/view_compiler/dex_testcase_generator.cc delete mode 100644 startop/view_compiler/java_lang_builder.cc delete mode 100644 startop/view_compiler/java_lang_builder.h delete mode 100644 startop/view_compiler/layout_validation.cc delete mode 100644 startop/view_compiler/layout_validation.h delete mode 100644 startop/view_compiler/layout_validation_test.cc delete mode 100644 startop/view_compiler/main.cc delete mode 100644 startop/view_compiler/tinyxml_layout_parser.cc delete mode 100644 startop/view_compiler/tinyxml_layout_parser.h delete mode 100644 startop/view_compiler/util.cc delete mode 100644 startop/view_compiler/util.h delete mode 100644 startop/view_compiler/util_test.cc diff --git a/core/api/test-current.txt b/core/api/test-current.txt index eeddeb21aa9d..cf1586c32dd8 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3493,10 +3493,6 @@ package android.view { method public boolean isSystemGroup(); } - public abstract class LayoutInflater { - method public void setPrecompiledLayoutsEnabledForTesting(boolean); - } - public final class MotionEvent extends android.view.InputEvent implements android.os.Parcelable { method public int getDisplayId(); method public void setActionButton(int); diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index 99a7fe598936..aad3bf2679d9 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -20,11 +20,9 @@ import android.annotation.LayoutRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; -import android.annotation.TestApi; import android.annotation.UiContext; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; -import android.content.pm.ApplicationInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; @@ -42,15 +40,11 @@ import android.widget.FrameLayout; import com.android.internal.R; -import dalvik.system.PathClassLoader; - import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; -import java.lang.reflect.Method; import java.util.HashMap; import java.util.Objects; @@ -82,12 +76,6 @@ public abstract class LayoutInflater { private static final String TAG = LayoutInflater.class.getSimpleName(); private static final boolean DEBUG = false; - private static final String COMPILED_VIEW_DEX_FILE_NAME = "/compiled_view.dex"; - /** - * Whether or not we use the precompiled layout. - */ - private static final String USE_PRECOMPILED_LAYOUT = "view.precompiled_layout_enabled"; - /** Empty stack trace used to avoid log spam in re-throw exceptions. */ private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0]; @@ -116,13 +104,6 @@ public abstract class LayoutInflater { private Factory2 mPrivateFactory; private Filter mFilter; - // Indicates whether we should try to inflate layouts using a precompiled layout instead of - // inflating from the XML resource. - private boolean mUseCompiledView; - // This variable holds the classloader that will be used to look for precompiled layouts. The - // The classloader includes the generated compiled_view.dex file. - private ClassLoader mPrecompiledClassLoader; - /** * This is not a public API. Two APIs are now available to alleviate the need to access * this directly: {@link #createView(Context, String, String, AttributeSet)} and @@ -259,7 +240,6 @@ public abstract class LayoutInflater { protected LayoutInflater(Context context) { StrictMode.assertConfigurationContext(context, "LayoutInflater"); mContext = context; - initPrecompiledViews(); } /** @@ -277,7 +257,6 @@ public abstract class LayoutInflater { mFactory2 = original.mFactory2; mPrivateFactory = original.mPrivateFactory; setFilter(original.mFilter); - initPrecompiledViews(); } /** @@ -419,57 +398,6 @@ public abstract class LayoutInflater { } } - private void initPrecompiledViews() { - // Precompiled layouts are not supported in this release. - boolean enabled = false; - initPrecompiledViews(enabled); - } - - private void initPrecompiledViews(boolean enablePrecompiledViews) { - mUseCompiledView = enablePrecompiledViews; - - if (!mUseCompiledView) { - mPrecompiledClassLoader = null; - return; - } - - // Make sure the application allows code generation - ApplicationInfo appInfo = mContext.getApplicationInfo(); - if (appInfo.isEmbeddedDexUsed() || appInfo.isPrivilegedApp()) { - mUseCompiledView = false; - return; - } - - // Try to load the precompiled layout file. - try { - mPrecompiledClassLoader = mContext.getClassLoader(); - String dexFile = mContext.getCodeCacheDir() + COMPILED_VIEW_DEX_FILE_NAME; - if (new File(dexFile).exists()) { - mPrecompiledClassLoader = new PathClassLoader(dexFile, mPrecompiledClassLoader); - } else { - // If the precompiled layout file doesn't exist, then disable precompiled - // layouts. - mUseCompiledView = false; - } - } catch (Throwable e) { - if (DEBUG) { - Log.e(TAG, "Failed to initialized precompiled views layouts", e); - } - mUseCompiledView = false; - } - if (!mUseCompiledView) { - mPrecompiledClassLoader = null; - } - } - - /** - * @hide for use by CTS tests - */ - @TestApi - public void setPrecompiledLayoutsEnabledForTesting(boolean enablePrecompiledLayouts) { - initPrecompiledViews(enablePrecompiledLayouts); - } - /** * Inflate a new view hierarchy from the specified xml resource. Throws * {@link InflateException} if there is an error. @@ -529,10 +457,6 @@ public abstract class LayoutInflater { + Integer.toHexString(resource) + ")"); } - View view = tryInflatePrecompiled(resource, res, root, attachToRoot); - if (view != null) { - return view; - } XmlResourceParser parser = res.getLayout(resource); try { return inflate(parser, root, attachToRoot); @@ -541,54 +465,6 @@ public abstract class LayoutInflater { } } - private @Nullable - View tryInflatePrecompiled(@LayoutRes int resource, Resources res, @Nullable ViewGroup root, - boolean attachToRoot) { - if (!mUseCompiledView) { - return null; - } - - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate (precompiled)"); - - // Try to inflate using a precompiled layout. - String pkg = res.getResourcePackageName(resource); - String layout = res.getResourceEntryName(resource); - - try { - Class clazz = Class.forName("" + pkg + ".CompiledView", false, mPrecompiledClassLoader); - Method inflater = clazz.getMethod(layout, Context.class, int.class); - View view = (View) inflater.invoke(null, mContext, resource); - - if (view != null && root != null) { - // We were able to use the precompiled inflater, but now we need to do some work to - // attach the view to the root correctly. - XmlResourceParser parser = res.getLayout(resource); - try { - AttributeSet attrs = Xml.asAttributeSet(parser); - advanceToRootNode(parser); - ViewGroup.LayoutParams params = root.generateLayoutParams(attrs); - - if (attachToRoot) { - root.addView(view, params); - } else { - view.setLayoutParams(params); - } - } finally { - parser.close(); - } - } - - return view; - } catch (Throwable e) { - if (DEBUG) { - Log.e(TAG, "Failed to use precompiled view", e); - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIEW); - } - return null; - } - /** * Advances the given parser to the first START_TAG. Throws InflateException if no start tag is * found. @@ -1050,7 +926,7 @@ public abstract class LayoutInflater { * of the general view creation logic, and thus may return {@code null} for some tags. This * method is used by {@link LayoutInflater#inflate} in creating {@code View} objects. * - * @hide for use by precompiled layouts. + * @hide originally for internal use by precompiled layouts, which have since been removed. * * @param parent the parent view, used to inflate layout params * @param name the name of the XML tag used to define the view @@ -1217,85 +1093,80 @@ public abstract class LayoutInflater { + "reference. The layout ID " + value + " is not valid."); } - final View precompiled = tryInflatePrecompiled(layout, context.getResources(), - (ViewGroup) parent, /*attachToRoot=*/true); - if (precompiled == null) { - final XmlResourceParser childParser = context.getResources().getLayout(layout); - - try { - final AttributeSet childAttrs = Xml.asAttributeSet(childParser); - - while ((type = childParser.next()) != XmlPullParser.START_TAG && - type != XmlPullParser.END_DOCUMENT) { - // Empty. - } + final XmlResourceParser childParser = context.getResources().getLayout(layout); + try { + final AttributeSet childAttrs = Xml.asAttributeSet(childParser); - if (type != XmlPullParser.START_TAG) { - throw new InflateException(getParserStateDescription(context, childAttrs) - + ": No start tag found!"); - } + while ((type = childParser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + // Empty. + } - final String childName = childParser.getName(); + if (type != XmlPullParser.START_TAG) { + throw new InflateException(getParserStateDescription(context, childAttrs) + + ": No start tag found!"); + } - if (TAG_MERGE.equals(childName)) { - // The <merge> tag doesn't support android:theme, so - // nothing special to do here. - rInflate(childParser, parent, context, childAttrs, false); - } else { - final View view = createViewFromTag(parent, childName, - context, childAttrs, hasThemeOverride); - final ViewGroup group = (ViewGroup) parent; - - final TypedArray a = context.obtainStyledAttributes( - attrs, R.styleable.Include); - final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID); - final int visibility = a.getInt(R.styleable.Include_visibility, -1); - a.recycle(); - - // We try to load the layout params set in the <include /> tag. - // If the parent can't generate layout params (ex. missing width - // or height for the framework ViewGroups, though this is not - // necessarily true of all ViewGroups) then we expect it to throw - // a runtime exception. - // We catch this exception and set localParams accordingly: true - // means we successfully loaded layout params from the <include> - // tag, false means we need to rely on the included layout params. - ViewGroup.LayoutParams params = null; - try { - params = group.generateLayoutParams(attrs); - } catch (RuntimeException e) { - // Ignore, just fail over to child attrs. - } - if (params == null) { - params = group.generateLayoutParams(childAttrs); - } - view.setLayoutParams(params); + final String childName = childParser.getName(); - // Inflate all children. - rInflateChildren(childParser, view, childAttrs, true); + if (TAG_MERGE.equals(childName)) { + // The <merge> tag doesn't support android:theme, so + // nothing special to do here. + rInflate(childParser, parent, context, childAttrs, false); + } else { + final View view = + createViewFromTag(parent, childName, context, childAttrs, hasThemeOverride); + final ViewGroup group = (ViewGroup) parent; + + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Include); + final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID); + final int visibility = a.getInt(R.styleable.Include_visibility, -1); + a.recycle(); + + // We try to load the layout params set in the <include /> tag. + // If the parent can't generate layout params (ex. missing width + // or height for the framework ViewGroups, though this is not + // necessarily true of all ViewGroups) then we expect it to throw + // a runtime exception. + // We catch this exception and set localParams accordingly: true + // means we successfully loaded layout params from the <include> + // tag, false means we need to rely on the included layout params. + ViewGroup.LayoutParams params = null; + try { + params = group.generateLayoutParams(attrs); + } catch (RuntimeException e) { + // Ignore, just fail over to child attrs. + } + if (params == null) { + params = group.generateLayoutParams(childAttrs); + } + view.setLayoutParams(params); - if (id != View.NO_ID) { - view.setId(id); - } + // Inflate all children. + rInflateChildren(childParser, view, childAttrs, true); - switch (visibility) { - case 0: - view.setVisibility(View.VISIBLE); - break; - case 1: - view.setVisibility(View.INVISIBLE); - break; - case 2: - view.setVisibility(View.GONE); - break; - } + if (id != View.NO_ID) { + view.setId(id); + } - group.addView(view); + switch (visibility) { + case 0: + view.setVisibility(View.VISIBLE); + break; + case 1: + view.setVisibility(View.INVISIBLE); + break; + case 2: + view.setVisibility(View.GONE); + break; } - } finally { - childParser.close(); + + group.addView(view); } + } finally { + childParser.close(); } + LayoutInflater.consumeChildElements(parser); } diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java index 8bd2982d1ead..a10bae92aea5 100644 --- a/services/core/java/com/android/server/pm/DexOptHelper.java +++ b/services/core/java/com/android/server/pm/DexOptHelper.java @@ -223,11 +223,6 @@ public final class DexOptHelper { pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT; } - // TODO(b/251903639): Do this when ART Service is used, or remove it from here. - if (SystemProperties.getBoolean(mPm.PRECOMPILE_LAYOUTS, false)) { - mPm.mArtManagerService.compileLayouts(packageState, pkg); - } - int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0; String filter = getCompilerFilterForReason(pkgCompilationReason); diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index edb45aa0ffb4..88d894cd89d1 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -59,7 +59,6 @@ import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY; import static com.android.server.pm.PackageManagerService.MIN_INSTALLABLE_TARGET_SDK; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import static com.android.server.pm.PackageManagerService.POST_INSTALL; -import static com.android.server.pm.PackageManagerService.PRECOMPILE_LAYOUTS; import static com.android.server.pm.PackageManagerService.SCAN_AS_APEX; import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX; import static com.android.server.pm.PackageManagerService.SCAN_AS_FACTORY; @@ -135,7 +134,6 @@ import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.SELinux; -import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; @@ -167,7 +165,6 @@ import com.android.server.pm.Installer.LegacyDexoptDisabledException; import com.android.server.pm.dex.ArtManagerService; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; -import com.android.server.pm.dex.ViewCompiler; import com.android.server.pm.parsing.PackageCacher; import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; @@ -222,7 +219,6 @@ final class InstallPackageHelper { private final Context mContext; private final PackageDexOptimizer mPackageDexOptimizer; private final PackageAbiHelper mPackageAbiHelper; - private final ViewCompiler mViewCompiler; private final SharedLibrariesImpl mSharedLibraries; private final PackageManagerServiceInjector mInjector; private final UpdateOwnershipHelper mUpdateOwnershipHelper; @@ -246,7 +242,6 @@ final class InstallPackageHelper { mContext = pm.mInjector.getContext(); mPackageDexOptimizer = pm.mInjector.getPackageDexOptimizer(); mPackageAbiHelper = pm.mInjector.getAbiHelper(); - mViewCompiler = pm.mInjector.getViewCompiler(); mSharedLibraries = pm.mInjector.getSharedLibrariesImpl(); mUpdateOwnershipHelper = pm.mInjector.getUpdateOwnershipHelper(); } @@ -2538,13 +2533,6 @@ final class InstallPackageHelper { && !isApex; if (performDexopt) { - // Compile the layout resources. - if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts"); - mViewCompiler.compileLayouts(ps, pkg.getBaseApkPath()); - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); // This mirrors logic from commitReconciledScanResultLocked, where the library files diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 0ebd33b9cd81..4ed31636ad56 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -1128,14 +1128,6 @@ public class Installer extends SystemService { throw new InstallerException("Invalid instruction set: " + instructionSet); } - public boolean compileLayouts(String apkPath, String packageName, String outDexFile, int uid) { - try { - return mInstalld.compileLayouts(apkPath, packageName, outDexFile, uid); - } catch (RemoteException e) { - return false; - } - } - /** * Returns the visibility of the optimized artifacts. * diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 68aa93d28330..115b52b7e762 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -213,7 +213,6 @@ import com.android.server.pm.dex.ArtManagerService; import com.android.server.pm.dex.ArtUtils; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DynamicCodeLogger; -import com.android.server.pm.dex.ViewCompiler; import com.android.server.pm.local.PackageManagerLocalImpl; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.PackageParser2; @@ -810,8 +809,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService private final DexManager mDexManager; private final DynamicCodeLogger mDynamicCodeLogger; - final ViewCompiler mViewCompiler; - private final AtomicInteger mNextMoveId = new AtomicInteger(); final MovePackageHelper.MoveCallbacks mMoveCallbacks; @@ -1669,7 +1666,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService (i, pm) -> new ArtManagerService(i.getContext(), i.getInstaller(), i.getInstallLock()), (i, pm) -> ApexManager.getInstance(), - (i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()), (i, pm) -> (IncrementalManager) i.getContext().getSystemService(Context.INCREMENTAL_SERVICE), (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class), @@ -1880,7 +1876,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService mProcessLoggingHandler = testParams.processLoggingHandler; mProtectedPackages = testParams.protectedPackages; mSeparateProcesses = testParams.separateProcesses; - mViewCompiler = testParams.viewCompiler; mRequiredVerifierPackages = testParams.requiredVerifierPackages; mRequiredInstallerPackage = testParams.requiredInstallerPackage; mRequiredUninstallerPackage = testParams.requiredUninstallerPackage; @@ -2043,7 +2038,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService mBackgroundDexOptService = injector.getBackgroundDexOptService(); mArtManagerService = injector.getArtManagerService(); mMoveCallbacks = new MovePackageHelper.MoveCallbacks(FgThread.get().getLooper()); - mViewCompiler = injector.getViewCompiler(); mSharedLibraries = mInjector.getSharedLibrariesImpl(); mBackgroundHandler = injector.getBackgroundHandler(); diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java index 5b770aab19ca..ebf1c04bee12 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java @@ -32,7 +32,6 @@ import com.android.server.compat.PlatformCompat; import com.android.server.pm.dex.ArtManagerService; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DynamicCodeLogger; -import com.android.server.pm.dex.ViewCompiler; import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.permission.LegacyPermissionManagerInternal; import com.android.server.pm.permission.PermissionManagerServiceInternal; @@ -112,7 +111,6 @@ public class PackageManagerServiceInjector { private final Singleton<ArtManagerService> mArtManagerServiceProducer; private final Singleton<ApexManager> mApexManagerProducer; - private final Singleton<ViewCompiler> mViewCompilerProducer; private final Singleton<IncrementalManager> mIncrementalManagerProducer; private final Singleton<DefaultAppProvider> @@ -164,7 +162,6 @@ public class PackageManagerServiceInjector { Producer<DynamicCodeLogger> dynamicCodeLoggerProducer, Producer<ArtManagerService> artManagerServiceProducer, Producer<ApexManager> apexManagerProducer, - Producer<ViewCompiler> viewCompilerProducer, Producer<IncrementalManager> incrementalManagerProducer, Producer<DefaultAppProvider> defaultAppProviderProducer, Producer<DisplayMetrics> displayMetricsProducer, @@ -214,7 +211,6 @@ public class PackageManagerServiceInjector { mArtManagerServiceProducer = new Singleton<>( artManagerServiceProducer); mApexManagerProducer = new Singleton<>(apexManagerProducer); - mViewCompilerProducer = new Singleton<>(viewCompilerProducer); mIncrementalManagerProducer = new Singleton<>( incrementalManagerProducer); mDefaultAppProviderProducer = new Singleton<>( @@ -339,10 +335,6 @@ public class PackageManagerServiceInjector { return mApexManagerProducer.get(this, mPackageManager); } - public ViewCompiler getViewCompiler() { - return mViewCompilerProducer.get(this, mPackageManager); - } - public Handler getBackgroundHandler() { return mBackgroundHandler; } diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java index ca572091486e..9428ef6c8ba9 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java @@ -34,7 +34,6 @@ import com.android.internal.content.om.OverlayConfig; import com.android.server.pm.dex.ArtManagerService; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DynamicCodeLogger; -import com.android.server.pm.dex.ViewCompiler; import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.permission.LegacyPermissionManagerInternal; import com.android.server.pm.pkg.AndroidPackage; @@ -93,7 +92,6 @@ public final class PackageManagerServiceTestParams { public @Nullable String systemTextClassifierPackage; public @Nullable String overlayConfigSignaturePackage; public @NonNull String requiredSdkSandboxPackage; - public ViewCompiler viewCompiler; public @Nullable String retailDemoPackage; public @Nullable String recentsPackage; public @Nullable String ambientContextDetectionPackage; diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index f4f03f4c9c4e..ae47aa823245 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -539,44 +539,6 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { } } - /** - * Compile layout resources in a given package. - */ - public boolean compileLayouts(@NonNull PackageStateInternal ps, @NonNull AndroidPackage pkg) { - try { - if (ps.isPrivileged() || pkg.isUseEmbeddedDex() - || pkg.isDefaultToDeviceProtectedStorage()) { - // Privileged apps prefer to load trusted code so they don't use compiled views. - // If the app is not privileged but prefers code integrity, also avoid compiling - // views. - // Also disable the view compiler for protected storage apps since there are - // selinux permissions required for writing to user_de. - return false; - } - final String packageName = pkg.getPackageName(); - final String apkPath = pkg.getSplits().get(0).getPath(); - final File dataDir = PackageInfoUtils.getDataDir(ps, UserHandle.myUserId()); - if (dataDir == null) { - // The app is not installed on the target user and doesn't have a data dir - return false; - } - final String outDexFile = dataDir.getAbsolutePath() + "/code_cache/compiled_view.dex"; - Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath + - ") to " + outDexFile); - final long callingId = Binder.clearCallingIdentity(); - try { - return mInstaller.compileLayouts(apkPath, packageName, outDexFile, - pkg.getUid()); - } finally { - Binder.restoreCallingIdentity(callingId); - } - } - catch (Throwable e) { - Log.e("PackageManager", "Failed to compile layouts", e); - return false; - } - } - /** * Build the profiles names for all the package code paths (excluding resource only paths). * Return the map [code path -> profile name]. diff --git a/services/core/java/com/android/server/pm/dex/ViewCompiler.java b/services/core/java/com/android/server/pm/dex/ViewCompiler.java deleted file mode 100644 index 0026922492a5..000000000000 --- a/services/core/java/com/android/server/pm/dex/ViewCompiler.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 - */ - -package com.android.server.pm.dex; - -import android.os.Binder; -import android.os.UserHandle; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; -import com.android.server.pm.Installer; -import com.android.server.pm.parsing.PackageInfoUtils; -import com.android.server.pm.pkg.PackageStateInternal; - -import java.io.File; - -public class ViewCompiler { - private final Object mInstallLock; - @GuardedBy("mInstallLock") - private final Installer mInstaller; - - public ViewCompiler(Object installLock, Installer installer) { - mInstallLock = installLock; - mInstaller = installer; - } - - public boolean compileLayouts(PackageStateInternal ps, String apkPath) { - try { - final String packageName = ps.getPackageName(); - File dataDir = PackageInfoUtils.getDataDir(ps, UserHandle.myUserId()); - if (dataDir == null) { - // The app is not installed on the target user and doesn't have a data dir - return false; - } - final String outDexFile = dataDir.getAbsolutePath() + "/code_cache/compiled_view.dex"; - Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath + - ") to " + outDexFile); - final long callingId = Binder.clearCallingIdentity(); - try { - synchronized (mInstallLock) { - return mInstaller.compileLayouts(apkPath, packageName, outDexFile, - ps.getAppId()); - } - } finally { - Binder.restoreCallingIdentity(callingId); - } - } catch (Throwable e) { - Log.e("PackageManager", "Failed to compile layouts", e); - return false; - } - } -} diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp deleted file mode 100644 index e17209088750..000000000000 --- a/startop/view_compiler/Android.bp +++ /dev/null @@ -1,115 +0,0 @@ -// -// Copyright (C) 2018 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 { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -cc_defaults { - name: "viewcompiler_defaults", - header_libs: [ - "libbase_headers", - ], - shared_libs: [ - "libbase", - "slicer", - ], - static_libs: [ - "libcutils", - "libtinyxml2", - "liblog", - "libutils", - "libziparchive", - "libz", - ], - cpp_std: "gnu++2b", - target: { - android: { - shared_libs: [ - "libandroidfw", - ], - }, - host: { - static_libs: [ - "libandroidfw", - ], - }, - }, -} - -cc_library_static { - name: "libviewcompiler", - defaults: ["viewcompiler_defaults"], - srcs: [ - "apk_layout_compiler.cc", - "dex_builder.cc", - "dex_layout_compiler.cc", - "java_lang_builder.cc", - "tinyxml_layout_parser.cc", - "util.cc", - "layout_validation.cc", - ], - host_supported: true, -} - -cc_binary { - name: "viewcompiler", - defaults: ["viewcompiler_defaults"], - srcs: [ - "main.cc", - ], - static_libs: [ - "libgflags", - "libviewcompiler", - ], - host_supported: true, -} - -cc_test_host { - name: "view-compiler-tests", - defaults: ["viewcompiler_defaults"], - srcs: [ - "layout_validation_test.cc", - "util_test.cc", - ], - static_libs: [ - "libviewcompiler", - ], -} - -cc_binary_host { - name: "dex_testcase_generator", - defaults: ["viewcompiler_defaults"], - srcs: ["dex_testcase_generator.cc"], - static_libs: [ - "libviewcompiler", - ], -} - -genrule { - name: "generate_dex_testcases", - tools: [":dex_testcase_generator"], - cmd: "$(location :dex_testcase_generator) $(genDir)", - out: [ - "simple.dex", - "trivial.dex", - ], -} diff --git a/startop/view_compiler/OWNERS b/startop/view_compiler/OWNERS deleted file mode 100644 index e5aead9ddac8..000000000000 --- a/startop/view_compiler/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -eholk@google.com -mathieuc@google.com diff --git a/startop/view_compiler/README.md b/startop/view_compiler/README.md deleted file mode 100644 index f8da02b53907..000000000000 --- a/startop/view_compiler/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# View Compiler - -This directory contains an experimental compiler for layout files. - -It will take a layout XML file and produce a CompiledLayout.java file with a -specialized layout inflation function. - -To use it, let's assume you had a layout in `my_layout.xml` and your app was in -the Java language package `com.example.myapp`. Run the following command: - - viewcompiler my_layout.xml --package com.example.myapp --out CompiledView.java - -This will produce a `CompiledView.java`, which can then be compiled into your -Android app. Then to use it, in places where you would have inflated -`R.layouts.my_layout`, instead call `CompiledView.inflate`. - -Precompiling views like this generally improves the time needed to inflate them. - -This tool is still in its early stages and has a number of limitations. -* Currently only one layout can be compiled at a time. -* `merge` and `include` nodes are not supported. -* View compilation is a manual process that requires code changes in the - application. -* This only works for apps that do not use a custom layout inflater. -* Other limitations yet to be discovered. - -## DexBuilder Tests - -The DexBuilder has several low-level end to end tests to verify generated DEX -code validates, runs, and has the correct behavior. There are, unfortunately, a -number of pieces that must be added to generate new tests. Here are the -components: - -* `dex_testcase_generator` - Written in C++ using `DexBuilder`. This runs as a - build step produce the DEX files that will be tested on device. See the - `genrule` named `generate_dex_testcases` in `Android.bp`. These files are then - copied over to the device by TradeFed when running tests. -* `DexBuilderTest` - This is a Java Language test harness that loads the - generated DEX files and exercises methods in the file. - -To add a new DEX file test, follow these steps: -1. Modify `dex_testcase_generator` to produce the DEX file. -2. Add the filename to the `out` list of the `generate_dex_testcases` rule in - `Android.bp`. -3. Add a new `push` option to `AndroidTest.xml` to copy the DEX file to the - device. -4. Modify `DexBuilderTest.java` to load and exercise the new test. - -In each case, you should be able to cargo-cult the existing test cases. - -In general, you can probably get by without adding a new generated DEX file, and -instead add more methods to the files that are already generated. In this case, -you can skip all of steps 2 and 3 above, and simplify steps 1 and 4. diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc deleted file mode 100644 index 5f5652c2acac..000000000000 --- a/startop/view_compiler/apk_layout_compiler.cc +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#include "apk_layout_compiler.h" -#include "dex_layout_compiler.h" -#include "java_lang_builder.h" -#include "layout_validation.h" -#include "util.h" - -#include "androidfw/ApkAssets.h" -#include "androidfw/AssetManager2.h" -#include "androidfw/ResourceTypes.h" - -#include <iostream> -#include <locale> - -#include "android-base/stringprintf.h" - -namespace startop { - -using android::ResXMLParser; -using android::base::StringPrintf; - -class ResXmlVisitorAdapter { - public: - ResXmlVisitorAdapter(ResXMLParser* parser) : parser_{parser} {} - - template <typename Visitor> - void Accept(Visitor* visitor) { - size_t depth{0}; - do { - switch (parser_->next()) { - case ResXMLParser::START_DOCUMENT: - depth++; - visitor->VisitStartDocument(); - break; - case ResXMLParser::END_DOCUMENT: - depth--; - visitor->VisitEndDocument(); - break; - case ResXMLParser::START_TAG: { - depth++; - size_t name_length = 0; - const char16_t* name = parser_->getElementName(&name_length); - visitor->VisitStartTag(std::u16string{name, name_length}); - break; - } - case ResXMLParser::END_TAG: - depth--; - visitor->VisitEndTag(); - break; - default:; - } - } while (depth > 0 || parser_->getEventType() == ResXMLParser::FIRST_CHUNK_CODE); - } - - private: - ResXMLParser* parser_; -}; - -bool CanCompileLayout(ResXMLParser* parser) { - ResXmlVisitorAdapter adapter{parser}; - LayoutValidationVisitor visitor; - adapter.Accept(&visitor); - - return visitor.can_compile(); -} - -namespace { -void CompileApkAssetsLayouts(const android::ApkAssetsPtr& assets, CompilationTarget target, - std::ostream& target_out) { - android::AssetManager2 resources; - resources.SetApkAssets({assets}); - - std::string package_name; - - // TODO: handle multiple packages better - bool first = true; - for (const auto& package : assets->GetLoadedArsc()->GetPackages()) { - CHECK(first); - package_name = package->GetPackageName(); - first = false; - } - - dex::DexBuilder dex_file; - dex::ClassBuilder compiled_view{ - dex_file.MakeClass(StringPrintf("%s.CompiledView", package_name.c_str()))}; - std::vector<dex::MethodBuilder> methods; - - assets->GetAssetsProvider()->ForEachFile("res/", [&](android::StringPiece s, android::FileType) { - if (s == "layout") { - auto path = StringPrintf("res/%.*s/", (int)s.size(), s.data()); - assets->GetAssetsProvider() - ->ForEachFile(path, [&](android::StringPiece layout_file, android::FileType) { - auto layout_path = StringPrintf("%s%.*s", path.c_str(), - (int)layout_file.size(), layout_file.data()); - android::ApkAssetsCookie cookie = android::kInvalidCookie; - auto asset = resources.OpenNonAsset(layout_path, - android::Asset::ACCESS_RANDOM, &cookie); - CHECK(asset); - CHECK(android::kInvalidCookie != cookie); - const auto dynamic_ref_table = resources.GetDynamicRefTableForCookie(cookie); - CHECK(nullptr != dynamic_ref_table); - android::ResXMLTree xml_tree{dynamic_ref_table}; - xml_tree.setTo(asset->getBuffer(/*wordAligned=*/true), asset->getLength(), - /*copy_data=*/true); - android::ResXMLParser parser{xml_tree}; - parser.restart(); - if (CanCompileLayout(&parser)) { - parser.restart(); - const std::string layout_name = - startop::util::FindLayoutNameFromFilename(layout_path); - ResXmlVisitorAdapter adapter{&parser}; - switch (target) { - case CompilationTarget::kDex: { - methods.push_back(compiled_view.CreateMethod( - layout_name, - dex::Prototype{dex::TypeDescriptor::FromClassname( - "android.view.View"), - dex::TypeDescriptor::FromClassname( - "android.content.Context"), - dex::TypeDescriptor::Int()})); - DexViewBuilder builder(&methods.back()); - builder.Start(); - LayoutCompilerVisitor visitor{&builder}; - adapter.Accept(&visitor); - builder.Finish(); - methods.back().Encode(); - break; - } - case CompilationTarget::kJavaLanguage: { - JavaLangViewBuilder builder{package_name, layout_name, - target_out}; - builder.Start(); - LayoutCompilerVisitor visitor{&builder}; - adapter.Accept(&visitor); - builder.Finish(); - break; - } - } - } - }); - } - }); - - if (target == CompilationTarget::kDex) { - slicer::MemView image{dex_file.CreateImage()}; - target_out.write(image.ptr<const char>(), image.size()); - } -} -} // namespace - -void CompileApkLayouts(const std::string& filename, CompilationTarget target, - std::ostream& target_out) { - auto assets = android::ApkAssets::Load(filename); - CompileApkAssetsLayouts(assets, target, target_out); -} - -void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target, - std::ostream& target_out) { - constexpr const char* friendly_name{"viewcompiler assets"}; - auto assets = android::ApkAssets::LoadFromFd(std::move(fd), friendly_name); - CompileApkAssetsLayouts(assets, target, target_out); -} - -} // namespace startop diff --git a/startop/view_compiler/apk_layout_compiler.h b/startop/view_compiler/apk_layout_compiler.h deleted file mode 100644 index 03bd545d9121..000000000000 --- a/startop/view_compiler/apk_layout_compiler.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#ifndef APK_LAYOUT_COMPILER_H_ -#define APK_LAYOUT_COMPILER_H_ - -#include <string> - -#include "android-base/unique_fd.h" - -namespace startop { - -enum class CompilationTarget { kJavaLanguage, kDex }; - -void CompileApkLayouts(const std::string& filename, CompilationTarget target, - std::ostream& target_out); -void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target, - std::ostream& target_out); - -} // namespace startop - -#endif // APK_LAYOUT_COMPILER_H_ \ No newline at end of file diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc deleted file mode 100644 index 50cf5a50d7a8..000000000000 --- a/startop/view_compiler/dex_builder.cc +++ /dev/null @@ -1,704 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#include "dex_builder.h" - -#include <fstream> -#include <memory> - -namespace startop { -namespace dex { - -using std::shared_ptr; -using std::string; - -using ::dex::kAccPublic; -using Op = Instruction::Op; - -const TypeDescriptor TypeDescriptor::Int() { return TypeDescriptor{"I"}; }; -const TypeDescriptor TypeDescriptor::Void() { return TypeDescriptor{"V"}; }; - -namespace { -// From https://source.android.com/devices/tech/dalvik/dex-format#dex-file-magic -constexpr uint8_t kDexFileMagic[]{0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x38, 0x00}; - -// Strings lengths can be 32 bits long, but encoded as LEB128 this can take up to five bytes. -constexpr size_t kMaxEncodedStringLength{5}; - -// Converts invoke-* to invoke-*/range -constexpr ::dex::Opcode InvokeToInvokeRange(::dex::Opcode opcode) { - switch (opcode) { - case ::dex::Opcode::OP_INVOKE_VIRTUAL: - return ::dex::Opcode::OP_INVOKE_VIRTUAL_RANGE; - case ::dex::Opcode::OP_INVOKE_DIRECT: - return ::dex::Opcode::OP_INVOKE_DIRECT_RANGE; - case ::dex::Opcode::OP_INVOKE_STATIC: - return ::dex::Opcode::OP_INVOKE_STATIC_RANGE; - case ::dex::Opcode::OP_INVOKE_INTERFACE: - return ::dex::Opcode::OP_INVOKE_INTERFACE_RANGE; - default: - LOG(FATAL) << opcode << " is not a recognized invoke opcode."; - __builtin_unreachable(); - } -} - -std::string DotToDescriptor(const char* class_name) { - std::string descriptor(class_name); - std::replace(descriptor.begin(), descriptor.end(), '.', '/'); - if (descriptor.length() > 0 && descriptor[0] != '[') { - descriptor = "L" + descriptor + ";"; - } - return descriptor; -} - -} // namespace - -std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) { - switch (opcode) { - case Instruction::Op::kReturn: - out << "kReturn"; - return out; - case Instruction::Op::kReturnObject: - out << "kReturnObject"; - return out; - case Instruction::Op::kMove: - out << "kMove"; - return out; - case Instruction::Op::kMoveObject: - out << "kMoveObject"; - return out; - case Instruction::Op::kInvokeVirtual: - out << "kInvokeVirtual"; - return out; - case Instruction::Op::kInvokeDirect: - out << "kInvokeDirect"; - return out; - case Instruction::Op::kInvokeStatic: - out << "kInvokeStatic"; - return out; - case Instruction::Op::kInvokeInterface: - out << "kInvokeInterface"; - return out; - case Instruction::Op::kBindLabel: - out << "kBindLabel"; - return out; - case Instruction::Op::kBranchEqz: - out << "kBranchEqz"; - return out; - case Instruction::Op::kBranchNEqz: - out << "kBranchNEqz"; - return out; - case Instruction::Op::kNew: - out << "kNew"; - return out; - case Instruction::Op::kCheckCast: - out << "kCheckCast"; - return out; - case Instruction::Op::kGetStaticField: - out << "kGetStaticField"; - return out; - case Instruction::Op::kSetStaticField: - out << "kSetStaticField"; - return out; - case Instruction::Op::kGetInstanceField: - out << "kGetInstanceField"; - return out; - case Instruction::Op::kSetInstanceField: - out << "kSetInstanceField"; - return out; - } -} - -std::ostream& operator<<(std::ostream& out, const Value& value) { - if (value.is_register()) { - out << "Register(" << value.value() << ")"; - } else if (value.is_parameter()) { - out << "Parameter(" << value.value() << ")"; - } else if (value.is_immediate()) { - out << "Immediate(" << value.value() << ")"; - } else if (value.is_string()) { - out << "String(" << value.value() << ")"; - } else if (value.is_label()) { - out << "Label(" << value.value() << ")"; - } else if (value.is_type()) { - out << "Type(" << value.value() << ")"; - } else { - out << "UnknownValue"; - } - return out; -} - -void* TrackingAllocator::Allocate(size_t size) { - std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(size); - void* raw_buffer = buffer.get(); - allocations_[raw_buffer] = std::move(buffer); - return raw_buffer; -} - -void TrackingAllocator::Free(void* ptr) { allocations_.erase(allocations_.find(ptr)); } - -// Write out a DEX file that is basically: -// -// package dextest; -// public class DexTest { -// public static int foo(String s) { return s.length(); } -// } -void WriteTestDexFile(const string& filename) { - DexBuilder dex_file; - - ClassBuilder cbuilder{dex_file.MakeClass("dextest.DexTest")}; - cbuilder.set_source_file("dextest.java"); - - TypeDescriptor string_type = TypeDescriptor::FromClassname("java.lang.String"); - - MethodBuilder method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int(), string_type})}; - - LiveRegister result = method.AllocRegister(); - - MethodDeclData string_length = - dex_file.GetOrDeclareMethod(string_type, "length", Prototype{TypeDescriptor::Int()}); - - method.AddInstruction(Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0))); - method.BuildReturn(result); - - method.Encode(); - - slicer::MemView image{dex_file.CreateImage()}; - - std::ofstream out_file(filename); - out_file.write(image.ptr<const char>(), image.size()); -} - -TypeDescriptor TypeDescriptor::FromClassname(const std::string& name) { - return TypeDescriptor{DotToDescriptor(name.c_str())}; -} - -DexBuilder::DexBuilder() : dex_file_{std::make_shared<ir::DexFile>()} { - dex_file_->magic = slicer::MemView{kDexFileMagic, sizeof(kDexFileMagic)}; -} - -slicer::MemView DexBuilder::CreateImage() { - ::dex::Writer writer(dex_file_); - size_t image_size{0}; - ::dex::u1* image = writer.CreateImage(&allocator_, &image_size); - return slicer::MemView{image, image_size}; -} - -ir::String* DexBuilder::GetOrAddString(const std::string& string) { - ir::String*& entry = strings_[string]; - - if (entry == nullptr) { - // Need to encode the length and then write out the bytes, including 1 byte for null terminator - auto buffer = std::make_unique<uint8_t[]>(string.size() + kMaxEncodedStringLength + 1); - uint8_t* string_data_start = ::dex::WriteULeb128(buffer.get(), string.size()); - - size_t header_length = - reinterpret_cast<uintptr_t>(string_data_start) - reinterpret_cast<uintptr_t>(buffer.get()); - - auto end = std::copy(string.begin(), string.end(), string_data_start); - *end = '\0'; - - entry = Alloc<ir::String>(); - // +1 for null terminator - entry->data = slicer::MemView{buffer.get(), header_length + string.size() + 1}; - ::dex::u4 const new_index = dex_file_->strings_indexes.AllocateIndex(); - dex_file_->strings_map[new_index] = entry; - entry->orig_index = new_index; - string_data_.push_back(std::move(buffer)); - } - return entry; -} - -ClassBuilder DexBuilder::MakeClass(const std::string& name) { - auto* class_def = Alloc<ir::Class>(); - ir::Type* type_def = GetOrAddType(DotToDescriptor(name.c_str())); - type_def->class_def = class_def; - - class_def->type = type_def; - class_def->super_class = GetOrAddType(DotToDescriptor("java.lang.Object")); - class_def->access_flags = kAccPublic; - return ClassBuilder{this, name, class_def}; -} - -ir::Type* DexBuilder::GetOrAddType(const std::string& descriptor) { - if (types_by_descriptor_.find(descriptor) != types_by_descriptor_.end()) { - return types_by_descriptor_[descriptor]; - } - - ir::Type* type = Alloc<ir::Type>(); - type->descriptor = GetOrAddString(descriptor); - types_by_descriptor_[descriptor] = type; - type->orig_index = dex_file_->types_indexes.AllocateIndex(); - dex_file_->types_map[type->orig_index] = type; - return type; -} - -ir::FieldDecl* DexBuilder::GetOrAddField(TypeDescriptor parent, const std::string& name, - TypeDescriptor type) { - const auto key = std::make_tuple(parent, name); - if (field_decls_by_key_.find(key) != field_decls_by_key_.end()) { - return field_decls_by_key_[key]; - } - - ir::FieldDecl* field = Alloc<ir::FieldDecl>(); - field->parent = GetOrAddType(parent); - field->name = GetOrAddString(name); - field->type = GetOrAddType(type); - field->orig_index = dex_file_->fields_indexes.AllocateIndex(); - dex_file_->fields_map[field->orig_index] = field; - field_decls_by_key_[key] = field; - return field; -} - -ir::Proto* Prototype::Encode(DexBuilder* dex) const { - auto* proto = dex->Alloc<ir::Proto>(); - proto->shorty = dex->GetOrAddString(Shorty()); - proto->return_type = dex->GetOrAddType(return_type_.descriptor()); - if (param_types_.size() > 0) { - proto->param_types = dex->Alloc<ir::TypeList>(); - for (const auto& param_type : param_types_) { - proto->param_types->types.push_back(dex->GetOrAddType(param_type.descriptor())); - } - } else { - proto->param_types = nullptr; - } - return proto; -} - -std::string Prototype::Shorty() const { - std::string shorty; - shorty.append(return_type_.short_descriptor()); - for (const auto& type_descriptor : param_types_) { - shorty.append(type_descriptor.short_descriptor()); - } - return shorty; -} - -const TypeDescriptor& Prototype::ArgType(size_t index) const { - CHECK_LT(index, param_types_.size()); - return param_types_[index]; -} - -ClassBuilder::ClassBuilder(DexBuilder* parent, const std::string& name, ir::Class* class_def) - : parent_(parent), type_descriptor_{TypeDescriptor::FromClassname(name)}, class_(class_def) {} - -MethodBuilder ClassBuilder::CreateMethod(const std::string& name, Prototype prototype) { - ir::MethodDecl* decl = parent_->GetOrDeclareMethod(type_descriptor_, name, prototype).decl; - - return MethodBuilder{parent_, class_, decl}; -} - -void ClassBuilder::set_source_file(const string& source) { - class_->source_file = parent_->GetOrAddString(source); -} - -MethodBuilder::MethodBuilder(DexBuilder* dex, ir::Class* class_def, ir::MethodDecl* decl) - : dex_{dex}, class_{class_def}, decl_{decl} {} - -ir::EncodedMethod* MethodBuilder::Encode() { - auto* method = dex_->Alloc<ir::EncodedMethod>(); - method->decl = decl_; - - // TODO: make access flags configurable - method->access_flags = kAccPublic | ::dex::kAccStatic; - - auto* code = dex_->Alloc<ir::Code>(); - CHECK(decl_->prototype != nullptr); - size_t const num_args = - decl_->prototype->param_types != nullptr ? decl_->prototype->param_types->types.size() : 0; - code->registers = NumRegisters() + num_args + kMaxScratchRegisters; - code->ins_count = num_args; - EncodeInstructions(); - code->instructions = slicer::ArrayView<const ::dex::u2>(buffer_.data(), buffer_.size()); - size_t const return_count = decl_->prototype->return_type == dex_->GetOrAddType("V") ? 0 : 1; - code->outs_count = std::max(return_count, max_args_); - method->code = code; - - class_->direct_methods.push_back(method); - - return method; -} - -LiveRegister MethodBuilder::AllocRegister() { - // Find a free register - for (size_t i = 0; i < register_liveness_.size(); ++i) { - if (!register_liveness_[i]) { - register_liveness_[i] = true; - return LiveRegister{®ister_liveness_, i}; - } - } - - // If we get here, all the registers are in use, so we have to allocate a new - // one. - register_liveness_.push_back(true); - return LiveRegister{®ister_liveness_, register_liveness_.size() - 1}; -} - -Value MethodBuilder::MakeLabel() { - labels_.push_back({}); - return Value::Label(labels_.size() - 1); -} - -void MethodBuilder::AddInstruction(Instruction instruction) { - instructions_.push_back(instruction); -} - -void MethodBuilder::BuildReturn() { AddInstruction(Instruction::OpNoArgs(Op::kReturn)); } - -void MethodBuilder::BuildReturn(Value src, bool is_object) { - AddInstruction(Instruction::OpWithArgs( - is_object ? Op::kReturnObject : Op::kReturn, /*destination=*/{}, src)); -} - -void MethodBuilder::BuildConst4(Value target, int value) { - CHECK_LT(value, 16); - AddInstruction(Instruction::OpWithArgs(Op::kMove, target, Value::Immediate(value))); -} - -void MethodBuilder::BuildConstString(Value target, const std::string& value) { - const ir::String* const dex_string = dex_->GetOrAddString(value); - AddInstruction(Instruction::OpWithArgs(Op::kMove, target, Value::String(dex_string->orig_index))); -} - -void MethodBuilder::EncodeInstructions() { - buffer_.clear(); - for (const auto& instruction : instructions_) { - EncodeInstruction(instruction); - } -} - -void MethodBuilder::EncodeInstruction(const Instruction& instruction) { - switch (instruction.opcode()) { - case Instruction::Op::kReturn: - return EncodeReturn(instruction, ::dex::Opcode::OP_RETURN); - case Instruction::Op::kReturnObject: - return EncodeReturn(instruction, ::dex::Opcode::OP_RETURN_OBJECT); - case Instruction::Op::kMove: - case Instruction::Op::kMoveObject: - return EncodeMove(instruction); - case Instruction::Op::kInvokeVirtual: - return EncodeInvoke(instruction, ::dex::Opcode::OP_INVOKE_VIRTUAL); - case Instruction::Op::kInvokeDirect: - return EncodeInvoke(instruction, ::dex::Opcode::OP_INVOKE_DIRECT); - case Instruction::Op::kInvokeStatic: - return EncodeInvoke(instruction, ::dex::Opcode::OP_INVOKE_STATIC); - case Instruction::Op::kInvokeInterface: - return EncodeInvoke(instruction, ::dex::Opcode::OP_INVOKE_INTERFACE); - case Instruction::Op::kBindLabel: - return BindLabel(instruction.args()[0]); - case Instruction::Op::kBranchEqz: - return EncodeBranch(::dex::Opcode::OP_IF_EQZ, instruction); - case Instruction::Op::kBranchNEqz: - return EncodeBranch(::dex::Opcode::OP_IF_NEZ, instruction); - case Instruction::Op::kNew: - return EncodeNew(instruction); - case Instruction::Op::kCheckCast: - return EncodeCast(instruction); - case Instruction::Op::kGetStaticField: - case Instruction::Op::kSetStaticField: - case Instruction::Op::kGetInstanceField: - case Instruction::Op::kSetInstanceField: - return EncodeFieldOp(instruction); - } -} - -void MethodBuilder::EncodeReturn(const Instruction& instruction, ::dex::Opcode opcode) { - CHECK(!instruction.dest().has_value()); - if (instruction.args().size() == 0) { - Encode10x(::dex::Opcode::OP_RETURN_VOID); - } else { - CHECK_EQ(1, instruction.args().size()); - size_t source = RegisterValue(instruction.args()[0]); - Encode11x(opcode, source); - } -} - -void MethodBuilder::EncodeMove(const Instruction& instruction) { - CHECK(Instruction::Op::kMove == instruction.opcode() || - Instruction::Op::kMoveObject == instruction.opcode()); - CHECK(instruction.dest().has_value()); - CHECK(instruction.dest()->is_variable()); - CHECK_EQ(1, instruction.args().size()); - - const Value& source = instruction.args()[0]; - - if (source.is_immediate()) { - // TODO: support more registers - CHECK_LT(RegisterValue(*instruction.dest()), 16); - Encode11n(::dex::Opcode::OP_CONST_4, RegisterValue(*instruction.dest()), source.value()); - } else if (source.is_string()) { - constexpr size_t kMaxRegisters = 256; - CHECK_LT(RegisterValue(*instruction.dest()), kMaxRegisters); - CHECK_LT(source.value(), 65536); // make sure we don't need a jumbo string - Encode21c(::dex::Opcode::OP_CONST_STRING, RegisterValue(*instruction.dest()), source.value()); - } else if (source.is_variable()) { - // For the moment, we only use this when we need to reshuffle registers for - // an invoke instruction, meaning we are too big for the 4-bit version. - // We'll err on the side of caution and always generate the 16-bit form of - // the instruction. - auto opcode = instruction.opcode() == Instruction::Op::kMove - ? ::dex::Opcode::OP_MOVE_16 - : ::dex::Opcode::OP_MOVE_OBJECT_16; - Encode32x(opcode, RegisterValue(*instruction.dest()), RegisterValue(source)); - } else { - UNIMPLEMENTED(FATAL); - } -} - -void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::dex::Opcode opcode) { - constexpr size_t kMaxArgs = 5; - - // Currently, we only support up to 5 arguments. - CHECK_LE(instruction.args().size(), kMaxArgs); - - uint8_t arguments[kMaxArgs]{}; - bool has_long_args = false; - for (size_t i = 0; i < instruction.args().size(); ++i) { - CHECK(instruction.args()[i].is_variable()); - arguments[i] = RegisterValue(instruction.args()[i]); - if (!IsShortRegister(arguments[i])) { - has_long_args = true; - } - } - - if (has_long_args) { - // Some of the registers don't fit in the four bit short form of the invoke - // instruction, so we need to do an invoke/range. To do this, we need to - // first move all the arguments into contiguous temporary registers. - std::array<Value, kMaxArgs> scratch = GetScratchRegisters<kMaxArgs>(); - - const auto& prototype = dex_->GetPrototypeByMethodId(instruction.index_argument()); - CHECK(prototype.has_value()); - - for (size_t i = 0; i < instruction.args().size(); ++i) { - Instruction::Op move_op; - if (opcode == ::dex::Opcode::OP_INVOKE_VIRTUAL || - opcode == ::dex::Opcode::OP_INVOKE_DIRECT) { - // In this case, there is an implicit `this` argument, which is always an object. - if (i == 0) { - move_op = Instruction::Op::kMoveObject; - } else { - move_op = prototype->ArgType(i - 1).is_object() ? Instruction::Op::kMoveObject - : Instruction::Op::kMove; - } - } else { - move_op = prototype->ArgType(i).is_object() ? Instruction::Op::kMoveObject - : Instruction::Op::kMove; - } - - EncodeMove(Instruction::OpWithArgs(move_op, scratch[i], instruction.args()[i])); - } - - Encode3rc(InvokeToInvokeRange(opcode), - instruction.args().size(), - instruction.index_argument(), - RegisterValue(scratch[0])); - } else { - Encode35c(opcode, - instruction.args().size(), - instruction.index_argument(), - arguments[0], - arguments[1], - arguments[2], - arguments[3], - arguments[4]); - } - - // If there is a return value, add a move-result instruction - if (instruction.dest().has_value()) { - Encode11x(instruction.result_is_object() ? ::dex::Opcode::OP_MOVE_RESULT_OBJECT - : ::dex::Opcode::OP_MOVE_RESULT, - RegisterValue(*instruction.dest())); - } - - max_args_ = std::max(max_args_, instruction.args().size()); -} - -// Encodes a conditional branch that tests a single argument. -void MethodBuilder::EncodeBranch(::dex::Opcode op, const Instruction& instruction) { - const auto& args = instruction.args(); - const auto& test_value = args[0]; - const auto& branch_target = args[1]; - CHECK_EQ(2, args.size()); - CHECK(test_value.is_variable()); - CHECK(branch_target.is_label()); - - size_t instruction_offset = buffer_.size(); - size_t field_offset = buffer_.size() + 1; - Encode21c( - op, RegisterValue(test_value), LabelValue(branch_target, instruction_offset, field_offset)); -} - -void MethodBuilder::EncodeNew(const Instruction& instruction) { - CHECK_EQ(Instruction::Op::kNew, instruction.opcode()); - CHECK(instruction.dest().has_value()); - CHECK(instruction.dest()->is_variable()); - CHECK_EQ(1, instruction.args().size()); - - const Value& type = instruction.args()[0]; - CHECK_LT(RegisterValue(*instruction.dest()), 256); - CHECK(type.is_type()); - Encode21c(::dex::Opcode::OP_NEW_INSTANCE, RegisterValue(*instruction.dest()), type.value()); -} - -void MethodBuilder::EncodeCast(const Instruction& instruction) { - CHECK_EQ(Instruction::Op::kCheckCast, instruction.opcode()); - CHECK(instruction.dest().has_value()); - CHECK(instruction.dest()->is_variable()); - CHECK_EQ(1, instruction.args().size()); - - const Value& type = instruction.args()[0]; - CHECK_LT(RegisterValue(*instruction.dest()), 256); - CHECK(type.is_type()); - Encode21c(::dex::Opcode::OP_CHECK_CAST, RegisterValue(*instruction.dest()), type.value()); -} - -void MethodBuilder::EncodeFieldOp(const Instruction& instruction) { - const auto& args = instruction.args(); - switch (instruction.opcode()) { - case Instruction::Op::kGetStaticField: { - CHECK(instruction.dest().has_value()); - CHECK(instruction.dest()->is_variable()); - CHECK_EQ(0, instruction.args().size()); - - Encode21c(::dex::Opcode::OP_SGET, - RegisterValue(*instruction.dest()), - instruction.index_argument()); - break; - } - case Instruction::Op::kSetStaticField: { - CHECK(!instruction.dest().has_value()); - CHECK_EQ(1, args.size()); - CHECK(args[0].is_variable()); - - Encode21c(::dex::Opcode::OP_SPUT, RegisterValue(args[0]), instruction.index_argument()); - break; - } - case Instruction::Op::kGetInstanceField: { - CHECK(instruction.dest().has_value()); - CHECK(instruction.dest()->is_variable()); - CHECK_EQ(1, instruction.args().size()); - - Encode22c(::dex::Opcode::OP_IGET, - RegisterValue(*instruction.dest()), - RegisterValue(args[0]), - instruction.index_argument()); - break; - } - case Instruction::Op::kSetInstanceField: { - CHECK(!instruction.dest().has_value()); - CHECK_EQ(2, args.size()); - CHECK(args[0].is_variable()); - CHECK(args[1].is_variable()); - - Encode22c(::dex::Opcode::OP_IPUT, - RegisterValue(args[1]), - RegisterValue(args[0]), - instruction.index_argument()); - break; - } - default: { LOG(FATAL) << "Unsupported field operation"; } - } -} - -size_t MethodBuilder::RegisterValue(const Value& value) const { - if (value.is_register()) { - return value.value(); - } else if (value.is_parameter()) { - return value.value() + NumRegisters() + kMaxScratchRegisters; - } - CHECK(false && "Must be either a parameter or a register"); - return 0; -} - -void MethodBuilder::BindLabel(const Value& label_id) { - CHECK(label_id.is_label()); - - LabelData& label = labels_[label_id.value()]; - CHECK(!label.bound_address.has_value()); - - label.bound_address = buffer_.size(); - - // patch any forward references to this label. - for (const auto& ref : label.references) { - buffer_[ref.field_offset] = *label.bound_address - ref.instruction_offset; - } - // No point keeping these around anymore. - label.references.clear(); -} - -::dex::u2 MethodBuilder::LabelValue(const Value& label_id, size_t instruction_offset, - size_t field_offset) { - CHECK(label_id.is_label()); - LabelData& label = labels_[label_id.value()]; - - // Short-circuit if the label is already bound. - if (label.bound_address.has_value()) { - return *label.bound_address - instruction_offset; - } - - // Otherwise, save a reference to where we need to back-patch later. - label.references.push_front(LabelReference{instruction_offset, field_offset}); - return 0; -} - -const MethodDeclData& DexBuilder::GetOrDeclareMethod(TypeDescriptor type, const std::string& name, - Prototype prototype) { - MethodDeclData& entry = method_id_map_[{type, name, prototype}]; - - if (entry.decl == nullptr) { - // This method has not already been declared, so declare it. - ir::MethodDecl* decl = dex_file_->Alloc<ir::MethodDecl>(); - // The method id is the last added method. - size_t id = dex_file_->methods.size() - 1; - - ir::String* dex_name{GetOrAddString(name)}; - decl->name = dex_name; - decl->parent = GetOrAddType(type.descriptor()); - decl->prototype = GetOrEncodeProto(prototype); - - // update the index -> ir node map (see tools/dexter/slicer/dex_ir_builder.cc) - auto new_index = dex_file_->methods_indexes.AllocateIndex(); - auto& ir_node = dex_file_->methods_map[new_index]; - CHECK(ir_node == nullptr); - ir_node = decl; - decl->orig_index = decl->index = new_index; - - entry = {id, decl}; - } - - return entry; -} - -std::optional<const Prototype> DexBuilder::GetPrototypeByMethodId(size_t method_id) const { - for (const auto& entry : method_id_map_) { - if (entry.second.id == method_id) { - return entry.first.prototype; - } - } - return {}; -} - -ir::Proto* DexBuilder::GetOrEncodeProto(Prototype prototype) { - ir::Proto*& ir_proto = proto_map_[prototype]; - if (ir_proto == nullptr) { - ir_proto = prototype.Encode(this); - } - return ir_proto; -} - -} // namespace dex -} // namespace startop diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h deleted file mode 100644 index eb2dc88835d4..000000000000 --- a/startop/view_compiler/dex_builder.h +++ /dev/null @@ -1,626 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ -#ifndef DEX_BUILDER_H_ -#define DEX_BUILDER_H_ - -#include <array> -#include <forward_list> -#include <map> -#include <optional> -#include <string> -#include <unordered_map> -#include <vector> - -#include "android-base/logging.h" - -#include "slicer/dex_bytecode.h" -#include "slicer/dex_ir.h" -#include "slicer/writer.h" - -namespace startop { -namespace dex { - -// TODO: remove this once the dex generation code is complete. -void WriteTestDexFile(const std::string& filename); - -////////////////////////// -// Forward declarations // -////////////////////////// -class DexBuilder; - -// Our custom allocator for dex::Writer -// -// This keeps track of all allocations and ensures they are freed when -// TrackingAllocator is destroyed. Pointers to memory allocated by this -// allocator must not outlive the allocator. -class TrackingAllocator : public ::dex::Writer::Allocator { - public: - virtual void* Allocate(size_t size); - virtual void Free(void* ptr); - - private: - std::unordered_map<void*, std::unique_ptr<uint8_t[]>> allocations_; -}; - -// Represents a DEX type descriptor. -// -// TODO: add a way to create a descriptor for a reference of a class type. -class TypeDescriptor { - public: - // Named constructors for base type descriptors. - static const TypeDescriptor Int(); - static const TypeDescriptor Void(); - - // Creates a type descriptor from a fully-qualified class name. For example, it turns the class - // name java.lang.Object into the descriptor Ljava/lang/Object. - static TypeDescriptor FromClassname(const std::string& name); - - // Return the full descriptor, such as I or Ljava/lang/Object - const std::string& descriptor() const { return descriptor_; } - // Return the shorty descriptor, such as I or L - std::string short_descriptor() const { return descriptor().substr(0, 1); } - - bool is_object() const { return short_descriptor() == "L"; } - - bool operator<(const TypeDescriptor& rhs) const { return descriptor_ < rhs.descriptor_; } - - private: - explicit TypeDescriptor(std::string descriptor) : descriptor_{descriptor} {} - - const std::string descriptor_; -}; - -// Defines a function signature. For example, Prototype{TypeDescriptor::VOID, TypeDescriptor::Int} -// represents the function type (Int) -> Void. -class Prototype { - public: - template <typename... TypeDescriptors> - explicit Prototype(TypeDescriptor return_type, TypeDescriptors... param_types) - : return_type_{return_type}, param_types_{param_types...} {} - - // Encode this prototype into the dex file. - ir::Proto* Encode(DexBuilder* dex) const; - - // Get the shorty descriptor, such as VII for (Int, Int) -> Void - std::string Shorty() const; - - const TypeDescriptor& ArgType(size_t index) const; - - bool operator<(const Prototype& rhs) const { - return std::make_tuple(return_type_, param_types_) < - std::make_tuple(rhs.return_type_, rhs.param_types_); - } - - private: - const TypeDescriptor return_type_; - const std::vector<TypeDescriptor> param_types_; -}; - -// Represents a DEX register or constant. We separate regular registers and parameters -// because we will not know the real parameter id until after all instructions -// have been generated. -class Value { - public: - static constexpr Value Local(size_t id) { return Value{id, Kind::kLocalRegister}; } - static constexpr Value Parameter(size_t id) { return Value{id, Kind::kParameter}; } - static constexpr Value Immediate(size_t value) { return Value{value, Kind::kImmediate}; } - static constexpr Value String(size_t value) { return Value{value, Kind::kString}; } - static constexpr Value Label(size_t id) { return Value{id, Kind::kLabel}; } - static constexpr Value Type(size_t id) { return Value{id, Kind::kType}; } - - bool is_register() const { return kind_ == Kind::kLocalRegister; } - bool is_parameter() const { return kind_ == Kind::kParameter; } - bool is_variable() const { return is_register() || is_parameter(); } - bool is_immediate() const { return kind_ == Kind::kImmediate; } - bool is_string() const { return kind_ == Kind::kString; } - bool is_label() const { return kind_ == Kind::kLabel; } - bool is_type() const { return kind_ == Kind::kType; } - - size_t value() const { return value_; } - - constexpr Value() : value_{0}, kind_{Kind::kInvalid} {} - - private: - enum class Kind { kInvalid, kLocalRegister, kParameter, kImmediate, kString, kLabel, kType }; - - size_t value_; - Kind kind_; - - constexpr Value(size_t value, Kind kind) : value_{value}, kind_{kind} {} -}; - -// Represents an allocated register returned by MethodBuilder::AllocRegister -class LiveRegister { - friend class MethodBuilder; - - public: - LiveRegister(LiveRegister&& other) : liveness_{other.liveness_}, index_{other.index_} { - other.index_ = {}; - }; - ~LiveRegister() { - if (index_.has_value()) { - (*liveness_)[*index_] = false; - } - }; - - operator const Value() const { return Value::Local(*index_); } - - private: - LiveRegister(std::vector<bool>* liveness, size_t index) : liveness_{liveness}, index_{index} {} - - std::vector<bool>* const liveness_; - std::optional<size_t> index_; -}; - -// A virtual instruction. We convert these to real instructions in MethodBuilder::Encode. -// Virtual instructions are needed to keep track of information that is not known until all of the -// code is generated. This information includes things like how many local registers are created and -// branch target locations. -class Instruction { - public: - // The operation performed by this instruction. These are virtual instructions that do not - // correspond exactly to DEX instructions. - enum class Op { - kBindLabel, - kBranchEqz, - kBranchNEqz, - kCheckCast, - kGetInstanceField, - kGetStaticField, - kInvokeDirect, - kInvokeInterface, - kInvokeStatic, - kInvokeVirtual, - kMove, - kMoveObject, - kNew, - kReturn, - kReturnObject, - kSetInstanceField, - kSetStaticField - }; - - //////////////////////// - // Named Constructors // - //////////////////////// - - // For instructions with no return value and no arguments. - static inline Instruction OpNoArgs(Op opcode) { - return Instruction{opcode, /*index_argument*/ 0, /*dest*/ {}}; - } - // For most instructions, which take some number of arguments and have an optional return value. - template <typename... T> - static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, - const T&... args) { - return Instruction{opcode, /*index_argument=*/0, /*result_is_object=*/false, dest, args...}; - } - - // A cast instruction. Basically, `(type)val` - static inline Instruction Cast(Value val, Value type) { - CHECK(type.is_type()); - return OpWithArgs(Op::kCheckCast, val, type); - } - - // For method calls. - template <typename... T> - static inline Instruction InvokeVirtual(size_t index_argument, std::optional<const Value> dest, - Value this_arg, T... args) { - return Instruction{ - Op::kInvokeVirtual, index_argument, /*result_is_object=*/false, dest, this_arg, args...}; - } - // Returns an object - template <typename... T> - static inline Instruction InvokeVirtualObject(size_t index_argument, - std::optional<const Value> dest, Value this_arg, - const T&... args) { - return Instruction{ - Op::kInvokeVirtual, index_argument, /*result_is_object=*/true, dest, this_arg, args...}; - } - // For direct calls (basically, constructors). - template <typename... T> - static inline Instruction InvokeDirect(size_t index_argument, std::optional<const Value> dest, - Value this_arg, const T&... args) { - return Instruction{ - Op::kInvokeDirect, index_argument, /*result_is_object=*/false, dest, this_arg, args...}; - } - // Returns an object - template <typename... T> - static inline Instruction InvokeDirectObject(size_t index_argument, - std::optional<const Value> dest, Value this_arg, - T... args) { - return Instruction{ - Op::kInvokeDirect, index_argument, /*result_is_object=*/true, dest, this_arg, args...}; - } - // For static calls. - template <typename... T> - static inline Instruction InvokeStatic(size_t index_argument, std::optional<const Value> dest, - T... args) { - return Instruction{ - Op::kInvokeStatic, index_argument, /*result_is_object=*/false, dest, args...}; - } - // Returns an object - template <typename... T> - static inline Instruction InvokeStaticObject(size_t index_argument, - std::optional<const Value> dest, T... args) { - return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/true, dest, args...}; - } - // For static calls. - template <typename... T> - static inline Instruction InvokeInterface(size_t index_argument, std::optional<const Value> dest, - const T&... args) { - return Instruction{ - Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...}; - } - - static inline Instruction GetStaticField(size_t field_id, Value dest) { - return Instruction{Op::kGetStaticField, field_id, dest}; - } - - static inline Instruction SetStaticField(size_t field_id, Value value) { - return Instruction{ - Op::kSetStaticField, field_id, /*result_is_object=*/false, /*dest=*/{}, value}; - } - - static inline Instruction GetField(size_t field_id, Value dest, Value object) { - return Instruction{Op::kGetInstanceField, field_id, /*result_is_object=*/false, dest, object}; - } - - static inline Instruction SetField(size_t field_id, Value object, Value value) { - return Instruction{ - Op::kSetInstanceField, field_id, /*result_is_object=*/false, /*dest=*/{}, object, value}; - } - - /////////////// - // Accessors // - /////////////// - - Op opcode() const { return opcode_; } - size_t index_argument() const { return index_argument_; } - bool result_is_object() const { return result_is_object_; } - const std::optional<const Value>& dest() const { return dest_; } - const std::vector<const Value>& args() const { return args_; } - - private: - inline Instruction(Op opcode, size_t index_argument, std::optional<const Value> dest) - : opcode_{opcode}, - index_argument_{index_argument}, - result_is_object_{false}, - dest_{dest}, - args_{} {} - - template <typename... T> - inline Instruction(Op opcode, size_t index_argument, bool result_is_object, - std::optional<const Value> dest, const T&... args) - : opcode_{opcode}, - index_argument_{index_argument}, - result_is_object_{result_is_object}, - dest_{dest}, - args_{args...} {} - - const Op opcode_; - // The index of the method to invoke, for kInvokeVirtual and similar opcodes. - const size_t index_argument_{0}; - const bool result_is_object_; - const std::optional<const Value> dest_; - const std::vector<const Value> args_; -}; - -// Needed for CHECK_EQ, DCHECK_EQ, etc. -std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode); - -// Keeps track of information needed to manipulate or call a method. -struct MethodDeclData { - size_t id; - ir::MethodDecl* decl; -}; - -// Tools to help build methods and their bodies. -class MethodBuilder { - public: - MethodBuilder(DexBuilder* dex, ir::Class* class_def, ir::MethodDecl* decl); - - // Encode the method into DEX format. - ir::EncodedMethod* Encode(); - - // Create a new register to be used to storing values. - LiveRegister AllocRegister(); - - Value MakeLabel(); - - ///////////////////////////////// - // Instruction builder methods // - ///////////////////////////////// - - void AddInstruction(Instruction instruction); - - // return-void - void BuildReturn(); - void BuildReturn(Value src, bool is_object = false); - // const/4 - void BuildConst4(Value target, int value); - void BuildConstString(Value target, const std::string& value); - template <typename... T> - void BuildNew(Value target, TypeDescriptor type, Prototype constructor, const T&... args); - - // TODO: add builders for more instructions - - DexBuilder* dex_file() const { return dex_; } - - private: - void EncodeInstructions(); - void EncodeInstruction(const Instruction& instruction); - - // Encodes a return instruction. For instructions with no return value, the opcode field is - // ignored. Otherwise, this specifies which return instruction will be used (return, - // return-object, etc.) - void EncodeReturn(const Instruction& instruction, ::dex::Opcode opcode); - - void EncodeMove(const Instruction& instruction); - void EncodeInvoke(const Instruction& instruction, ::dex::Opcode opcode); - void EncodeBranch(::dex::Opcode op, const Instruction& instruction); - void EncodeNew(const Instruction& instruction); - void EncodeCast(const Instruction& instruction); - void EncodeFieldOp(const Instruction& instruction); - - // Low-level instruction format encoding. See - // https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of - // formats. - - inline uint8_t ToBits(::dex::Opcode opcode) { - static_assert(sizeof(uint8_t) == sizeof(::dex::Opcode)); - return static_cast<uint8_t>(opcode); - } - - inline void Encode10x(::dex::Opcode opcode) { - // 00|op - static_assert(sizeof(uint8_t) == sizeof(::dex::Opcode)); - buffer_.push_back(ToBits(opcode)); - } - - inline void Encode11x(::dex::Opcode opcode, uint8_t a) { - // aa|op - buffer_.push_back((a << 8) | ToBits(opcode)); - } - - inline void Encode11n(::dex::Opcode opcode, uint8_t a, int8_t b) { - // b|a|op - - // Make sure the fields are in bounds (4 bits for a, 4 bits for b). - CHECK_LT(a, 16); - CHECK_LE(-8, b); - CHECK_LT(b, 8); - - buffer_.push_back(((b & 0xf) << 12) | (a << 8) | ToBits(opcode)); - } - - inline void Encode21c(::dex::Opcode opcode, uint8_t a, uint16_t b) { - // aa|op|bbbb - buffer_.push_back((a << 8) | ToBits(opcode)); - buffer_.push_back(b); - } - - inline void Encode22c(::dex::Opcode opcode, uint8_t a, uint8_t b, uint16_t c) { - // b|a|op|bbbb - CHECK(IsShortRegister(a)); - CHECK(IsShortRegister(b)); - buffer_.push_back((b << 12) | (a << 8) | ToBits(opcode)); - buffer_.push_back(c); - } - - inline void Encode32x(::dex::Opcode opcode, uint16_t a, uint16_t b) { - buffer_.push_back(ToBits(opcode)); - buffer_.push_back(a); - buffer_.push_back(b); - } - - inline void Encode35c(::dex::Opcode opcode, size_t a, uint16_t b, uint8_t c, uint8_t d, - uint8_t e, uint8_t f, uint8_t g) { - // a|g|op|bbbb|f|e|d|c - - CHECK_LE(a, 5); - CHECK(IsShortRegister(c)); - CHECK(IsShortRegister(d)); - CHECK(IsShortRegister(e)); - CHECK(IsShortRegister(f)); - CHECK(IsShortRegister(g)); - buffer_.push_back((a << 12) | (g << 8) | ToBits(opcode)); - buffer_.push_back(b); - buffer_.push_back((f << 12) | (e << 8) | (d << 4) | c); - } - - inline void Encode3rc(::dex::Opcode opcode, size_t a, uint16_t b, uint16_t c) { - CHECK_LE(a, 255); - buffer_.push_back((a << 8) | ToBits(opcode)); - buffer_.push_back(b); - buffer_.push_back(c); - } - - static constexpr bool IsShortRegister(size_t register_value) { return register_value < 16; } - - // Returns an array of num_regs scratch registers. These are guaranteed to be - // contiguous, so they are suitable for the invoke-*/range instructions. - template <int num_regs> - std::array<Value, num_regs> GetScratchRegisters() const { - static_assert(num_regs <= kMaxScratchRegisters); - std::array<Value, num_regs> regs; - for (size_t i = 0; i < num_regs; ++i) { - regs[i] = std::move(Value::Local(NumRegisters() + i)); - } - return regs; - } - - // Converts a register or parameter to its DEX register number. - size_t RegisterValue(const Value& value) const; - - // Sets a label's address to the current position in the instruction buffer. If there are any - // forward references to the label, this function will back-patch them. - void BindLabel(const Value& label); - - // Returns the offset of the label relative to the given instruction offset. If the label is not - // bound, a reference will be saved and it will automatically be patched when the label is bound. - ::dex::u2 LabelValue(const Value& label, size_t instruction_offset, size_t field_offset); - - DexBuilder* dex_; - ir::Class* class_; - ir::MethodDecl* decl_; - - // A list of the instructions we will eventually encode. - std::vector<Instruction> instructions_; - - // A buffer to hold instructions that have been encoded. - std::vector<::dex::u2> buffer_; - - // We create some scratch registers for when we have to shuffle registers - // around to make legal DEX code. - static constexpr size_t kMaxScratchRegisters = 5; - - size_t NumRegisters() const { - return register_liveness_.size(); - } - - // Stores information needed to back-patch a label once it is bound. We need to know the start of - // the instruction that refers to the label, and the offset to where the actual label value should - // go. - struct LabelReference { - size_t instruction_offset; - size_t field_offset; - }; - - struct LabelData { - std::optional<size_t> bound_address; - std::forward_list<LabelReference> references; - }; - - std::vector<LabelData> labels_; - - // During encoding, keep track of the largest number of arguments needed, so we can use it for our - // outs count - size_t max_args_{0}; - - std::vector<bool> register_liveness_; -}; - -// A helper to build class definitions. -class ClassBuilder { - public: - ClassBuilder(DexBuilder* parent, const std::string& name, ir::Class* class_def); - - void set_source_file(const std::string& source); - - // Create a method with the given name and prototype. The returned MethodBuilder can be used to - // fill in the method body. - MethodBuilder CreateMethod(const std::string& name, Prototype prototype); - - private: - DexBuilder* const parent_; - const TypeDescriptor type_descriptor_; - ir::Class* const class_; -}; - -// Builds Dex files from scratch. -class DexBuilder { - public: - DexBuilder(); - - // Create an in-memory image of the DEX file that can either be loaded directly or written to a - // file. - slicer::MemView CreateImage(); - - template <typename T> - T* Alloc() { - return dex_file_->Alloc<T>(); - } - - // Find the ir::String that matches the given string, creating it if it does not exist. - ir::String* GetOrAddString(const std::string& string); - // Create a new class of the given name. - ClassBuilder MakeClass(const std::string& name); - - // Add a type for the given descriptor, or return the existing one if it already exists. - // See the TypeDescriptor class for help generating these. GetOrAddType can be used to declare - // imported classes. - ir::Type* GetOrAddType(const std::string& descriptor); - inline ir::Type* GetOrAddType(TypeDescriptor descriptor) { - return GetOrAddType(descriptor.descriptor()); - } - - ir::FieldDecl* GetOrAddField(TypeDescriptor parent, const std::string& name, TypeDescriptor type); - - // Returns the method id for the method, creating it if it has not been created yet. - const MethodDeclData& GetOrDeclareMethod(TypeDescriptor type, const std::string& name, - Prototype prototype); - - std::optional<const Prototype> GetPrototypeByMethodId(size_t method_id) const; - - private: - // Looks up the ir::Proto* corresponding to this given prototype, or creates one if it does not - // exist. - ir::Proto* GetOrEncodeProto(Prototype prototype); - - std::shared_ptr<ir::DexFile> dex_file_; - - // allocator_ is needed to be able to encode the image. - TrackingAllocator allocator_; - - // We'll need to allocate buffers for all of the encoded strings we create. This is where we store - // all of them. - std::vector<std::unique_ptr<uint8_t[]>> string_data_; - - // Keep track of what types we've defined so we can look them up later. - std::unordered_map<std::string, ir::Type*> types_by_descriptor_; - - struct MethodDescriptor { - TypeDescriptor type; - std::string name; - Prototype prototype; - - inline bool operator<(const MethodDescriptor& rhs) const { - return std::make_tuple(type, name, prototype) < - std::make_tuple(rhs.type, rhs.name, rhs.prototype); - } - }; - - // Maps method declarations to their method index. This is needed to encode references to them. - // When we go to actually write the DEX file, slicer will re-assign these after correctly sorting - // the methods list. - std::map<MethodDescriptor, MethodDeclData> method_id_map_; - - // Keep track of what strings we've defined so we can look them up later. - std::unordered_map<std::string, ir::String*> strings_; - - // Keep track of already-encoded protos. - std::map<Prototype, ir::Proto*> proto_map_; - - // Keep track of fields that have been declared - std::map<std::tuple<TypeDescriptor, std::string>, ir::FieldDecl*> field_decls_by_key_; -}; - -template <typename... T> -void MethodBuilder::BuildNew(Value target, TypeDescriptor type, Prototype constructor, - const T&... args) { - MethodDeclData constructor_data{dex_->GetOrDeclareMethod(type, "<init>", constructor)}; - // allocate the object - ir::Type* type_def = dex_->GetOrAddType(type.descriptor()); - AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kNew, target, Value::Type(type_def->orig_index))); - // call the constructor - AddInstruction(Instruction::InvokeDirect(constructor_data.id, /*dest=*/{}, target, args...)); -}; - -} // namespace dex -} // namespace startop - -#endif // DEX_BUILDER_H_ diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp deleted file mode 100644 index bcba2febdcdc..000000000000 --- a/startop/view_compiler/dex_builder_test/Android.bp +++ /dev/null @@ -1,68 +0,0 @@ -// -// Copyright (C) 2018 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 { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -genrule { - name: "generate_compiled_layout1", - tools: [":viewcompiler"], - cmd: "$(location :viewcompiler) $(in) --dex --out $(out) --package android.startop.test", - srcs: ["res/layout/layout1.xml"], - out: [ - "layout1.dex", - ], -} - -genrule { - name: "generate_compiled_layout2", - tools: [":viewcompiler"], - cmd: "$(location :viewcompiler) $(in) --dex --out $(out) --package android.startop.test", - srcs: ["res/layout/layout2.xml"], - out: [ - "layout2.dex", - ], -} - -android_test { - name: "dex-builder-test", - srcs: [ - "src/android/startop/test/DexBuilderTest.java", - "src/android/startop/test/LayoutCompilerTest.java", - "src/android/startop/test/TestClass.java", - ], - sdk_version: "current", - data: [ - ":generate_dex_testcases", - ":generate_compiled_layout1", - ":generate_compiled_layout2", - ], - static_libs: [ - "androidx.test.core", - "androidx.test.runner", - "junit", - ], - manifest: "AndroidManifest.xml", - resource_dirs: ["res"], - test_config: "AndroidTest.xml", - test_suites: ["general-tests"], -} diff --git a/startop/view_compiler/dex_builder_test/AndroidManifest.xml b/startop/view_compiler/dex_builder_test/AndroidManifest.xml deleted file mode 100644 index b33566363286..000000000000 --- a/startop/view_compiler/dex_builder_test/AndroidManifest.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2018 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. ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="android.startop.test" > - - <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" /> - - <application> - <uses-library android:name="android.test.runner" /> - </application> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="android.startop.test" - android:label="DexBuilder Tests"/> - -</manifest> diff --git a/startop/view_compiler/dex_builder_test/AndroidTest.xml b/startop/view_compiler/dex_builder_test/AndroidTest.xml deleted file mode 100644 index 59093c79bd0d..000000000000 --- a/startop/view_compiler/dex_builder_test/AndroidTest.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2018 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. ---> -<configuration description="Runs DexBuilder Tests."> - <option name="test-suite-tag" value="apct" /> - <option name="test-suite-tag" value="apct-instrumentation" /> - <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="cleanup-apks" value="true" /> - <option name="test-file-name" value="dex-builder-test.apk" /> - </target_preparer> - - <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> - <option name="cleanup" value="true" /> - <option name="push" value="trivial.dex->/data/local/tmp/dex-builder-test/trivial.dex" /> - <option name="push" value="simple.dex->/data/local/tmp/dex-builder-test/simple.dex" /> - <option name="push" value="layout1.dex->/data/local/tmp/dex-builder-test/layout1.dex" /> - <option name="push" value="layout2.dex->/data/local/tmp/dex-builder-test/layout2.dex" /> - </target_preparer> - - <test class="com.android.tradefed.testtype.AndroidJUnitTest" > - <option name="package" value="android.startop.test" /> - <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> - </test> -</configuration> diff --git a/startop/view_compiler/dex_builder_test/res/layout/layout1.xml b/startop/view_compiler/dex_builder_test/res/layout/layout1.xml deleted file mode 100644 index 0f9375c6ebce..000000000000 --- a/startop/view_compiler/dex_builder_test/res/layout/layout1.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingLeft="16dp" - android:paddingRight="16dp" - android:orientation="vertical" - android:gravity="center"> - - <Button - android:layout_width="match_parent" - android:layout_height="match_parent"/> - <Button - android:layout_width="match_parent" - android:layout_height="match_parent"/> - - </LinearLayout> diff --git a/startop/view_compiler/dex_builder_test/res/layout/layout2.xml b/startop/view_compiler/dex_builder_test/res/layout/layout2.xml deleted file mode 100644 index b092e1c20311..000000000000 --- a/startop/view_compiler/dex_builder_test/res/layout/layout2.xml +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <TableRow - android:layout_width="match_parent" - android:layout_height="match_parent" > - - <Button - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Button" /> - - <TableRow - android:layout_width="match_parent" - android:layout_height="match_parent" > - - <Button - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Button" /> - <TableRow - android:layout_width="match_parent" - android:layout_height="match_parent" > - - <Button - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Button" /> - - <TableRow - android:layout_width="match_parent" - android:layout_height="match_parent" > - - <Button - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Button" /> - </TableRow> - - </TableRow> - </TableRow> - </TableRow> -</LinearLayout> diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java deleted file mode 100644 index 6af01f6f3292..000000000000 --- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (C) 2018 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 android.startop.test; - -import android.content.Context; -import androidx.test.InstrumentationRegistry; -import dalvik.system.PathClassLoader; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import org.junit.Assert; -import org.junit.Test; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -// Adding tests here requires changes in several other places. See README.md in -// the view_compiler directory for more information. -public final class DexBuilderTest { - static ClassLoader loadDexFile(String filename) throws Exception { - return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename, - DexBuilderTest.class.getClassLoader()); - } - - public void hello() {} - - @Test - public void loadTrivialDex() throws Exception { - ClassLoader loader = loadDexFile("trivial.dex"); - loader.loadClass("android.startop.test.testcases.Trivial"); - } - - @Test - public void return5() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("return5"); - Assert.assertEquals(5, method.invoke(null)); - } - - @Test - public void returnInteger5() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("returnInteger5"); - Assert.assertEquals(5, method.invoke(null)); - } - - @Test - public void returnParam() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("returnParam", int.class); - Assert.assertEquals(5, method.invoke(null, 5)); - Assert.assertEquals(42, method.invoke(null, 42)); - } - - @Test - public void returnStringLength() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("returnStringLength", String.class); - Assert.assertEquals(13, method.invoke(null, "Hello, World!")); - } - - @Test - public void returnIfZero() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("returnIfZero", int.class); - Assert.assertEquals(5, method.invoke(null, 0)); - Assert.assertEquals(3, method.invoke(null, 17)); - } - - @Test - public void returnIfNotZero() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("returnIfNotZero", int.class); - Assert.assertEquals(3, method.invoke(null, 0)); - Assert.assertEquals(5, method.invoke(null, 17)); - } - - @Test - public void backwardsBranch() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("backwardsBranch"); - Assert.assertEquals(2, method.invoke(null)); - } - - @Test - public void returnNull() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("returnNull"); - Assert.assertEquals(null, method.invoke(null)); - } - - @Test - public void makeString() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("makeString"); - Assert.assertEquals("Hello, World!", method.invoke(null)); - } - - @Test - public void returnStringIfZeroAB() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("returnStringIfZeroAB", int.class); - Assert.assertEquals("a", method.invoke(null, 0)); - Assert.assertEquals("b", method.invoke(null, 1)); - } - - @Test - public void returnStringIfZeroBA() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("returnStringIfZeroBA", int.class); - Assert.assertEquals("b", method.invoke(null, 0)); - Assert.assertEquals("a", method.invoke(null, 1)); - } - - @Test - public void invokeStaticReturnObject() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("invokeStaticReturnObject", int.class, int.class); - Assert.assertEquals("10", method.invoke(null, 10, 10)); - Assert.assertEquals("a", method.invoke(null, 10, 16)); - Assert.assertEquals("5", method.invoke(null, 5, 16)); - } - - @Test - public void invokeVirtualReturnObject() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("invokeVirtualReturnObject", String.class, int.class); - Assert.assertEquals("bc", method.invoke(null, "abc", 1)); - } - - @Test - public void castObjectToString() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("castObjectToString", Object.class); - Assert.assertEquals("abc", method.invoke(null, "abc")); - boolean castFailed = false; - try { - method.invoke(null, 5); - } catch (InvocationTargetException e) { - if (e.getCause() instanceof ClassCastException) { - castFailed = true; - } else { - throw e; - } - } - Assert.assertTrue(castFailed); - } - - @Test - public void readStaticField() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("readStaticField"); - TestClass.staticInteger = 5; - Assert.assertEquals(5, method.invoke(null)); - } - - @Test - public void setStaticField() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("setStaticField"); - TestClass.staticInteger = 5; - method.invoke(null); - Assert.assertEquals(7, TestClass.staticInteger); - } - - @Test - public void readInstanceField() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("readInstanceField", TestClass.class); - TestClass obj = new TestClass(); - obj.instanceField = 5; - Assert.assertEquals(5, method.invoke(null, obj)); - } - - @Test - public void setInstanceField() throws Exception { - ClassLoader loader = loadDexFile("simple.dex"); - Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); - Method method = clazz.getMethod("setInstanceField", TestClass.class); - TestClass obj = new TestClass(); - obj.instanceField = 5; - method.invoke(null, obj); - Assert.assertEquals(7, obj.instanceField); - } -} diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java deleted file mode 100644 index b0cf91d5fb97..000000000000 --- a/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2018 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 android.startop.test; - -import android.content.Context; -import androidx.test.InstrumentationRegistry; -import android.view.View; -import dalvik.system.PathClassLoader; -import java.lang.reflect.Method; -import org.junit.Assert; -import org.junit.Test; - -import java.lang.reflect.Method; - -// Adding tests here requires changes in several other places. See README.md in -// the view_compiler directory for more information. -public class LayoutCompilerTest { - static ClassLoader loadDexFile(String filename) throws Exception { - return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename, - ClassLoader.getSystemClassLoader()); - } - - @Test - public void loadAndInflateLayout1() throws Exception { - ClassLoader dex_file = loadDexFile("layout1.dex"); - Class compiled_view = dex_file.loadClass("android.startop.test.CompiledView"); - Method layout1 = compiled_view.getMethod("layout1", Context.class, int.class); - Context context = InstrumentationRegistry.getTargetContext(); - layout1.invoke(null, context, R.layout.layout1); - } - - @Test - public void loadAndInflateLayout2() throws Exception { - ClassLoader dex_file = loadDexFile("layout2.dex"); - Class compiled_view = dex_file.loadClass("android.startop.test.CompiledView"); - Method layout1 = compiled_view.getMethod("layout2", Context.class, int.class); - Context context = InstrumentationRegistry.getTargetContext(); - layout1.invoke(null, context, R.layout.layout1); - } -} diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java deleted file mode 100644 index dd7792306030..000000000000 --- a/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2018 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 android.startop.test; - - /** - * A simple class to help test DexBuilder. - */ -public final class TestClass { - public static int staticInteger; - - public int instanceField; -} diff --git a/startop/view_compiler/dex_layout_compiler.cc b/startop/view_compiler/dex_layout_compiler.cc deleted file mode 100644 index bddb8aa6716a..000000000000 --- a/startop/view_compiler/dex_layout_compiler.cc +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#include "dex_layout_compiler.h" -#include "layout_validation.h" - -#include "android-base/stringprintf.h" - -namespace startop { - -using android::base::StringPrintf; -using dex::Instruction; -using dex::LiveRegister; -using dex::Prototype; -using dex::TypeDescriptor; -using dex::Value; - -namespace { -// TODO: these are a bunch of static initializers, which we should avoid. See if -// we can make them constexpr. -const TypeDescriptor kAttributeSet = TypeDescriptor::FromClassname("android.util.AttributeSet"); -const TypeDescriptor kContext = TypeDescriptor::FromClassname("android.content.Context"); -const TypeDescriptor kLayoutInflater = TypeDescriptor::FromClassname("android.view.LayoutInflater"); -const TypeDescriptor kResources = TypeDescriptor::FromClassname("android.content.res.Resources"); -const TypeDescriptor kString = TypeDescriptor::FromClassname("java.lang.String"); -const TypeDescriptor kView = TypeDescriptor::FromClassname("android.view.View"); -const TypeDescriptor kViewGroup = TypeDescriptor::FromClassname("android.view.ViewGroup"); -const TypeDescriptor kXmlResourceParser = - TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"); -} // namespace - -DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method) - : method_{method}, - context_{Value::Parameter(0)}, - resid_{Value::Parameter(1)}, - inflater_{method->AllocRegister()}, - xml_{method->AllocRegister()}, - attrs_{method->AllocRegister()}, - classname_tmp_{method->AllocRegister()}, - xml_next_{method->dex_file()->GetOrDeclareMethod(kXmlResourceParser, "next", - Prototype{TypeDescriptor::Int()})}, - try_create_view_{method->dex_file()->GetOrDeclareMethod( - kLayoutInflater, "tryCreateView", - Prototype{kView, kView, kString, kContext, kAttributeSet})}, - generate_layout_params_{method->dex_file()->GetOrDeclareMethod( - kViewGroup, "generateLayoutParams", - Prototype{TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"), - kAttributeSet})}, - add_view_{method->dex_file()->GetOrDeclareMethod( - kViewGroup, "addView", - Prototype{TypeDescriptor::Void(), - kView, - TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})} {} - -void DexViewBuilder::BuildGetLayoutInflater(Value dest) { - // dest = LayoutInflater.from(context); - auto layout_inflater_from = method_->dex_file()->GetOrDeclareMethod( - kLayoutInflater, "from", Prototype{kLayoutInflater, kContext}); - method_->AddInstruction(Instruction::InvokeStaticObject(layout_inflater_from.id, dest, context_)); -} - -void DexViewBuilder::BuildGetResources(Value dest) { - // dest = context.getResources(); - auto get_resources = - method_->dex_file()->GetOrDeclareMethod(kContext, "getResources", Prototype{kResources}); - method_->AddInstruction(Instruction::InvokeVirtualObject(get_resources.id, dest, context_)); -} - -void DexViewBuilder::BuildGetLayoutResource(Value dest, Value resources, Value resid) { - // dest = resources.getLayout(resid); - auto get_layout = method_->dex_file()->GetOrDeclareMethod( - kResources, "getLayout", Prototype{kXmlResourceParser, TypeDescriptor::Int()}); - method_->AddInstruction(Instruction::InvokeVirtualObject(get_layout.id, dest, resources, resid)); -} - -void DexViewBuilder::BuildLayoutResourceToAttributeSet(dex::Value dest, - dex::Value layout_resource) { - // dest = Xml.asAttributeSet(layout_resource); - auto as_attribute_set = method_->dex_file()->GetOrDeclareMethod( - TypeDescriptor::FromClassname("android.util.Xml"), - "asAttributeSet", - Prototype{kAttributeSet, TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")}); - method_->AddInstruction( - Instruction::InvokeStaticObject(as_attribute_set.id, dest, layout_resource)); -} - -void DexViewBuilder::BuildXmlNext() { - // xml_.next(); - method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_)); -} - -void DexViewBuilder::Start() { - BuildGetLayoutInflater(/*dest=*/inflater_); - BuildGetResources(/*dest=*/xml_); - BuildGetLayoutResource(/*dest=*/xml_, /*resources=*/xml_, resid_); - BuildLayoutResourceToAttributeSet(/*dest=*/attrs_, /*layout_resource=*/xml_); - - // Advance past start document tag - BuildXmlNext(); -} - -void DexViewBuilder::Finish() {} - -namespace { -std::string ResolveName(const std::string& name) { - if (name == "View") return "android.view.View"; - if (name == "ViewGroup") return "android.view.ViewGroup"; - if (name.find('.') == std::string::npos) { - return StringPrintf("android.widget.%s", name.c_str()); - } - return name; -} -} // namespace - -void DexViewBuilder::BuildTryCreateView(Value dest, Value parent, Value classname) { - // dest = inflater_.tryCreateView(parent, classname, context_, attrs_); - method_->AddInstruction(Instruction::InvokeVirtualObject( - try_create_view_.id, dest, inflater_, parent, classname, context_, attrs_)); -} - -void DexViewBuilder::StartView(const std::string& name, bool is_viewgroup) { - bool const is_root_view = view_stack_.empty(); - - // Advance to start tag - BuildXmlNext(); - - LiveRegister view = AcquireRegister(); - // try to create the view using the factories - method_->BuildConstString(classname_tmp_, - name); // TODO: the need to fully qualify the classname - if (is_root_view) { - LiveRegister null = AcquireRegister(); - method_->BuildConst4(null, 0); - BuildTryCreateView(/*dest=*/view, /*parent=*/null, classname_tmp_); - } else { - BuildTryCreateView(/*dest=*/view, /*parent=*/GetCurrentView(), classname_tmp_); - } - auto label = method_->MakeLabel(); - // branch if not null - method_->AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label)); - - // If null, create the class directly. - method_->BuildNew(view, - TypeDescriptor::FromClassname(ResolveName(name)), - Prototype{TypeDescriptor::Void(), kContext, kAttributeSet}, - context_, - attrs_); - - method_->AddInstruction(Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, label)); - - if (is_viewgroup) { - // Cast to a ViewGroup so we can add children later. - const ir::Type* view_group_def = method_->dex_file()->GetOrAddType(kViewGroup.descriptor()); - method_->AddInstruction(Instruction::Cast(view, Value::Type(view_group_def->orig_index))); - } - - if (!is_root_view) { - // layout_params = parent.generateLayoutParams(attrs); - LiveRegister layout_params{AcquireRegister()}; - method_->AddInstruction(Instruction::InvokeVirtualObject( - generate_layout_params_.id, layout_params, GetCurrentView(), attrs_)); - view_stack_.push_back({std::move(view), std::move(layout_params)}); - } else { - view_stack_.push_back({std::move(view), {}}); - } -} - -void DexViewBuilder::FinishView() { - if (view_stack_.size() == 1) { - method_->BuildReturn(GetCurrentView(), /*is_object=*/true); - } else { - // parent.add(view, layout_params) - method_->AddInstruction(Instruction::InvokeVirtual( - add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams())); - // xml.next(); // end tag - method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_)); - } - PopViewStack(); -} - -LiveRegister DexViewBuilder::AcquireRegister() { return method_->AllocRegister(); } - -Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; } -Value DexViewBuilder::GetCurrentLayoutParams() const { - return view_stack_.back().layout_params.value(); -} -Value DexViewBuilder::GetParentView() const { return view_stack_[view_stack_.size() - 2].view; } - -void DexViewBuilder::PopViewStack() { - // Unconditionally release the view register. - view_stack_.pop_back(); -} - -} // namespace startop diff --git a/startop/view_compiler/dex_layout_compiler.h b/startop/view_compiler/dex_layout_compiler.h deleted file mode 100644 index a34ed1f0168e..000000000000 --- a/startop/view_compiler/dex_layout_compiler.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#ifndef DEX_LAYOUT_COMPILER_H_ -#define DEX_LAYOUT_COMPILER_H_ - -#include "dex_builder.h" - -#include <codecvt> -#include <locale> -#include <string> -#include <vector> - -namespace startop { - -// This visitor does the actual view compilation, using a supplied builder. -template <typename Builder> -class LayoutCompilerVisitor { - public: - explicit LayoutCompilerVisitor(Builder* builder) : builder_{builder} {} - - void VisitStartDocument() { builder_->Start(); } - void VisitEndDocument() { builder_->Finish(); } - void VisitStartTag(const std::u16string& name) { - parent_stack_.push_back(ViewEntry{ - std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(name), {}}); - } - void VisitEndTag() { - auto entry = parent_stack_.back(); - parent_stack_.pop_back(); - - if (parent_stack_.empty()) { - GenerateCode(entry); - } else { - parent_stack_.back().children.push_back(entry); - } - } - - private: - struct ViewEntry { - std::string name; - std::vector<ViewEntry> children; - }; - - void GenerateCode(const ViewEntry& view) { - builder_->StartView(view.name, !view.children.empty()); - for (const auto& child : view.children) { - GenerateCode(child); - } - builder_->FinishView(); - } - - Builder* builder_; - - std::vector<ViewEntry> parent_stack_; -}; - -class DexViewBuilder { - public: - DexViewBuilder(dex::MethodBuilder* method); - - void Start(); - void Finish(); - void StartView(const std::string& name, bool is_viewgroup); - void FinishView(); - - private: - // Accessors for the stack of views that are under construction. - dex::LiveRegister AcquireRegister(); - dex::Value GetCurrentView() const; - dex::Value GetCurrentLayoutParams() const; - dex::Value GetParentView() const; - void PopViewStack(); - - // Methods to simplify building different code fragments. - void BuildGetLayoutInflater(dex::Value dest); - void BuildGetResources(dex::Value dest); - void BuildGetLayoutResource(dex::Value dest, dex::Value resources, dex::Value resid); - void BuildLayoutResourceToAttributeSet(dex::Value dest, dex::Value layout_resource); - void BuildXmlNext(); - void BuildTryCreateView(dex::Value dest, dex::Value parent, dex::Value classname); - - dex::MethodBuilder* method_; - - // Parameters to the generated method - dex::Value const context_; - dex::Value const resid_; - - // Registers used for code generation - const dex::LiveRegister inflater_; - const dex::LiveRegister xml_; - const dex::LiveRegister attrs_; - const dex::LiveRegister classname_tmp_; - - const dex::MethodDeclData xml_next_; - const dex::MethodDeclData try_create_view_; - const dex::MethodDeclData generate_layout_params_; - const dex::MethodDeclData add_view_; - - // Keep track of the views currently in progress. - struct ViewEntry { - dex::LiveRegister view; - std::optional<dex::LiveRegister> layout_params; - }; - std::vector<ViewEntry> view_stack_; -}; - -} // namespace startop - -#endif // DEX_LAYOUT_COMPILER_H_ diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc deleted file mode 100644 index 5dda59e3473f..000000000000 --- a/startop/view_compiler/dex_testcase_generator.cc +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#include "android-base/logging.h" -#include "dex_builder.h" - -#include <fstream> -#include <string> - -// Adding tests here requires changes in several other places. See README.md in -// the view_compiler directory for more information. - -using namespace startop::dex; -using namespace std; - -void GenerateTrivialDexFile(const string& outdir) { - DexBuilder dex_file; - - ClassBuilder cbuilder{dex_file.MakeClass("android.startop.test.testcases.Trivial")}; - cbuilder.set_source_file("dex_testcase_generator.cc#GenerateTrivialDexFile"); - - slicer::MemView image{dex_file.CreateImage()}; - std::ofstream out_file(outdir + "/trivial.dex"); - out_file.write(image.ptr<const char>(), image.size()); -} - -// Generates test cases that test around 1 instruction. -void GenerateSimpleTestCases(const string& outdir) { - DexBuilder dex_file; - - ClassBuilder cbuilder{dex_file.MakeClass("android.startop.test.testcases.SimpleTests")}; - cbuilder.set_source_file("dex_testcase_generator.cc#GenerateSimpleTestCases"); - - // int return5() { return 5; } - auto return5{cbuilder.CreateMethod("return5", Prototype{TypeDescriptor::Int()})}; - { - LiveRegister r{return5.AllocRegister()}; - return5.BuildConst4(r, 5); - return5.BuildReturn(r); - } - return5.Encode(); - - // int return5() { return 5; } - auto integer_type{TypeDescriptor::FromClassname("java.lang.Integer")}; - auto returnInteger5{cbuilder.CreateMethod("returnInteger5", Prototype{integer_type})}; - [&](MethodBuilder& method) { - LiveRegister five{method.AllocRegister()}; - method.BuildConst4(five, 5); - LiveRegister object{method.AllocRegister()}; - method.BuildNew( - object, integer_type, Prototype{TypeDescriptor::Void(), TypeDescriptor::Int()}, five); - method.BuildReturn(object, /*is_object=*/true); - }(returnInteger5); - returnInteger5.Encode(); - - // // int returnParam(int x) { return x; } - auto returnParam{cbuilder.CreateMethod("returnParam", - Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})}; - returnParam.BuildReturn(Value::Parameter(0)); - returnParam.Encode(); - - // int returnStringLength(String x) { return x.length(); } - auto string_type{TypeDescriptor::FromClassname("java.lang.String")}; - MethodDeclData string_length{ - dex_file.GetOrDeclareMethod(string_type, "length", Prototype{TypeDescriptor::Int()})}; - - auto returnStringLength{ - cbuilder.CreateMethod("returnStringLength", Prototype{TypeDescriptor::Int(), string_type})}; - { - LiveRegister result = returnStringLength.AllocRegister(); - returnStringLength.AddInstruction( - Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0))); - returnStringLength.BuildReturn(result); - } - returnStringLength.Encode(); - - // int returnIfZero(int x) { if (x == 0) { return 5; } else { return 3; } } - MethodBuilder returnIfZero{cbuilder.CreateMethod( - "returnIfZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})}; - { - LiveRegister resultIfZero{returnIfZero.AllocRegister()}; - Value else_target{returnIfZero.MakeLabel()}; - returnIfZero.AddInstruction(Instruction::OpWithArgs( - Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target)); - // else branch - returnIfZero.BuildConst4(resultIfZero, 3); - returnIfZero.AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kReturn, /*dest=*/{}, resultIfZero)); - // then branch - returnIfZero.AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target)); - returnIfZero.BuildConst4(resultIfZero, 5); - returnIfZero.AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kReturn, /*dest=*/{}, resultIfZero)); - } - returnIfZero.Encode(); - - // int returnIfNotZero(int x) { if (x != 0) { return 5; } else { return 3; } } - MethodBuilder returnIfNotZero{cbuilder.CreateMethod( - "returnIfNotZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})}; - { - LiveRegister resultIfNotZero{returnIfNotZero.AllocRegister()}; - Value else_target{returnIfNotZero.MakeLabel()}; - returnIfNotZero.AddInstruction(Instruction::OpWithArgs( - Instruction::Op::kBranchNEqz, /*dest=*/{}, Value::Parameter(0), else_target)); - // else branch - returnIfNotZero.BuildConst4(resultIfNotZero, 3); - returnIfNotZero.AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kReturn, /*dest=*/{}, resultIfNotZero)); - // then branch - returnIfNotZero.AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target)); - returnIfNotZero.BuildConst4(resultIfNotZero, 5); - returnIfNotZero.AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kReturn, /*dest=*/{}, resultIfNotZero)); - } - returnIfNotZero.Encode(); - - // Make sure backwards branches work too. - // - // Pseudo code for test: - // { - // zero = 0; - // result = 1; - // if (zero == 0) goto B; - // A: - // return result; - // B: - // result = 2; - // if (zero == 0) goto A; - // result = 3; - // return result; - // } - // If it runs correctly, this test should return 2. - MethodBuilder backwardsBranch{ - cbuilder.CreateMethod("backwardsBranch", Prototype{TypeDescriptor::Int()})}; - [](MethodBuilder& method) { - LiveRegister zero = method.AllocRegister(); - LiveRegister result = method.AllocRegister(); - Value labelA = method.MakeLabel(); - Value labelB = method.MakeLabel(); - method.BuildConst4(zero, 0); - method.BuildConst4(result, 1); - method.AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kBranchEqz, /*dest=*/{}, zero, labelB)); - - method.AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, labelA)); - method.BuildReturn(result); - - method.AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, labelB)); - method.BuildConst4(result, 2); - method.AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kBranchEqz, /*dest=*/{}, zero, labelA)); - - method.BuildConst4(result, 3); - method.BuildReturn(result); - }(backwardsBranch); - backwardsBranch.Encode(); - - // Test that we can make a null value. Basically: - // - // public static String returnNull() { return null; } - MethodBuilder returnNull{cbuilder.CreateMethod("returnNull", Prototype{string_type})}; - [](MethodBuilder& method) { - LiveRegister zero = method.AllocRegister(); - method.BuildConst4(zero, 0); - method.BuildReturn(zero, /*is_object=*/true); - }(returnNull); - returnNull.Encode(); - - // Test that we can make String literals. Basically: - // - // public static String makeString() { return "Hello, World!"; } - MethodBuilder makeString{cbuilder.CreateMethod("makeString", Prototype{string_type})}; - [](MethodBuilder& method) { - LiveRegister string = method.AllocRegister(); - method.BuildConstString(string, "Hello, World!"); - method.BuildReturn(string, /*is_object=*/true); - }(makeString); - makeString.Encode(); - - // Make sure strings are sorted correctly. - // - // int returnStringIfZeroAB(int x) { if (x == 0) { return "a"; } else { return "b"; } } - MethodBuilder returnStringIfZeroAB{ - cbuilder.CreateMethod("returnStringIfZeroAB", Prototype{string_type, TypeDescriptor::Int()})}; - [&](MethodBuilder& method) { - LiveRegister resultIfZero{method.AllocRegister()}; - Value else_target{method.MakeLabel()}; - method.AddInstruction(Instruction::OpWithArgs( - Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target)); - // else branch - method.BuildConstString(resultIfZero, "b"); - method.AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero)); - // then branch - method.AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target)); - method.BuildConstString(resultIfZero, "a"); - method.AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero)); - method.Encode(); - }(returnStringIfZeroAB); - // int returnStringIfZeroAB(int x) { if (x == 0) { return "b"; } else { return "a"; } } - MethodBuilder returnStringIfZeroBA{ - cbuilder.CreateMethod("returnStringIfZeroBA", Prototype{string_type, TypeDescriptor::Int()})}; - [&](MethodBuilder& method) { - LiveRegister resultIfZero{method.AllocRegister()}; - Value else_target{method.MakeLabel()}; - method.AddInstruction(Instruction::OpWithArgs( - Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target)); - // else branch - method.BuildConstString(resultIfZero, "a"); - method.AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero)); - // then branch - method.AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target)); - method.BuildConstString(resultIfZero, "b"); - method.AddInstruction( - Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero)); - method.Encode(); - }(returnStringIfZeroBA); - - // Make sure we can invoke static methods that return an object - // String invokeStaticReturnObject(int n, int radix) { return java.lang.Integer.toString(n, - // radix); } - MethodBuilder invokeStaticReturnObject{ - cbuilder.CreateMethod("invokeStaticReturnObject", - Prototype{string_type, TypeDescriptor::Int(), TypeDescriptor::Int()})}; - [&](MethodBuilder& method) { - LiveRegister result{method.AllocRegister()}; - MethodDeclData to_string{dex_file.GetOrDeclareMethod( - TypeDescriptor::FromClassname("java.lang.Integer"), - "toString", - Prototype{string_type, TypeDescriptor::Int(), TypeDescriptor::Int()})}; - method.AddInstruction(Instruction::InvokeStaticObject( - to_string.id, result, Value::Parameter(0), Value::Parameter(1))); - method.BuildReturn(result, /*is_object=*/true); - method.Encode(); - }(invokeStaticReturnObject); - - // Make sure we can invoke virtual methods that return an object - // String invokeVirtualReturnObject(String s, int n) { return s.substring(n); } - MethodBuilder invokeVirtualReturnObject{cbuilder.CreateMethod( - "invokeVirtualReturnObject", Prototype{string_type, string_type, TypeDescriptor::Int()})}; - [&](MethodBuilder& method) { - LiveRegister result{method.AllocRegister()}; - MethodDeclData substring{dex_file.GetOrDeclareMethod( - string_type, "substring", Prototype{string_type, TypeDescriptor::Int()})}; - method.AddInstruction(Instruction::InvokeVirtualObject( - substring.id, result, Value::Parameter(0), Value::Parameter(1))); - method.BuildReturn(result, /*is_object=*/true); - method.Encode(); - }(invokeVirtualReturnObject); - - // Make sure we can cast objects - // String castObjectToString(Object o) { return (String)o; } - MethodBuilder castObjectToString{cbuilder.CreateMethod( - "castObjectToString", - Prototype{string_type, TypeDescriptor::FromClassname("java.lang.Object")})}; - [&](MethodBuilder& method) { - const ir::Type* type_def = dex_file.GetOrAddType(string_type.descriptor()); - method.AddInstruction( - Instruction::Cast(Value::Parameter(0), Value::Type(type_def->orig_index))); - method.BuildReturn(Value::Parameter(0), /*is_object=*/true); - method.Encode(); - }(castObjectToString); - - TypeDescriptor test_class = TypeDescriptor::FromClassname("android.startop.test.TestClass"); - - // Read a static field - // int readStaticField() { return TestClass.staticInteger; } - MethodBuilder readStaticField{ - cbuilder.CreateMethod("readStaticField", Prototype{TypeDescriptor::Int()})}; - [&](MethodBuilder& method) { - const ir::FieldDecl* field = - dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int()); - LiveRegister result{method.AllocRegister()}; - method.AddInstruction(Instruction::GetStaticField(field->orig_index, result)); - method.BuildReturn(result, /*is_object=*/false); - method.Encode(); - }(readStaticField); - - // Set a static field - // void setStaticField() { TestClass.staticInteger = 7; } - MethodBuilder setStaticField{ - cbuilder.CreateMethod("setStaticField", Prototype{TypeDescriptor::Void()})}; - [&](MethodBuilder& method) { - const ir::FieldDecl* field = - dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int()); - LiveRegister number{method.AllocRegister()}; - method.BuildConst4(number, 7); - method.AddInstruction(Instruction::SetStaticField(field->orig_index, number)); - method.BuildReturn(); - method.Encode(); - }(setStaticField); - - // Read an instance field - // int readInstanceField(TestClass obj) { return obj.instanceField; } - MethodBuilder readInstanceField{ - cbuilder.CreateMethod("readInstanceField", Prototype{TypeDescriptor::Int(), test_class})}; - [&](MethodBuilder& method) { - const ir::FieldDecl* field = - dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int()); - LiveRegister result{method.AllocRegister()}; - method.AddInstruction(Instruction::GetField(field->orig_index, result, Value::Parameter(0))); - method.BuildReturn(result, /*is_object=*/false); - method.Encode(); - }(readInstanceField); - - // Set an instance field - // void setInstanceField(TestClass obj) { obj.instanceField = 7; } - MethodBuilder setInstanceField{ - cbuilder.CreateMethod("setInstanceField", Prototype{TypeDescriptor::Void(), test_class})}; - [&](MethodBuilder& method) { - const ir::FieldDecl* field = - dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int()); - LiveRegister number{method.AllocRegister()}; - method.BuildConst4(number, 7); - method.AddInstruction(Instruction::SetField(field->orig_index, Value::Parameter(0), number)); - method.BuildReturn(); - method.Encode(); - }(setInstanceField); - - slicer::MemView image{dex_file.CreateImage()}; - std::ofstream out_file(outdir + "/simple.dex"); - out_file.write(image.ptr<const char>(), image.size()); -} - -int main(int argc, char** argv) { - CHECK_EQ(argc, 2); - - string outdir = argv[1]; - - GenerateTrivialDexFile(outdir); - GenerateSimpleTestCases(outdir); -} diff --git a/startop/view_compiler/java_lang_builder.cc b/startop/view_compiler/java_lang_builder.cc deleted file mode 100644 index 920caeecf58e..000000000000 --- a/startop/view_compiler/java_lang_builder.cc +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#include "java_lang_builder.h" - -#include "android-base/stringprintf.h" - -using android::base::StringPrintf; -using std::string; - -void JavaLangViewBuilder::Start() const { - out_ << StringPrintf("package %s;\n", package_.c_str()) - << "import android.content.Context;\n" - "import android.content.res.Resources;\n" - "import android.content.res.XmlResourceParser;\n" - "import android.util.AttributeSet;\n" - "import android.util.Xml;\n" - "import android.view.*;\n" - "import android.widget.*;\n" - "\n" - "public final class CompiledView {\n" - "\n" - "static <T extends View> T createView(Context context, AttributeSet attrs, View parent, " - "String name, LayoutInflater.Factory factory, LayoutInflater.Factory2 factory2) {" - "\n" - " if (factory2 != null) {\n" - " return (T)factory2.onCreateView(parent, name, context, attrs);\n" - " } else if (factory != null) {\n" - " return (T)factory.onCreateView(name, context, attrs);\n" - " }\n" - // TODO: find a way to call the private factory - " return null;\n" - "}\n" - "\n" - " public static View inflate(Context context) {\n" - " try {\n" - " LayoutInflater inflater = LayoutInflater.from(context);\n" - " LayoutInflater.Factory factory = inflater.getFactory();\n" - " LayoutInflater.Factory2 factory2 = inflater.getFactory2();\n" - " Resources res = context.getResources();\n" - << StringPrintf(" XmlResourceParser xml = res.getLayout(%s.R.layout.%s);\n", - package_.c_str(), - layout_name_.c_str()) - << " AttributeSet attrs = Xml.asAttributeSet(xml);\n" - // The Java-language XmlPullParser needs a call to next to find the start document tag. - " xml.next(); // start document\n"; -} - -void JavaLangViewBuilder::Finish() const { - out_ << " } catch (Exception e) {\n" - " return null;\n" - " }\n" // end try - " }\n" // end inflate - "}\n"; // end CompiledView -} - -void JavaLangViewBuilder::StartView(const string& class_name, bool /*is_viewgroup*/) { - const string view_var = MakeVar("view"); - const string layout_var = MakeVar("layout"); - std::string parent = "null"; - if (!view_stack_.empty()) { - const StackEntry& parent_entry = view_stack_.back(); - parent = parent_entry.view_var; - } - out_ << " xml.next(); // <" << class_name << ">\n" - << StringPrintf(" %s %s = createView(context, attrs, %s, \"%s\", factory, factory2);\n", - class_name.c_str(), - view_var.c_str(), - parent.c_str(), - class_name.c_str()) - << StringPrintf(" if (%s == null) %s = new %s(context, attrs);\n", - view_var.c_str(), - view_var.c_str(), - class_name.c_str()); - if (!view_stack_.empty()) { - out_ << StringPrintf(" ViewGroup.LayoutParams %s = %s.generateLayoutParams(attrs);\n", - layout_var.c_str(), - parent.c_str()); - } - view_stack_.push_back({class_name, view_var, layout_var}); -} - -void JavaLangViewBuilder::FinishView() { - const StackEntry var = view_stack_.back(); - view_stack_.pop_back(); - if (!view_stack_.empty()) { - const string& parent = view_stack_.back().view_var; - out_ << StringPrintf(" xml.next(); // </%s>\n", var.class_name.c_str()) - << StringPrintf(" %s.addView(%s, %s);\n", - parent.c_str(), - var.view_var.c_str(), - var.layout_params_var.c_str()); - } else { - out_ << StringPrintf(" return %s;\n", var.view_var.c_str()); - } -} - -const std::string JavaLangViewBuilder::MakeVar(std::string prefix) { - std::stringstream v; - v << prefix << view_id_++; - return v.str(); -} diff --git a/startop/view_compiler/java_lang_builder.h b/startop/view_compiler/java_lang_builder.h deleted file mode 100644 index 69356d3a6594..000000000000 --- a/startop/view_compiler/java_lang_builder.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ -#ifndef JAVA_LANG_BUILDER_H_ -#define JAVA_LANG_BUILDER_H_ - -#include <iostream> -#include <sstream> -#include <vector> - -// Build Java language code to instantiate views. -// -// This has a very small interface to make it easier to generate additional -// backends, such as a direct-to-DEX version. -class JavaLangViewBuilder { - public: - JavaLangViewBuilder(std::string package, std::string layout_name, std::ostream& out = std::cout) - : package_(package), layout_name_(layout_name), out_(out) {} - - // Begin generating a class. Adds the package boilerplate, etc. - void Start() const; - // Finish generating a class, closing off any open curly braces, etc. - void Finish() const; - - // Begin creating a view (i.e. process the opening tag) - void StartView(const std::string& class_name, bool is_viewgroup); - // Finish a view, after all of its child nodes have been processed. - void FinishView(); - - private: - const std::string MakeVar(std::string prefix); - - std::string const package_; - std::string const layout_name_; - - std::ostream& out_; - - size_t view_id_ = 0; - - struct StackEntry { - // The class name for this view object - const std::string class_name; - - // The variable name that is holding the view object - const std::string view_var; - - // The variable name that holds the object's layout parameters - const std::string layout_params_var; - }; - std::vector<StackEntry> view_stack_; -}; - -#endif // JAVA_LANG_BUILDER_H_ diff --git a/startop/view_compiler/layout_validation.cc b/startop/view_compiler/layout_validation.cc deleted file mode 100644 index 8c7737749124..000000000000 --- a/startop/view_compiler/layout_validation.cc +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#include "layout_validation.h" - -#include "android-base/stringprintf.h" - -namespace startop { - -void LayoutValidationVisitor::VisitStartTag(const std::u16string& name) { - if (0 == name.compare(u"merge")) { - message_ = "Merge tags are not supported"; - can_compile_ = false; - } - if (0 == name.compare(u"include")) { - message_ = "Include tags are not supported"; - can_compile_ = false; - } - if (0 == name.compare(u"view")) { - message_ = "View tags are not supported"; - can_compile_ = false; - } - if (0 == name.compare(u"fragment")) { - message_ = "Fragment tags are not supported"; - can_compile_ = false; - } -} - -} // namespace startop \ No newline at end of file diff --git a/startop/view_compiler/layout_validation.h b/startop/view_compiler/layout_validation.h deleted file mode 100644 index bed34bb38e5e..000000000000 --- a/startop/view_compiler/layout_validation.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#ifndef LAYOUT_VALIDATION_H_ -#define LAYOUT_VALIDATION_H_ - -#include "dex_builder.h" - -#include <string> - -namespace startop { - -// This visitor determines whether a layout can be compiled. Since we do not currently support all -// features, such as includes and merges, we need to pre-validate the layout before we start -// compiling. -class LayoutValidationVisitor { - public: - void VisitStartDocument() const {} - void VisitEndDocument() const {} - void VisitStartTag(const std::u16string& name); - void VisitEndTag() const {} - - const std::string& message() const { return message_; } - bool can_compile() const { return can_compile_; } - - private: - std::string message_{"Okay"}; - bool can_compile_{true}; -}; - -} // namespace startop - -#endif // LAYOUT_VALIDATION_H_ diff --git a/startop/view_compiler/layout_validation_test.cc b/startop/view_compiler/layout_validation_test.cc deleted file mode 100644 index b74cdae8d725..000000000000 --- a/startop/view_compiler/layout_validation_test.cc +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ -#include "tinyxml_layout_parser.h" - -#include "gtest/gtest.h" - -using startop::CanCompileLayout; -using std::string; - -namespace { -void ValidateXmlText(const string& xml, bool expected) { - tinyxml2::XMLDocument doc; - doc.Parse(xml.c_str()); - EXPECT_EQ(CanCompileLayout(doc), expected); -} -} // namespace - -TEST(LayoutValidationTest, SingleButtonLayout) { - const string xml = R"(<?xml version="1.0" encoding="utf-8"?> -<Button xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:text="Hello, World!"> - -</Button>)"; - ValidateXmlText(xml, /*expected=*/true); -} - -TEST(LayoutValidationTest, SmallConstraintLayout) { - const string xml = R"(<?xml version="1.0" encoding="utf-8"?> -<android.support.constraint.ConstraintLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <Button - android:id="@+id/button6" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="16dp" - android:layout_marginBottom="16dp" - android:text="Button" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" /> - - <Button - android:id="@+id/button7" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="8dp" - android:layout_marginBottom="16dp" - android:text="Button2" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@+id/button6" /> - - <Button - android:id="@+id/button8" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="8dp" - android:layout_marginBottom="16dp" - android:text="Button1" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@+id/button7" /> -</android.support.constraint.ConstraintLayout>)"; - ValidateXmlText(xml, /*expected=*/true); -} - -TEST(LayoutValidationTest, MergeNode) { - const string xml = R"(<?xml version="1.0" encoding="utf-8"?> -<merge xmlns:android="http://schemas.android.com/apk/res/android"> - - <TextView - android:id="@+id/textView3" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="TextView" /> - - <Button - android:id="@+id/button9" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Button" /> -</merge>)"; - ValidateXmlText(xml, /*expected=*/false); -} - -TEST(LayoutValidationTest, IncludeLayout) { - const string xml = R"(<?xml version="1.0" encoding="utf-8"?> -<android.support.constraint.ConstraintLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <include - layout="@layout/single_button_layout" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> -</android.support.constraint.ConstraintLayout>)"; - ValidateXmlText(xml, /*expected=*/false); -} - -TEST(LayoutValidationTest, ViewNode) { - const string xml = R"(<?xml version="1.0" encoding="utf-8"?> -<android.support.constraint.ConstraintLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <view - class="android.support.design.button.MaterialButton" - id="@+id/view" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> -</android.support.constraint.ConstraintLayout>)"; - ValidateXmlText(xml, /*expected=*/false); -} - -TEST(LayoutValidationTest, FragmentNode) { - // This test case is from https://developer.android.com/guide/components/fragments - const string xml = R"(<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <fragment android:name="com.example.news.ArticleListFragment" - android:id="@+id/list" - android:layout_weight="1" - android:layout_width="0dp" - android:layout_height="match_parent" /> - <fragment android:name="com.example.news.ArticleReaderFragment" - android:id="@+id/viewer" - android:layout_weight="2" - android:layout_width="0dp" - android:layout_height="match_parent" /> -</LinearLayout>)"; - ValidateXmlText(xml, /*expected=*/false); -} diff --git a/startop/view_compiler/main.cc b/startop/view_compiler/main.cc deleted file mode 100644 index 11ecde27f5cd..000000000000 --- a/startop/view_compiler/main.cc +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#include "gflags/gflags.h" - -#include "android-base/stringprintf.h" -#include "apk_layout_compiler.h" -#include "dex_builder.h" -#include "dex_layout_compiler.h" -#include "java_lang_builder.h" -#include "layout_validation.h" -#include "tinyxml_layout_parser.h" -#include "util.h" - -#include "tinyxml2.h" - -#include <fstream> -#include <iostream> -#include <sstream> -#include <string> -#include <vector> - -namespace { - -using namespace tinyxml2; -using android::base::StringPrintf; -using startop::dex::ClassBuilder; -using startop::dex::DexBuilder; -using startop::dex::MethodBuilder; -using startop::dex::Prototype; -using startop::dex::TypeDescriptor; -using namespace startop::util; -using std::string; - -constexpr char kStdoutFilename[]{"stdout"}; - -DEFINE_bool(apk, false, "Compile layouts in an APK"); -DEFINE_bool(dex, false, "Generate a DEX file instead of Java"); -DEFINE_int32(infd, -1, "Read input from the given file descriptor"); -DEFINE_string(out, kStdoutFilename, "Where to write the generated class"); -DEFINE_string(package, "", "The package name for the generated class (required)"); - -template <typename Visitor> -class XmlVisitorAdapter : public XMLVisitor { - public: - explicit XmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {} - - bool VisitEnter(const XMLDocument& /*doc*/) override { - visitor_->VisitStartDocument(); - return true; - } - - bool VisitExit(const XMLDocument& /*doc*/) override { - visitor_->VisitEndDocument(); - return true; - } - - bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override { - visitor_->VisitStartTag( - std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes( - element.Name())); - return true; - } - - bool VisitExit(const XMLElement& /*element*/) override { - visitor_->VisitEndTag(); - return true; - } - - private: - Visitor* visitor_; -}; - -template <typename Builder> -void CompileLayout(XMLDocument* xml, Builder* builder) { - startop::LayoutCompilerVisitor visitor{builder}; - XmlVisitorAdapter<decltype(visitor)> adapter{&visitor}; - xml->Accept(&adapter); -} - -} // end namespace - -int main(int argc, char** argv) { - constexpr size_t kProgramName = 0; - constexpr size_t kFileNameParam = 1; - constexpr size_t kNumRequiredArgs = 1; - - gflags::SetUsageMessage( - "Compile XML layout files into equivalent Java language code\n" - "\n" - " example usage: viewcompiler layout.xml --package com.example.androidapp"); - gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags*/ true); - - gflags::CommandLineFlagInfo cmd = gflags::GetCommandLineFlagInfoOrDie("package"); - if (argc < kNumRequiredArgs || cmd.is_default) { - gflags::ShowUsageWithFlags(argv[kProgramName]); - return 1; - } - - const bool is_stdout = FLAGS_out == kStdoutFilename; - - std::ofstream outfile; - if (!is_stdout) { - outfile.open(FLAGS_out); - } - - if (FLAGS_apk) { - const startop::CompilationTarget target = - FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage; - if (FLAGS_infd >= 0) { - startop::CompileApkLayoutsFd( - android::base::unique_fd{FLAGS_infd}, target, is_stdout ? std::cout : outfile); - } else { - if (argc < 2) { - gflags::ShowUsageWithFlags(argv[kProgramName]); - return 1; - } - const char* const filename = argv[kFileNameParam]; - startop::CompileApkLayouts(filename, target, is_stdout ? std::cout : outfile); - } - return 0; - } - - const char* const filename = argv[kFileNameParam]; - const string layout_name = startop::util::FindLayoutNameFromFilename(filename); - - XMLDocument xml; - xml.LoadFile(filename); - - string message{}; - if (!startop::CanCompileLayout(xml, &message)) { - LOG(ERROR) << "Layout not supported: " << message; - return 1; - } - - if (FLAGS_dex) { - DexBuilder dex_file; - string class_name = StringPrintf("%s.CompiledView", FLAGS_package.c_str()); - ClassBuilder compiled_view{dex_file.MakeClass(class_name)}; - MethodBuilder method{compiled_view.CreateMethod( - layout_name, - Prototype{TypeDescriptor::FromClassname("android.view.View"), - TypeDescriptor::FromClassname("android.content.Context"), - TypeDescriptor::Int()})}; - startop::DexViewBuilder builder{&method}; - CompileLayout(&xml, &builder); - method.Encode(); - - slicer::MemView image{dex_file.CreateImage()}; - - (is_stdout ? std::cout : outfile).write(image.ptr<const char>(), image.size()); - } else { - // Generate Java language output. - JavaLangViewBuilder builder{FLAGS_package, layout_name, is_stdout ? std::cout : outfile}; - - CompileLayout(&xml, &builder); - } - return 0; -} diff --git a/startop/view_compiler/tinyxml_layout_parser.cc b/startop/view_compiler/tinyxml_layout_parser.cc deleted file mode 100644 index 1b3a81f17976..000000000000 --- a/startop/view_compiler/tinyxml_layout_parser.cc +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ -#include "tinyxml_layout_parser.h" - -#include "layout_validation.h" - -namespace startop { - -bool CanCompileLayout(const tinyxml2::XMLDocument& xml, std::string* message) { - LayoutValidationVisitor validator; - TinyXmlVisitorAdapter adapter{&validator}; - xml.Accept(&adapter); - - if (message != nullptr) { - *message = validator.message(); - } - - return validator.can_compile(); -} - -} // namespace startop diff --git a/startop/view_compiler/tinyxml_layout_parser.h b/startop/view_compiler/tinyxml_layout_parser.h deleted file mode 100644 index 8f714a2c5a3f..000000000000 --- a/startop/view_compiler/tinyxml_layout_parser.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ -#ifndef TINYXML_LAYOUT_PARSER_H_ -#define TINYXML_LAYOUT_PARSER_H_ - -#include "tinyxml2.h" - -#include <codecvt> -#include <locale> -#include <string> - -namespace startop { - -template <typename Visitor> -class TinyXmlVisitorAdapter : public tinyxml2::XMLVisitor { - public: - explicit TinyXmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {} - - bool VisitEnter(const tinyxml2::XMLDocument& /*doc*/) override { - visitor_->VisitStartDocument(); - return true; - } - - bool VisitExit(const tinyxml2::XMLDocument& /*doc*/) override { - visitor_->VisitEndDocument(); - return true; - } - - bool VisitEnter(const tinyxml2::XMLElement& element, - const tinyxml2::XMLAttribute* /*firstAttribute*/) override { - visitor_->VisitStartTag( - std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes( - element.Name())); - return true; - } - - bool VisitExit(const tinyxml2::XMLElement& /*element*/) override { - visitor_->VisitEndTag(); - return true; - } - - private: - Visitor* visitor_; -}; - -// Returns whether a layout resource represented by a TinyXML document is supported by the layout -// compiler. -bool CanCompileLayout(const tinyxml2::XMLDocument& xml, std::string* message = nullptr); - -} // namespace startop - -#endif // TINYXML_LAYOUT_PARSER_H_ diff --git a/startop/view_compiler/util.cc b/startop/view_compiler/util.cc deleted file mode 100644 index c34d7b059cfc..000000000000 --- a/startop/view_compiler/util.cc +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#include "util.h" - -using std::string; - -namespace startop { -namespace util { - -// TODO: see if we can borrow this from somewhere else, like aapt2. -string FindLayoutNameFromFilename(const string& filename) { - size_t start = filename.rfind('/'); - if (start == string::npos) { - start = 0; - } else { - start++; // advance past '/' character - } - size_t end = filename.find('.', start); - - return filename.substr(start, end - start); -} - -} // namespace util -} // namespace startop diff --git a/startop/view_compiler/util.h b/startop/view_compiler/util.h deleted file mode 100644 index 0176175920c1..000000000000 --- a/startop/view_compiler/util.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ -#ifndef VIEW_COMPILER_UTIL_H_ -#define VIEW_COMPILER_UTIL_H_ - -#include <string> - -namespace startop { -namespace util { - -std::string FindLayoutNameFromFilename(const std::string& filename); - -} // namespace util -} // namespace startop - -#endif // VIEW_COMPILER_UTIL_H_ diff --git a/startop/view_compiler/util_test.cc b/startop/view_compiler/util_test.cc deleted file mode 100644 index 50682a04e3b1..000000000000 --- a/startop/view_compiler/util_test.cc +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#include "util.h" - -#include "gtest/gtest.h" - -using std::string; - -namespace startop { -namespace util { - -TEST(UtilTest, FindLayoutNameFromFilename) { - EXPECT_EQ("bar", startop::util::FindLayoutNameFromFilename("foo/bar.xml")); - EXPECT_EQ("bar", startop::util::FindLayoutNameFromFilename("bar.xml")); - EXPECT_EQ("bar", startop::util::FindLayoutNameFromFilename("./foo/bar.xml")); - EXPECT_EQ("bar", startop::util::FindLayoutNameFromFilename("/foo/bar.xml")); -} - -} // namespace util -} // namespace startop -- GitLab