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 26 void LayoutValidationVisitor::VisitStartTag(const std::u16string& name) { 27 if (0 == name.compare(u"merge")) { 28 message_ = "Merge tags are not supported"; 29 can_compile_ = false; 30 } 31 if (0 == name.compare(u"include")) { 32 message_ = "Include tags are not supported"; 33 can_compile_ = false; 34 } 35 if (0 == name.compare(u"view")) { 36 message_ = "View tags are not supported"; 37 can_compile_ = false; 38 } 39 if (0 == name.compare(u"fragment")) { 40 message_ = "Fragment tags are not supported"; 41 can_compile_ = false; 42 } 43 } 44 45 DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method) 46 : method_{method}, 47 context_{dex::Value::Parameter(0)}, 48 resid_{dex::Value::Parameter(1)}, 49 inflater_{method->MakeRegister()}, 50 xml_{method->MakeRegister()}, 51 attrs_{method->MakeRegister()}, 52 classname_tmp_{method->MakeRegister()}, 53 xml_next_{method->dex_file()->GetOrDeclareMethod( 54 dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"), "next", 55 dex::Prototype{dex::TypeDescriptor::Int()})}, 56 try_create_view_{method->dex_file()->GetOrDeclareMethod( 57 dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), "tryCreateView", 58 dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.View"), 59 dex::TypeDescriptor::FromClassname("android.view.View"), 60 dex::TypeDescriptor::FromClassname("java.lang.String"), 61 dex::TypeDescriptor::FromClassname("android.content.Context"), 62 dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})}, 63 generate_layout_params_{method->dex_file()->GetOrDeclareMethod( 64 dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "generateLayoutParams", 65 dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"), 66 dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})}, 67 add_view_{method->dex_file()->GetOrDeclareMethod( 68 dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "addView", 69 dex::Prototype{ 70 dex::TypeDescriptor::Void(), 71 dex::TypeDescriptor::FromClassname("android.view.View"), 72 dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})}, 73 // The register stack starts with one register, which will be null for the root view. 74 register_stack_{{method->MakeRegister()}} {} 75 76 void DexViewBuilder::Start() { 77 dex::DexBuilder* const dex = method_->dex_file(); 78 79 // LayoutInflater inflater = LayoutInflater.from(context); 80 auto layout_inflater_from = dex->GetOrDeclareMethod( 81 dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), 82 "from", 83 dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), 84 dex::TypeDescriptor::FromClassname("android.content.Context")}); 85 method_->AddInstruction( 86 dex::Instruction::InvokeStaticObject(layout_inflater_from.id, /*dest=*/inflater_, context_)); 87 88 // Resources res = context.getResources(); 89 auto context_type = dex::TypeDescriptor::FromClassname("android.content.Context"); 90 auto resources_type = dex::TypeDescriptor::FromClassname("android.content.res.Resources"); 91 auto get_resources = 92 dex->GetOrDeclareMethod(context_type, "getResources", dex::Prototype{resources_type}); 93 method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_resources.id, xml_, context_)); 94 95 // XmlResourceParser xml = res.getLayout(resid); 96 auto xml_resource_parser_type = 97 dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"); 98 auto get_layout = 99 dex->GetOrDeclareMethod(resources_type, 100 "getLayout", 101 dex::Prototype{xml_resource_parser_type, dex::TypeDescriptor::Int()}); 102 method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_layout.id, xml_, xml_, resid_)); 103 104 // AttributeSet attrs = Xml.asAttributeSet(xml); 105 auto as_attribute_set = dex->GetOrDeclareMethod( 106 dex::TypeDescriptor::FromClassname("android.util.Xml"), 107 "asAttributeSet", 108 dex::Prototype{dex::TypeDescriptor::FromClassname("android.util.AttributeSet"), 109 dex::TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")}); 110 method_->AddInstruction(dex::Instruction::InvokeStaticObject(as_attribute_set.id, attrs_, xml_)); 111 112 // xml.next(); // start document 113 method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_)); 114 } 115 116 void DexViewBuilder::Finish() {} 117 118 namespace { 119 std::string ResolveName(const std::string& name) { 120 if (name == "View") return "android.view.View"; 121 if (name == "ViewGroup") return "android.view.ViewGroup"; 122 if (name.find(".") == std::string::npos) { 123 return StringPrintf("android.widget.%s", name.c_str()); 124 } 125 return name; 126 } 127 } // namespace 128 129 void DexViewBuilder::StartView(const std::string& name, bool is_viewgroup) { 130 bool const is_root_view = view_stack_.empty(); 131 132 // xml.next(); // start tag 133 method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_)); 134 135 dex::Value view = AcquireRegister(); 136 // try to create the view using the factories 137 method_->BuildConstString(classname_tmp_, 138 name); // TODO: the need to fully qualify the classname 139 if (is_root_view) { 140 dex::Value null = AcquireRegister(); 141 method_->BuildConst4(null, 0); 142 method_->AddInstruction(dex::Instruction::InvokeVirtualObject( 143 try_create_view_.id, view, inflater_, null, classname_tmp_, context_, attrs_)); 144 ReleaseRegister(); 145 } else { 146 method_->AddInstruction(dex::Instruction::InvokeVirtualObject( 147 try_create_view_.id, view, inflater_, GetCurrentView(), classname_tmp_, context_, attrs_)); 148 } 149 auto label = method_->MakeLabel(); 150 // branch if not null 151 method_->AddInstruction( 152 dex::Instruction::OpWithArgs(dex::Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label)); 153 154 // If null, create the class directly. 155 method_->BuildNew(view, 156 dex::TypeDescriptor::FromClassname(ResolveName(name)), 157 dex::Prototype{dex::TypeDescriptor::Void(), 158 dex::TypeDescriptor::FromClassname("android.content.Context"), 159 dex::TypeDescriptor::FromClassname("android.util.AttributeSet")}, 160 context_, 161 attrs_); 162 163 method_->AddInstruction( 164 dex::Instruction::OpWithArgs(dex::Instruction::Op::kBindLabel, /*dest=*/{}, label)); 165 166 if (is_viewgroup) { 167 // Cast to a ViewGroup so we can add children later. 168 const ir::Type* view_group_def = method_->dex_file()->GetOrAddType( 169 dex::TypeDescriptor::FromClassname("android.view.ViewGroup").descriptor()); 170 method_->AddInstruction(dex::Instruction::Cast(view, dex::Value::Type(view_group_def->orig_index))); 171 } 172 173 if (!is_root_view) { 174 // layout_params = parent.generateLayoutParams(attrs); 175 dex::Value layout_params{AcquireRegister()}; 176 method_->AddInstruction(dex::Instruction::InvokeVirtualObject( 177 generate_layout_params_.id, layout_params, GetCurrentView(), attrs_)); 178 view_stack_.push_back({view, layout_params}); 179 } else { 180 view_stack_.push_back({view, {}}); 181 } 182 } 183 184 void DexViewBuilder::FinishView() { 185 if (view_stack_.size() == 1) { 186 method_->BuildReturn(GetCurrentView(), /*is_object=*/true); 187 } else { 188 // parent.add(view, layout_params) 189 method_->AddInstruction(dex::Instruction::InvokeVirtual( 190 add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams())); 191 // xml.next(); // end tag 192 method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_)); 193 } 194 PopViewStack(); 195 } 196 197 dex::Value DexViewBuilder::AcquireRegister() { 198 top_register_++; 199 if (register_stack_.size() == top_register_) { 200 register_stack_.push_back(method_->MakeRegister()); 201 } 202 return register_stack_[top_register_]; 203 } 204 205 void DexViewBuilder::ReleaseRegister() { top_register_--; } 206 207 dex::Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; } 208 dex::Value DexViewBuilder::GetCurrentLayoutParams() const { 209 return view_stack_.back().layout_params.value(); 210 } 211 dex::Value DexViewBuilder::GetParentView() const { 212 return view_stack_[view_stack_.size() - 2].view; 213 } 214 215 void DexViewBuilder::PopViewStack() { 216 const auto& top = view_stack_.back(); 217 // release the layout params if we have them 218 if (top.layout_params.has_value()) { 219 ReleaseRegister(); 220 } 221 // Unconditionally release the view register. 222 ReleaseRegister(); 223 view_stack_.pop_back(); 224 } 225 226 } // namespace startop