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); + final XmlResourceParser childParser = context.getResources().getLayout(layout); + try { + final AttributeSet childAttrs = Xml.asAttributeSet(childParser); - try { - final AttributeSet childAttrs = Xml.asAttributeSet(childParser); - - while ((type = childParser.next()) != XmlPullParser.START_TAG && - type != XmlPullParser.END_DOCUMENT) { - // Empty. - } - - if (type != XmlPullParser.START_TAG) { - throw new InflateException(getParserStateDescription(context, childAttrs) - + ": No start tag found!"); - } - - final String childName = childParser.getName(); - - if (TAG_MERGE.equals(childName)) { - // The 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 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 - // 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); - - // Inflate all children. - rInflateChildren(childParser, view, childAttrs, true); - - if (id != View.NO_ID) { - view.setId(id); - } - - switch (visibility) { - case 0: - view.setVisibility(View.VISIBLE); - break; - case 1: - view.setVisibility(View.INVISIBLE); - break; - case 2: - view.setVisibility(View.GONE); - break; - } - - group.addView(view); - } - } finally { - childParser.close(); + while ((type = childParser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + // Empty. } + + if (type != XmlPullParser.START_TAG) { + throw new InflateException(getParserStateDescription(context, childAttrs) + + ": No start tag found!"); + } + + final String childName = childParser.getName(); + + if (TAG_MERGE.equals(childName)) { + // The 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 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 + // 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); + + // Inflate all children. + rInflateChildren(childParser, view, childAttrs, true); + + if (id != View.NO_ID) { + view.setId(id); + } + + switch (visibility) { + case 0: + view.setVisibility(View.VISIBLE); + break; + case 1: + view.setVisibility(View.INVISIBLE); + break; + case 2: + view.setVisibility(View.GONE); + break; + } + + 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 mArtManagerServiceProducer; private final Singleton mApexManagerProducer; - private final Singleton mViewCompilerProducer; private final Singleton mIncrementalManagerProducer; private final Singleton @@ -164,7 +162,6 @@ public class PackageManagerServiceInjector { Producer dynamicCodeLoggerProducer, Producer artManagerServiceProducer, Producer apexManagerProducer, - Producer viewCompilerProducer, Producer incrementalManagerProducer, Producer defaultAppProviderProducer, Producer 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 -#include - -#include "android-base/stringprintf.h" - -namespace startop { - -using android::ResXMLParser; -using android::base::StringPrintf; - -class ResXmlVisitorAdapter { - public: - ResXmlVisitorAdapter(ResXMLParser* parser) : parser_{parser} {} - - template - 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 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(), 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 - -#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 -#include - -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 buffer = std::make_unique(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(), image.size()); -} - -TypeDescriptor TypeDescriptor::FromClassname(const std::string& name) { - return TypeDescriptor{DotToDescriptor(name.c_str())}; -} - -DexBuilder::DexBuilder() : dex_file_{std::make_shared()} { - 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(string.size() + kMaxEncodedStringLength + 1); - uint8_t* string_data_start = ::dex::WriteULeb128(buffer.get(), string.size()); - - size_t header_length = - reinterpret_cast(string_data_start) - reinterpret_cast(buffer.get()); - - auto end = std::copy(string.begin(), string.end(), string_data_start); - *end = '\0'; - - entry = Alloc(); - // +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::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(); - 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(); - 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(); - proto->shorty = dex->GetOrAddString(Shorty()); - proto->return_type = dex->GetOrAddType(return_type_.descriptor()); - if (param_types_.size() > 0) { - proto->param_types = dex->Alloc(); - 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(); - method->decl = decl_; - - // TODO: make access flags configurable - method->access_flags = kAccPublic | ::dex::kAccStatic; - - auto* code = dex_->Alloc(); - 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(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 scratch = GetScratchRegisters(); - - 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(); - // 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 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 -#include -#include -#include -#include -#include -#include - -#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> 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 - 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 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* liveness, size_t index) : liveness_{liveness}, index_{index} {} - - std::vector* const liveness_; - std::optional 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 - static inline Instruction OpWithArgs(Op opcode, std::optional 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 - static inline Instruction InvokeVirtual(size_t index_argument, std::optional dest, - Value this_arg, T... args) { - return Instruction{ - Op::kInvokeVirtual, index_argument, /*result_is_object=*/false, dest, this_arg, args...}; - } - // Returns an object - template - static inline Instruction InvokeVirtualObject(size_t index_argument, - std::optional 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 - static inline Instruction InvokeDirect(size_t index_argument, std::optional 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 - static inline Instruction InvokeDirectObject(size_t index_argument, - std::optional dest, Value this_arg, - T... args) { - return Instruction{ - Op::kInvokeDirect, index_argument, /*result_is_object=*/true, dest, this_arg, args...}; - } - // For static calls. - template - static inline Instruction InvokeStatic(size_t index_argument, std::optional dest, - T... args) { - return Instruction{ - Op::kInvokeStatic, index_argument, /*result_is_object=*/false, dest, args...}; - } - // Returns an object - template - static inline Instruction InvokeStaticObject(size_t index_argument, - std::optional dest, T... args) { - return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/true, dest, args...}; - } - // For static calls. - template - static inline Instruction InvokeInterface(size_t index_argument, std::optional 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& dest() const { return dest_; } - const std::vector& args() const { return args_; } - - private: - inline Instruction(Op opcode, size_t index_argument, std::optional dest) - : opcode_{opcode}, - index_argument_{index_argument}, - result_is_object_{false}, - dest_{dest}, - args_{} {} - - template - inline Instruction(Op opcode, size_t index_argument, bool result_is_object, - std::optional 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 dest_; - const std::vector 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 - 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(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 - std::array GetScratchRegisters() const { - static_assert(num_regs <= kMaxScratchRegisters); - std::array 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 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 bound_address; - std::forward_list references; - }; - - std::vector 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 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 - T* Alloc() { - return dex_file_->Alloc(); - } - - // 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 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 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> string_data_; - - // Keep track of what types we've defined so we can look them up later. - std::unordered_map 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 method_id_map_; - - // Keep track of what strings we've defined so we can look them up later. - std::unordered_map strings_; - - // Keep track of already-encoded protos. - std::map proto_map_; - - // Keep track of fields that have been declared - std::map, ir::FieldDecl*> field_decls_by_key_; -}; - -template -void MethodBuilder::BuildNew(Value target, TypeDescriptor type, Prototype constructor, - const T&... args) { - MethodDeclData constructor_data{dex_->GetOrDeclareMethod(type, "", 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 @@ - - - - - - - - - - - - - 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 @@ - - - - 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 @@ - - - - )"; - ValidateXmlText(xml, /*expected=*/true); -} - -TEST(LayoutValidationTest, SmallConstraintLayout) { - const string xml = R"( - - -