• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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