blob: bddb8aa6716a31bcc7bea7f77f39cf65fb7c5437 [file] [log] [blame]
Eric Holk42734572018-12-17 15:46:18 -08001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "dex_layout_compiler.h"
18#include "layout_validation.h"
19
20#include "android-base/stringprintf.h"
21
22namespace startop {
23
24using android::base::StringPrintf;
Eric Holk5c6a1a52019-09-17 13:28:34 -070025using dex::Instruction;
26using dex::LiveRegister;
27using dex::Prototype;
28using dex::TypeDescriptor;
29using dex::Value;
30
31namespace {
32// TODO: these are a bunch of static initializers, which we should avoid. See if
33// we can make them constexpr.
34const TypeDescriptor kAttributeSet = TypeDescriptor::FromClassname("android.util.AttributeSet");
35const TypeDescriptor kContext = TypeDescriptor::FromClassname("android.content.Context");
36const TypeDescriptor kLayoutInflater = TypeDescriptor::FromClassname("android.view.LayoutInflater");
37const TypeDescriptor kResources = TypeDescriptor::FromClassname("android.content.res.Resources");
38const TypeDescriptor kString = TypeDescriptor::FromClassname("java.lang.String");
39const TypeDescriptor kView = TypeDescriptor::FromClassname("android.view.View");
40const TypeDescriptor kViewGroup = TypeDescriptor::FromClassname("android.view.ViewGroup");
41const TypeDescriptor kXmlResourceParser =
42 TypeDescriptor::FromClassname("android.content.res.XmlResourceParser");
43} // namespace
Eric Holk42734572018-12-17 15:46:18 -080044
Eric Holk42734572018-12-17 15:46:18 -080045DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method)
46 : method_{method},
Eric Holk5c6a1a52019-09-17 13:28:34 -070047 context_{Value::Parameter(0)},
48 resid_{Value::Parameter(1)},
49 inflater_{method->AllocRegister()},
50 xml_{method->AllocRegister()},
51 attrs_{method->AllocRegister()},
52 classname_tmp_{method->AllocRegister()},
53 xml_next_{method->dex_file()->GetOrDeclareMethod(kXmlResourceParser, "next",
54 Prototype{TypeDescriptor::Int()})},
Eric Holk42734572018-12-17 15:46:18 -080055 try_create_view_{method->dex_file()->GetOrDeclareMethod(
Eric Holk5c6a1a52019-09-17 13:28:34 -070056 kLayoutInflater, "tryCreateView",
57 Prototype{kView, kView, kString, kContext, kAttributeSet})},
Eric Holk42734572018-12-17 15:46:18 -080058 generate_layout_params_{method->dex_file()->GetOrDeclareMethod(
Eric Holk5c6a1a52019-09-17 13:28:34 -070059 kViewGroup, "generateLayoutParams",
60 Prototype{TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"),
61 kAttributeSet})},
Eric Holk42734572018-12-17 15:46:18 -080062 add_view_{method->dex_file()->GetOrDeclareMethod(
Eric Holk5c6a1a52019-09-17 13:28:34 -070063 kViewGroup, "addView",
64 Prototype{TypeDescriptor::Void(),
65 kView,
66 TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})} {}
67
68void DexViewBuilder::BuildGetLayoutInflater(Value dest) {
69 // dest = LayoutInflater.from(context);
70 auto layout_inflater_from = method_->dex_file()->GetOrDeclareMethod(
71 kLayoutInflater, "from", Prototype{kLayoutInflater, kContext});
72 method_->AddInstruction(Instruction::InvokeStaticObject(layout_inflater_from.id, dest, context_));
73}
74
75void DexViewBuilder::BuildGetResources(Value dest) {
76 // dest = context.getResources();
77 auto get_resources =
78 method_->dex_file()->GetOrDeclareMethod(kContext, "getResources", Prototype{kResources});
79 method_->AddInstruction(Instruction::InvokeVirtualObject(get_resources.id, dest, context_));
80}
81
82void DexViewBuilder::BuildGetLayoutResource(Value dest, Value resources, Value resid) {
83 // dest = resources.getLayout(resid);
84 auto get_layout = method_->dex_file()->GetOrDeclareMethod(
85 kResources, "getLayout", Prototype{kXmlResourceParser, TypeDescriptor::Int()});
86 method_->AddInstruction(Instruction::InvokeVirtualObject(get_layout.id, dest, resources, resid));
87}
88
89void DexViewBuilder::BuildLayoutResourceToAttributeSet(dex::Value dest,
90 dex::Value layout_resource) {
91 // dest = Xml.asAttributeSet(layout_resource);
92 auto as_attribute_set = method_->dex_file()->GetOrDeclareMethod(
93 TypeDescriptor::FromClassname("android.util.Xml"),
94 "asAttributeSet",
95 Prototype{kAttributeSet, TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")});
96 method_->AddInstruction(
97 Instruction::InvokeStaticObject(as_attribute_set.id, dest, layout_resource));
98}
99
100void DexViewBuilder::BuildXmlNext() {
101 // xml_.next();
102 method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_));
103}
Eric Holk42734572018-12-17 15:46:18 -0800104
105void DexViewBuilder::Start() {
Eric Holk5c6a1a52019-09-17 13:28:34 -0700106 BuildGetLayoutInflater(/*dest=*/inflater_);
107 BuildGetResources(/*dest=*/xml_);
108 BuildGetLayoutResource(/*dest=*/xml_, /*resources=*/xml_, resid_);
109 BuildLayoutResourceToAttributeSet(/*dest=*/attrs_, /*layout_resource=*/xml_);
Eric Holk42734572018-12-17 15:46:18 -0800110
Eric Holk5c6a1a52019-09-17 13:28:34 -0700111 // Advance past start document tag
112 BuildXmlNext();
Eric Holk42734572018-12-17 15:46:18 -0800113}
114
115void DexViewBuilder::Finish() {}
116
117namespace {
118std::string ResolveName(const std::string& name) {
119 if (name == "View") return "android.view.View";
120 if (name == "ViewGroup") return "android.view.ViewGroup";
Chih-Hung Hsiehf2ef6572020-02-11 14:27:11 -0800121 if (name.find('.') == std::string::npos) {
Eric Holk42734572018-12-17 15:46:18 -0800122 return StringPrintf("android.widget.%s", name.c_str());
123 }
124 return name;
125}
126} // namespace
127
Eric Holk5c6a1a52019-09-17 13:28:34 -0700128void DexViewBuilder::BuildTryCreateView(Value dest, Value parent, Value classname) {
129 // dest = inflater_.tryCreateView(parent, classname, context_, attrs_);
130 method_->AddInstruction(Instruction::InvokeVirtualObject(
131 try_create_view_.id, dest, inflater_, parent, classname, context_, attrs_));
132}
133
Eric Holk42734572018-12-17 15:46:18 -0800134void DexViewBuilder::StartView(const std::string& name, bool is_viewgroup) {
135 bool const is_root_view = view_stack_.empty();
136
Eric Holk5c6a1a52019-09-17 13:28:34 -0700137 // Advance to start tag
138 BuildXmlNext();
Eric Holk42734572018-12-17 15:46:18 -0800139
Eric Holk5c6a1a52019-09-17 13:28:34 -0700140 LiveRegister view = AcquireRegister();
Eric Holk42734572018-12-17 15:46:18 -0800141 // try to create the view using the factories
142 method_->BuildConstString(classname_tmp_,
143 name); // TODO: the need to fully qualify the classname
144 if (is_root_view) {
Eric Holk5c6a1a52019-09-17 13:28:34 -0700145 LiveRegister null = AcquireRegister();
Eric Holk42734572018-12-17 15:46:18 -0800146 method_->BuildConst4(null, 0);
Eric Holk5c6a1a52019-09-17 13:28:34 -0700147 BuildTryCreateView(/*dest=*/view, /*parent=*/null, classname_tmp_);
Eric Holk42734572018-12-17 15:46:18 -0800148 } else {
Eric Holk5c6a1a52019-09-17 13:28:34 -0700149 BuildTryCreateView(/*dest=*/view, /*parent=*/GetCurrentView(), classname_tmp_);
Eric Holk42734572018-12-17 15:46:18 -0800150 }
151 auto label = method_->MakeLabel();
152 // branch if not null
153 method_->AddInstruction(
Eric Holk5c6a1a52019-09-17 13:28:34 -0700154 Instruction::OpWithArgs(Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label));
Eric Holk42734572018-12-17 15:46:18 -0800155
156 // If null, create the class directly.
157 method_->BuildNew(view,
Eric Holk5c6a1a52019-09-17 13:28:34 -0700158 TypeDescriptor::FromClassname(ResolveName(name)),
159 Prototype{TypeDescriptor::Void(), kContext, kAttributeSet},
Eric Holk42734572018-12-17 15:46:18 -0800160 context_,
161 attrs_);
162
Eric Holk5c6a1a52019-09-17 13:28:34 -0700163 method_->AddInstruction(Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, label));
Eric Holk42734572018-12-17 15:46:18 -0800164
165 if (is_viewgroup) {
166 // Cast to a ViewGroup so we can add children later.
Eric Holk5c6a1a52019-09-17 13:28:34 -0700167 const ir::Type* view_group_def = method_->dex_file()->GetOrAddType(kViewGroup.descriptor());
168 method_->AddInstruction(Instruction::Cast(view, Value::Type(view_group_def->orig_index)));
Eric Holk42734572018-12-17 15:46:18 -0800169 }
170
171 if (!is_root_view) {
172 // layout_params = parent.generateLayoutParams(attrs);
Eric Holk5c6a1a52019-09-17 13:28:34 -0700173 LiveRegister layout_params{AcquireRegister()};
174 method_->AddInstruction(Instruction::InvokeVirtualObject(
Eric Holk42734572018-12-17 15:46:18 -0800175 generate_layout_params_.id, layout_params, GetCurrentView(), attrs_));
Eric Holk5c6a1a52019-09-17 13:28:34 -0700176 view_stack_.push_back({std::move(view), std::move(layout_params)});
Eric Holk42734572018-12-17 15:46:18 -0800177 } else {
Eric Holk5c6a1a52019-09-17 13:28:34 -0700178 view_stack_.push_back({std::move(view), {}});
Eric Holk42734572018-12-17 15:46:18 -0800179 }
180}
181
182void DexViewBuilder::FinishView() {
183 if (view_stack_.size() == 1) {
184 method_->BuildReturn(GetCurrentView(), /*is_object=*/true);
185 } else {
186 // parent.add(view, layout_params)
Eric Holk5c6a1a52019-09-17 13:28:34 -0700187 method_->AddInstruction(Instruction::InvokeVirtual(
Eric Holk42734572018-12-17 15:46:18 -0800188 add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams()));
189 // xml.next(); // end tag
Eric Holk5c6a1a52019-09-17 13:28:34 -0700190 method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_));
Eric Holk42734572018-12-17 15:46:18 -0800191 }
192 PopViewStack();
193}
194
Eric Holk5c6a1a52019-09-17 13:28:34 -0700195LiveRegister DexViewBuilder::AcquireRegister() { return method_->AllocRegister(); }
Eric Holk42734572018-12-17 15:46:18 -0800196
Eric Holk5c6a1a52019-09-17 13:28:34 -0700197Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; }
198Value DexViewBuilder::GetCurrentLayoutParams() const {
Eric Holk42734572018-12-17 15:46:18 -0800199 return view_stack_.back().layout_params.value();
200}
Eric Holk5c6a1a52019-09-17 13:28:34 -0700201Value DexViewBuilder::GetParentView() const { return view_stack_[view_stack_.size() - 2].view; }
Eric Holk42734572018-12-17 15:46:18 -0800202
203void DexViewBuilder::PopViewStack() {
Eric Holk42734572018-12-17 15:46:18 -0800204 // Unconditionally release the view register.
Eric Holk42734572018-12-17 15:46:18 -0800205 view_stack_.pop_back();
206}
207
Chih-Hung Hsiehf2ef6572020-02-11 14:27:11 -0800208} // namespace startop