Bug: 30411878 Test: build with WITH_TIDY=1 Change-Id: I7fd815aa401fbcaff97b772f3ba5d6f1d2034011
209 lines
8.4 KiB
C++
209 lines
8.4 KiB
C++
/*
|
|
* 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_layout_compiler.h"
|
|
#include "layout_validation.h"
|
|
|
|
#include "android-base/stringprintf.h"
|
|
|
|
namespace startop {
|
|
|
|
using android::base::StringPrintf;
|
|
using dex::Instruction;
|
|
using dex::LiveRegister;
|
|
using dex::Prototype;
|
|
using dex::TypeDescriptor;
|
|
using dex::Value;
|
|
|
|
namespace {
|
|
// TODO: these are a bunch of static initializers, which we should avoid. See if
|
|
// we can make them constexpr.
|
|
const TypeDescriptor kAttributeSet = TypeDescriptor::FromClassname("android.util.AttributeSet");
|
|
const TypeDescriptor kContext = TypeDescriptor::FromClassname("android.content.Context");
|
|
const TypeDescriptor kLayoutInflater = TypeDescriptor::FromClassname("android.view.LayoutInflater");
|
|
const TypeDescriptor kResources = TypeDescriptor::FromClassname("android.content.res.Resources");
|
|
const TypeDescriptor kString = TypeDescriptor::FromClassname("java.lang.String");
|
|
const TypeDescriptor kView = TypeDescriptor::FromClassname("android.view.View");
|
|
const TypeDescriptor kViewGroup = TypeDescriptor::FromClassname("android.view.ViewGroup");
|
|
const TypeDescriptor kXmlResourceParser =
|
|
TypeDescriptor::FromClassname("android.content.res.XmlResourceParser");
|
|
} // namespace
|
|
|
|
DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method)
|
|
: method_{method},
|
|
context_{Value::Parameter(0)},
|
|
resid_{Value::Parameter(1)},
|
|
inflater_{method->AllocRegister()},
|
|
xml_{method->AllocRegister()},
|
|
attrs_{method->AllocRegister()},
|
|
classname_tmp_{method->AllocRegister()},
|
|
xml_next_{method->dex_file()->GetOrDeclareMethod(kXmlResourceParser, "next",
|
|
Prototype{TypeDescriptor::Int()})},
|
|
try_create_view_{method->dex_file()->GetOrDeclareMethod(
|
|
kLayoutInflater, "tryCreateView",
|
|
Prototype{kView, kView, kString, kContext, kAttributeSet})},
|
|
generate_layout_params_{method->dex_file()->GetOrDeclareMethod(
|
|
kViewGroup, "generateLayoutParams",
|
|
Prototype{TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"),
|
|
kAttributeSet})},
|
|
add_view_{method->dex_file()->GetOrDeclareMethod(
|
|
kViewGroup, "addView",
|
|
Prototype{TypeDescriptor::Void(),
|
|
kView,
|
|
TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})} {}
|
|
|
|
void DexViewBuilder::BuildGetLayoutInflater(Value dest) {
|
|
// dest = LayoutInflater.from(context);
|
|
auto layout_inflater_from = method_->dex_file()->GetOrDeclareMethod(
|
|
kLayoutInflater, "from", Prototype{kLayoutInflater, kContext});
|
|
method_->AddInstruction(Instruction::InvokeStaticObject(layout_inflater_from.id, dest, context_));
|
|
}
|
|
|
|
void DexViewBuilder::BuildGetResources(Value dest) {
|
|
// dest = context.getResources();
|
|
auto get_resources =
|
|
method_->dex_file()->GetOrDeclareMethod(kContext, "getResources", Prototype{kResources});
|
|
method_->AddInstruction(Instruction::InvokeVirtualObject(get_resources.id, dest, context_));
|
|
}
|
|
|
|
void DexViewBuilder::BuildGetLayoutResource(Value dest, Value resources, Value resid) {
|
|
// dest = resources.getLayout(resid);
|
|
auto get_layout = method_->dex_file()->GetOrDeclareMethod(
|
|
kResources, "getLayout", Prototype{kXmlResourceParser, TypeDescriptor::Int()});
|
|
method_->AddInstruction(Instruction::InvokeVirtualObject(get_layout.id, dest, resources, resid));
|
|
}
|
|
|
|
void DexViewBuilder::BuildLayoutResourceToAttributeSet(dex::Value dest,
|
|
dex::Value layout_resource) {
|
|
// dest = Xml.asAttributeSet(layout_resource);
|
|
auto as_attribute_set = method_->dex_file()->GetOrDeclareMethod(
|
|
TypeDescriptor::FromClassname("android.util.Xml"),
|
|
"asAttributeSet",
|
|
Prototype{kAttributeSet, TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")});
|
|
method_->AddInstruction(
|
|
Instruction::InvokeStaticObject(as_attribute_set.id, dest, layout_resource));
|
|
}
|
|
|
|
void DexViewBuilder::BuildXmlNext() {
|
|
// xml_.next();
|
|
method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_));
|
|
}
|
|
|
|
void DexViewBuilder::Start() {
|
|
BuildGetLayoutInflater(/*dest=*/inflater_);
|
|
BuildGetResources(/*dest=*/xml_);
|
|
BuildGetLayoutResource(/*dest=*/xml_, /*resources=*/xml_, resid_);
|
|
BuildLayoutResourceToAttributeSet(/*dest=*/attrs_, /*layout_resource=*/xml_);
|
|
|
|
// Advance past start document tag
|
|
BuildXmlNext();
|
|
}
|
|
|
|
void DexViewBuilder::Finish() {}
|
|
|
|
namespace {
|
|
std::string ResolveName(const std::string& name) {
|
|
if (name == "View") return "android.view.View";
|
|
if (name == "ViewGroup") return "android.view.ViewGroup";
|
|
if (name.find('.') == std::string::npos) {
|
|
return StringPrintf("android.widget.%s", name.c_str());
|
|
}
|
|
return name;
|
|
}
|
|
} // namespace
|
|
|
|
void DexViewBuilder::BuildTryCreateView(Value dest, Value parent, Value classname) {
|
|
// dest = inflater_.tryCreateView(parent, classname, context_, attrs_);
|
|
method_->AddInstruction(Instruction::InvokeVirtualObject(
|
|
try_create_view_.id, dest, inflater_, parent, classname, context_, attrs_));
|
|
}
|
|
|
|
void DexViewBuilder::StartView(const std::string& name, bool is_viewgroup) {
|
|
bool const is_root_view = view_stack_.empty();
|
|
|
|
// Advance to start tag
|
|
BuildXmlNext();
|
|
|
|
LiveRegister view = AcquireRegister();
|
|
// try to create the view using the factories
|
|
method_->BuildConstString(classname_tmp_,
|
|
name); // TODO: the need to fully qualify the classname
|
|
if (is_root_view) {
|
|
LiveRegister null = AcquireRegister();
|
|
method_->BuildConst4(null, 0);
|
|
BuildTryCreateView(/*dest=*/view, /*parent=*/null, classname_tmp_);
|
|
} else {
|
|
BuildTryCreateView(/*dest=*/view, /*parent=*/GetCurrentView(), classname_tmp_);
|
|
}
|
|
auto label = method_->MakeLabel();
|
|
// branch if not null
|
|
method_->AddInstruction(
|
|
Instruction::OpWithArgs(Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label));
|
|
|
|
// If null, create the class directly.
|
|
method_->BuildNew(view,
|
|
TypeDescriptor::FromClassname(ResolveName(name)),
|
|
Prototype{TypeDescriptor::Void(), kContext, kAttributeSet},
|
|
context_,
|
|
attrs_);
|
|
|
|
method_->AddInstruction(Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, label));
|
|
|
|
if (is_viewgroup) {
|
|
// Cast to a ViewGroup so we can add children later.
|
|
const ir::Type* view_group_def = method_->dex_file()->GetOrAddType(kViewGroup.descriptor());
|
|
method_->AddInstruction(Instruction::Cast(view, Value::Type(view_group_def->orig_index)));
|
|
}
|
|
|
|
if (!is_root_view) {
|
|
// layout_params = parent.generateLayoutParams(attrs);
|
|
LiveRegister layout_params{AcquireRegister()};
|
|
method_->AddInstruction(Instruction::InvokeVirtualObject(
|
|
generate_layout_params_.id, layout_params, GetCurrentView(), attrs_));
|
|
view_stack_.push_back({std::move(view), std::move(layout_params)});
|
|
} else {
|
|
view_stack_.push_back({std::move(view), {}});
|
|
}
|
|
}
|
|
|
|
void DexViewBuilder::FinishView() {
|
|
if (view_stack_.size() == 1) {
|
|
method_->BuildReturn(GetCurrentView(), /*is_object=*/true);
|
|
} else {
|
|
// parent.add(view, layout_params)
|
|
method_->AddInstruction(Instruction::InvokeVirtual(
|
|
add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams()));
|
|
// xml.next(); // end tag
|
|
method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_));
|
|
}
|
|
PopViewStack();
|
|
}
|
|
|
|
LiveRegister DexViewBuilder::AcquireRegister() { return method_->AllocRegister(); }
|
|
|
|
Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; }
|
|
Value DexViewBuilder::GetCurrentLayoutParams() const {
|
|
return view_stack_.back().layout_params.value();
|
|
}
|
|
Value DexViewBuilder::GetParentView() const { return view_stack_[view_stack_.size() - 2].view; }
|
|
|
|
void DexViewBuilder::PopViewStack() {
|
|
// Unconditionally release the view register.
|
|
view_stack_.pop_back();
|
|
}
|
|
|
|
} // namespace startop
|