1 /*
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
22 namespace startop {
23
24 using android::base::StringPrintf;
25 using dex::Instruction;
26 using dex::LiveRegister;
27 using dex::Prototype;
28 using dex::TypeDescriptor;
29 using dex::Value;
30
31 namespace {
32 // TODO: these are a bunch of static initializers, which we should avoid. See if
33 // we can make them constexpr.
34 const TypeDescriptor kAttributeSet = TypeDescriptor::FromClassname("android.util.AttributeSet");
35 const TypeDescriptor kContext = TypeDescriptor::FromClassname("android.content.Context");
36 const TypeDescriptor kLayoutInflater = TypeDescriptor::FromClassname("android.view.LayoutInflater");
37 const TypeDescriptor kResources = TypeDescriptor::FromClassname("android.content.res.Resources");
38 const TypeDescriptor kString = TypeDescriptor::FromClassname("java.lang.String");
39 const TypeDescriptor kView = TypeDescriptor::FromClassname("android.view.View");
40 const TypeDescriptor kViewGroup = TypeDescriptor::FromClassname("android.view.ViewGroup");
41 const TypeDescriptor kXmlResourceParser =
42 TypeDescriptor::FromClassname("android.content.res.XmlResourceParser");
43 } // namespace
44
DexViewBuilder(dex::MethodBuilder * method)45 DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method)
46 : method_{method},
47 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()})},
55 try_create_view_{method->dex_file()->GetOrDeclareMethod(
56 kLayoutInflater, "tryCreateView",
57 Prototype{kView, kView, kString, kContext, kAttributeSet})},
58 generate_layout_params_{method->dex_file()->GetOrDeclareMethod(
59 kViewGroup, "generateLayoutParams",
60 Prototype{TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"),
61 kAttributeSet})},
62 add_view_{method->dex_file()->GetOrDeclareMethod(
63 kViewGroup, "addView",
64 Prototype{TypeDescriptor::Void(),
65 kView,
66 TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})} {}
67
BuildGetLayoutInflater(Value dest)68 void 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
BuildGetResources(Value dest)75 void 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
BuildGetLayoutResource(Value dest,Value resources,Value resid)82 void 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
BuildLayoutResourceToAttributeSet(dex::Value dest,dex::Value layout_resource)89 void 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
BuildXmlNext()100 void DexViewBuilder::BuildXmlNext() {
101 // xml_.next();
102 method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_));
103 }
104
Start()105 void DexViewBuilder::Start() {
106 BuildGetLayoutInflater(/*dest=*/inflater_);
107 BuildGetResources(/*dest=*/xml_);
108 BuildGetLayoutResource(/*dest=*/xml_, /*resources=*/xml_, resid_);
109 BuildLayoutResourceToAttributeSet(/*dest=*/attrs_, /*layout_resource=*/xml_);
110
111 // Advance past start document tag
112 BuildXmlNext();
113 }
114
Finish()115 void DexViewBuilder::Finish() {}
116
117 namespace {
ResolveName(const std::string & name)118 std::string ResolveName(const std::string& name) {
119 if (name == "View") return "android.view.View";
120 if (name == "ViewGroup") return "android.view.ViewGroup";
121 if (name.find('.') == std::string::npos) {
122 return StringPrintf("android.widget.%s", name.c_str());
123 }
124 return name;
125 }
126 } // namespace
127
BuildTryCreateView(Value dest,Value parent,Value classname)128 void 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
StartView(const std::string & name,bool is_viewgroup)134 void DexViewBuilder::StartView(const std::string& name, bool is_viewgroup) {
135 bool const is_root_view = view_stack_.empty();
136
137 // Advance to start tag
138 BuildXmlNext();
139
140 LiveRegister view = AcquireRegister();
141 // 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) {
145 LiveRegister null = AcquireRegister();
146 method_->BuildConst4(null, 0);
147 BuildTryCreateView(/*dest=*/view, /*parent=*/null, classname_tmp_);
148 } else {
149 BuildTryCreateView(/*dest=*/view, /*parent=*/GetCurrentView(), classname_tmp_);
150 }
151 auto label = method_->MakeLabel();
152 // branch if not null
153 method_->AddInstruction(
154 Instruction::OpWithArgs(Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label));
155
156 // If null, create the class directly.
157 method_->BuildNew(view,
158 TypeDescriptor::FromClassname(ResolveName(name)),
159 Prototype{TypeDescriptor::Void(), kContext, kAttributeSet},
160 context_,
161 attrs_);
162
163 method_->AddInstruction(Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, label));
164
165 if (is_viewgroup) {
166 // Cast to a ViewGroup so we can add children later.
167 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)));
169 }
170
171 if (!is_root_view) {
172 // layout_params = parent.generateLayoutParams(attrs);
173 LiveRegister layout_params{AcquireRegister()};
174 method_->AddInstruction(Instruction::InvokeVirtualObject(
175 generate_layout_params_.id, layout_params, GetCurrentView(), attrs_));
176 view_stack_.push_back({std::move(view), std::move(layout_params)});
177 } else {
178 view_stack_.push_back({std::move(view), {}});
179 }
180 }
181
FinishView()182 void DexViewBuilder::FinishView() {
183 if (view_stack_.size() == 1) {
184 method_->BuildReturn(GetCurrentView(), /*is_object=*/true);
185 } else {
186 // parent.add(view, layout_params)
187 method_->AddInstruction(Instruction::InvokeVirtual(
188 add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams()));
189 // xml.next(); // end tag
190 method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_));
191 }
192 PopViewStack();
193 }
194
AcquireRegister()195 LiveRegister DexViewBuilder::AcquireRegister() { return method_->AllocRegister(); }
196
GetCurrentView() const197 Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; }
GetCurrentLayoutParams() const198 Value DexViewBuilder::GetCurrentLayoutParams() const {
199 return view_stack_.back().layout_params.value();
200 }
GetParentView() const201 Value DexViewBuilder::GetParentView() const { return view_stack_[view_stack_.size() - 2].view; }
202
PopViewStack()203 void DexViewBuilder::PopViewStack() {
204 // Unconditionally release the view register.
205 view_stack_.pop_back();
206 }
207
208 } // namespace startop
209