diff options
author | 2019-01-07 10:20:09 -0800 | |
---|---|---|
committer | 2019-01-07 10:20:09 -0800 | |
commit | ffdf092a77c6f6c8998394405d43f22ae54dfede (patch) | |
tree | dc6b52c396bf3e9cc617a6af852d17283a82f444 | |
parent | 79e14fd7eaf2afd8b0443ab52e199be7dd74e35f (diff) | |
parent | c31debe0376ff5e8f5d05345c85404445f358c86 (diff) |
Merge "[view compiler] Add XML to DEX compilation" am: d426ee84a0 am: 755467bb4f
am: c31debe037
Change-Id: Ib457db49ebdade8a9b9e8d52ceeb64d5aee4bbd6
-rw-r--r-- | startop/view_compiler/Android.bp | 1 | ||||
-rw-r--r-- | startop/view_compiler/dex_builder_test/Android.bp | 18 | ||||
-rw-r--r-- | startop/view_compiler/dex_builder_test/AndroidTest.xml | 1 | ||||
-rw-r--r-- | startop/view_compiler/dex_builder_test/res/layout/layout1.xml | 17 | ||||
-rw-r--r-- | startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java | 46 | ||||
-rw-r--r-- | startop/view_compiler/dex_layout_compiler.cc | 226 | ||||
-rw-r--r-- | startop/view_compiler/dex_layout_compiler.h | 118 | ||||
-rw-r--r-- | startop/view_compiler/java_lang_builder.cc | 2 | ||||
-rw-r--r-- | startop/view_compiler/java_lang_builder.h | 2 | ||||
-rw-r--r-- | startop/view_compiler/main.cc | 74 |
10 files changed, 479 insertions, 26 deletions
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp index 91cec554d7cd..82056e9a33fe 100644 --- a/startop/view_compiler/Android.bp +++ b/startop/view_compiler/Android.bp @@ -34,6 +34,7 @@ cc_library_host_static { defaults: ["viewcompiler_defaults"], srcs: [ "dex_builder.cc", + "dex_layout_compiler.cc", "java_lang_builder.cc", "tinyxml_layout_parser.cc", "util.cc", diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp index 4449ea0f707e..d4f38ed148c9 100644 --- a/startop/view_compiler/dex_builder_test/Android.bp +++ b/startop/view_compiler/dex_builder_test/Android.bp @@ -14,16 +14,30 @@ // limitations under the License. // +genrule { + name: "generate_compiled_layout", + tools: [":viewcompiler"], + cmd: "$(location :viewcompiler) $(in) --dex --out $(out) --package android.startop.test", + srcs: ["res/layout/layout1.xml"], + out: [ + "layout1.dex", + ], +} + android_test { name: "dex-builder-test", - srcs: ["src/android/startop/test/DexBuilderTest.java"], + srcs: [ + "src/android/startop/test/DexBuilderTest.java", + "src/android/startop/test/LayoutCompilerTest.java", + ], sdk_version: "current", - data: [":generate_dex_testcases"], + data: [":generate_dex_testcases", ":generate_compiled_layout"], static_libs: [ "android-support-test", "guava", ], manifest: "AndroidManifest.xml", + resource_dirs: ["res"], test_config: "AndroidTest.xml", test_suites: ["general-tests"], } diff --git a/startop/view_compiler/dex_builder_test/AndroidTest.xml b/startop/view_compiler/dex_builder_test/AndroidTest.xml index 6f90cf3b81a7..68d8fdc444d8 100644 --- a/startop/view_compiler/dex_builder_test/AndroidTest.xml +++ b/startop/view_compiler/dex_builder_test/AndroidTest.xml @@ -25,6 +25,7 @@ <option name="cleanup" value="true" /> <option name="push" value="trivial.dex->/data/local/tmp/dex-builder-test/trivial.dex" /> <option name="push" value="simple.dex->/data/local/tmp/dex-builder-test/simple.dex" /> + <option name="push" value="layout1.dex->/data/local/tmp/dex-builder-test/layout1.dex" /> </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > diff --git a/startop/view_compiler/dex_builder_test/res/layout/layout1.xml b/startop/view_compiler/dex_builder_test/res/layout/layout1.xml new file mode 100644 index 000000000000..0f9375c6ebce --- /dev/null +++ b/startop/view_compiler/dex_builder_test/res/layout/layout1.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:orientation="vertical" + android:gravity="center"> + + <Button + android:layout_width="match_parent" + android:layout_height="match_parent"/> + <Button + android:layout_width="match_parent" + android:layout_height="match_parent"/> + + </LinearLayout> diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java new file mode 100644 index 000000000000..ce3ce8328559 --- /dev/null +++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java @@ -0,0 +1,46 @@ +/* + * 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. + */ + +package android.startop.test; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.view.View; +import com.google.common.io.ByteStreams; +import dalvik.system.InMemoryDexClassLoader; +import dalvik.system.PathClassLoader; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import org.junit.Assert; +import org.junit.Test; + +// Adding tests here requires changes in several other places. See README.md in +// the view_compiler directory for more information. +public class LayoutCompilerTest { + static ClassLoader loadDexFile(String filename) throws Exception { + return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename, + ClassLoader.getSystemClassLoader()); + } + + @Test + public void loadAndInflaterLayout1() throws Exception { + ClassLoader dex_file = loadDexFile("layout1.dex"); + Class compiled_view = dex_file.loadClass("android.startop.test.CompiledView"); + Method layout1 = compiled_view.getMethod("layout1", Context.class, int.class); + Context context = InstrumentationRegistry.getTargetContext(); + layout1.invoke(null, context, R.layout.layout1); + } +} diff --git a/startop/view_compiler/dex_layout_compiler.cc b/startop/view_compiler/dex_layout_compiler.cc new file mode 100644 index 000000000000..c68793d10399 --- /dev/null +++ b/startop/view_compiler/dex_layout_compiler.cc @@ -0,0 +1,226 @@ +/* + * 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; + +void LayoutValidationVisitor::VisitStartTag(const std::u16string& name) { + if (0 == name.compare(u"merge")) { + message_ = "Merge tags are not supported"; + can_compile_ = false; + } + if (0 == name.compare(u"include")) { + message_ = "Include tags are not supported"; + can_compile_ = false; + } + if (0 == name.compare(u"view")) { + message_ = "View tags are not supported"; + can_compile_ = false; + } + if (0 == name.compare(u"fragment")) { + message_ = "Fragment tags are not supported"; + can_compile_ = false; + } +} + +DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method) + : method_{method}, + context_{dex::Value::Parameter(0)}, + resid_{dex::Value::Parameter(1)}, + inflater_{method->MakeRegister()}, + xml_{method->MakeRegister()}, + attrs_{method->MakeRegister()}, + classname_tmp_{method->MakeRegister()}, + xml_next_{method->dex_file()->GetOrDeclareMethod( + dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"), "next", + dex::Prototype{dex::TypeDescriptor::Int()})}, + try_create_view_{method->dex_file()->GetOrDeclareMethod( + dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), "tryCreateView", + dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.View"), + dex::TypeDescriptor::FromClassname("android.view.View"), + dex::TypeDescriptor::FromClassname("java.lang.String"), + dex::TypeDescriptor::FromClassname("android.content.Context"), + dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})}, + generate_layout_params_{method->dex_file()->GetOrDeclareMethod( + dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "generateLayoutParams", + dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"), + dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})}, + add_view_{method->dex_file()->GetOrDeclareMethod( + dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "addView", + dex::Prototype{ + dex::TypeDescriptor::Void(), + dex::TypeDescriptor::FromClassname("android.view.View"), + dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})}, + // The register stack starts with one register, which will be null for the root view. + register_stack_{{method->MakeRegister()}} {} + +void DexViewBuilder::Start() { + dex::DexBuilder* const dex = method_->dex_file(); + + // LayoutInflater inflater = LayoutInflater.from(context); + auto layout_inflater_from = dex->GetOrDeclareMethod( + dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), + "from", + dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), + dex::TypeDescriptor::FromClassname("android.content.Context")}); + method_->AddInstruction( + dex::Instruction::InvokeStaticObject(layout_inflater_from.id, /*dest=*/inflater_, context_)); + + // Resources res = context.getResources(); + auto context_type = dex::TypeDescriptor::FromClassname("android.content.Context"); + auto resources_type = dex::TypeDescriptor::FromClassname("android.content.res.Resources"); + auto get_resources = + dex->GetOrDeclareMethod(context_type, "getResources", dex::Prototype{resources_type}); + method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_resources.id, xml_, context_)); + + // XmlResourceParser xml = res.getLayout(resid); + auto xml_resource_parser_type = + dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"); + auto get_layout = + dex->GetOrDeclareMethod(resources_type, + "getLayout", + dex::Prototype{xml_resource_parser_type, dex::TypeDescriptor::Int()}); + method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_layout.id, xml_, xml_, resid_)); + + // AttributeSet attrs = Xml.asAttributeSet(xml); + auto as_attribute_set = dex->GetOrDeclareMethod( + dex::TypeDescriptor::FromClassname("android.util.Xml"), + "asAttributeSet", + dex::Prototype{dex::TypeDescriptor::FromClassname("android.util.AttributeSet"), + dex::TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")}); + method_->AddInstruction(dex::Instruction::InvokeStaticObject(as_attribute_set.id, attrs_, xml_)); + + // xml.next(); // start document + method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_)); +} + +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::StartView(const std::string& name, bool is_viewgroup) { + bool const is_root_view = view_stack_.empty(); + + // xml.next(); // start tag + method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_)); + + dex::Value 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) { + dex::Value null = AcquireRegister(); + method_->BuildConst4(null, 0); + method_->AddInstruction(dex::Instruction::InvokeVirtualObject( + try_create_view_.id, view, inflater_, null, classname_tmp_, context_, attrs_)); + ReleaseRegister(); + } else { + method_->AddInstruction(dex::Instruction::InvokeVirtualObject( + try_create_view_.id, view, inflater_, GetCurrentView(), classname_tmp_, context_, attrs_)); + } + auto label = method_->MakeLabel(); + // branch if not null + method_->AddInstruction( + dex::Instruction::OpWithArgs(dex::Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label)); + + // If null, create the class directly. + method_->BuildNew(view, + dex::TypeDescriptor::FromClassname(ResolveName(name)), + dex::Prototype{dex::TypeDescriptor::Void(), + dex::TypeDescriptor::FromClassname("android.content.Context"), + dex::TypeDescriptor::FromClassname("android.util.AttributeSet")}, + context_, + attrs_); + + method_->AddInstruction( + dex::Instruction::OpWithArgs(dex::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( + dex::TypeDescriptor::FromClassname("android.view.ViewGroup").descriptor()); + method_->AddInstruction(dex::Instruction::Cast(view, dex::Value::Type(view_group_def->orig_index))); + } + + if (!is_root_view) { + // layout_params = parent.generateLayoutParams(attrs); + dex::Value layout_params{AcquireRegister()}; + method_->AddInstruction(dex::Instruction::InvokeVirtualObject( + generate_layout_params_.id, layout_params, GetCurrentView(), attrs_)); + view_stack_.push_back({view, layout_params}); + } else { + view_stack_.push_back({view, {}}); + } +} + +void DexViewBuilder::FinishView() { + if (view_stack_.size() == 1) { + method_->BuildReturn(GetCurrentView(), /*is_object=*/true); + } else { + // parent.add(view, layout_params) + method_->AddInstruction(dex::Instruction::InvokeVirtual( + add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams())); + // xml.next(); // end tag + method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_)); + } + PopViewStack(); +} + +dex::Value DexViewBuilder::AcquireRegister() { + top_register_++; + if (register_stack_.size() == top_register_) { + register_stack_.push_back(method_->MakeRegister()); + } + return register_stack_[top_register_]; +} + +void DexViewBuilder::ReleaseRegister() { top_register_--; } + +dex::Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; } +dex::Value DexViewBuilder::GetCurrentLayoutParams() const { + return view_stack_.back().layout_params.value(); +} +dex::Value DexViewBuilder::GetParentView() const { + return view_stack_[view_stack_.size() - 2].view; +} + +void DexViewBuilder::PopViewStack() { + const auto& top = view_stack_.back(); + // release the layout params if we have them + if (top.layout_params.has_value()) { + ReleaseRegister(); + } + // Unconditionally release the view register. + ReleaseRegister(); + view_stack_.pop_back(); +} + +} // namespace startop
\ No newline at end of file diff --git a/startop/view_compiler/dex_layout_compiler.h b/startop/view_compiler/dex_layout_compiler.h new file mode 100644 index 000000000000..170a1a610297 --- /dev/null +++ b/startop/view_compiler/dex_layout_compiler.h @@ -0,0 +1,118 @@ +/* + * 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. + */ + +#ifndef DEX_LAYOUT_COMPILER_H_ +#define DEX_LAYOUT_COMPILER_H_ + +#include "dex_builder.h" + +#include <codecvt> +#include <locale> +#include <string> +#include <vector> + +namespace startop { + +// This visitor does the actual view compilation, using a supplied builder. +template <typename Builder> +class LayoutCompilerVisitor { + public: + explicit LayoutCompilerVisitor(Builder* builder) : builder_{builder} {} + + void VisitStartDocument() { builder_->Start(); } + void VisitEndDocument() { builder_->Finish(); } + void VisitStartTag(const std::u16string& name) { + parent_stack_.push_back(ViewEntry{ + std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(name), {}}); + } + void VisitEndTag() { + auto entry = parent_stack_.back(); + parent_stack_.pop_back(); + + if (parent_stack_.empty()) { + GenerateCode(entry); + } else { + parent_stack_.back().children.push_back(entry); + } + } + + private: + struct ViewEntry { + std::string name; + std::vector<ViewEntry> children; + }; + + void GenerateCode(const ViewEntry& view) { + builder_->StartView(view.name, !view.children.empty()); + for (const auto& child : view.children) { + GenerateCode(child); + } + builder_->FinishView(); + } + + Builder* builder_; + + std::vector<ViewEntry> parent_stack_; +}; + +class DexViewBuilder { + public: + DexViewBuilder(dex::MethodBuilder* method); + + void Start(); + void Finish(); + void StartView(const std::string& name, bool is_viewgroup); + void FinishView(); + + private: + // Accessors for the stack of views that are under construction. + dex::Value AcquireRegister(); + void ReleaseRegister(); + dex::Value GetCurrentView() const; + dex::Value GetCurrentLayoutParams() const; + dex::Value GetParentView() const; + void PopViewStack(); + + dex::MethodBuilder* method_; + + // Registers used for code generation + dex::Value const context_; + dex::Value const resid_; + const dex::Value inflater_; + const dex::Value xml_; + const dex::Value attrs_; + const dex::Value classname_tmp_; + + const dex::MethodDeclData xml_next_; + const dex::MethodDeclData try_create_view_; + const dex::MethodDeclData generate_layout_params_; + const dex::MethodDeclData add_view_; + + // used for keeping track of which registers are in use + size_t top_register_{0}; + std::vector<dex::Value> register_stack_; + + // Keep track of the views currently in progress. + struct ViewEntry { + dex::Value view; + std::optional<dex::Value> layout_params; + }; + std::vector<ViewEntry> view_stack_; +}; + +} // namespace startop + +#endif // DEX_LAYOUT_COMPILER_H_ diff --git a/startop/view_compiler/java_lang_builder.cc b/startop/view_compiler/java_lang_builder.cc index 0b8754fc7096..920caeecf58e 100644 --- a/startop/view_compiler/java_lang_builder.cc +++ b/startop/view_compiler/java_lang_builder.cc @@ -67,7 +67,7 @@ void JavaLangViewBuilder::Finish() const { "}\n"; // end CompiledView } -void JavaLangViewBuilder::StartView(const string& class_name) { +void JavaLangViewBuilder::StartView(const string& class_name, bool /*is_viewgroup*/) { const string view_var = MakeVar("view"); const string layout_var = MakeVar("layout"); std::string parent = "null"; diff --git a/startop/view_compiler/java_lang_builder.h b/startop/view_compiler/java_lang_builder.h index c8d20b23cd13..69356d3a6594 100644 --- a/startop/view_compiler/java_lang_builder.h +++ b/startop/view_compiler/java_lang_builder.h @@ -35,7 +35,7 @@ class JavaLangViewBuilder { void Finish() const; // Begin creating a view (i.e. process the opening tag) - void StartView(const std::string& class_name); + void StartView(const std::string& class_name, bool is_viewgroup); // Finish a view, after all of its child nodes have been processed. void FinishView(); diff --git a/startop/view_compiler/main.cc b/startop/view_compiler/main.cc index 609bcf377b46..ae00187e1908 100644 --- a/startop/view_compiler/main.cc +++ b/startop/view_compiler/main.cc @@ -16,8 +16,11 @@ #include "gflags/gflags.h" +#include "android-base/stringprintf.h" #include "dex_builder.h" +#include "dex_layout_compiler.h" #include "java_lang_builder.h" +#include "layout_validation.h" #include "tinyxml_layout_parser.h" #include "util.h" @@ -32,6 +35,12 @@ namespace { using namespace tinyxml2; +using android::base::StringPrintf; +using startop::dex::ClassBuilder; +using startop::dex::DexBuilder; +using startop::dex::MethodBuilder; +using startop::dex::Prototype; +using startop::dex::TypeDescriptor; using namespace startop::util; using std::string; @@ -41,34 +50,44 @@ DEFINE_bool(dex, false, "Generate a DEX file instead of Java"); DEFINE_string(out, kStdoutFilename, "Where to write the generated class"); DEFINE_string(package, "", "The package name for the generated class (required)"); -class ViewCompilerXmlVisitor : public XMLVisitor { +template <typename Visitor> +class XmlVisitorAdapter : public XMLVisitor { public: - explicit ViewCompilerXmlVisitor(JavaLangViewBuilder* builder) : builder_(builder) {} + explicit XmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {} bool VisitEnter(const XMLDocument& /*doc*/) override { - builder_->Start(); + visitor_->VisitStartDocument(); return true; } bool VisitExit(const XMLDocument& /*doc*/) override { - builder_->Finish(); + visitor_->VisitEndDocument(); return true; } bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override { - builder_->StartView(element.Name()); + visitor_->VisitStartTag( + std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes( + element.Name())); return true; } bool VisitExit(const XMLElement& /*element*/) override { - builder_->FinishView(); + visitor_->VisitEndTag(); return true; } private: - JavaLangViewBuilder* builder_; + Visitor* visitor_; }; +template <typename Builder> +void CompileLayout(XMLDocument* xml, Builder* builder) { + startop::LayoutCompilerVisitor visitor{builder}; + XmlVisitorAdapter<decltype(visitor)> adapter{&visitor}; + xml->Accept(&adapter); +} + } // end namespace int main(int argc, char** argv) { @@ -88,16 +107,8 @@ int main(int argc, char** argv) { return 1; } - if (FLAGS_dex) { - startop::dex::WriteTestDexFile("test.dex"); - return 0; - } - const char* const filename = argv[kFileNameParam]; - const string layout_name = FindLayoutNameFromFilename(filename); - - // We want to generate Java language code to inflate exactly this layout. This means - // generating code to walk the resource XML too. + const string layout_name = startop::util::FindLayoutNameFromFilename(filename); XMLDocument xml; xml.LoadFile(filename); @@ -108,15 +119,34 @@ int main(int argc, char** argv) { return 1; } + const bool is_stdout = FLAGS_out == kStdoutFilename; + std::ofstream outfile; - if (FLAGS_out != kStdoutFilename) { + if (!is_stdout) { outfile.open(FLAGS_out); } - JavaLangViewBuilder builder{ - FLAGS_package, layout_name, FLAGS_out == kStdoutFilename ? std::cout : outfile}; - - ViewCompilerXmlVisitor visitor{&builder}; - xml.Accept(&visitor); + if (FLAGS_dex) { + DexBuilder dex_file; + string class_name = StringPrintf("%s.CompiledView", FLAGS_package.c_str()); + ClassBuilder compiled_view{dex_file.MakeClass(class_name)}; + MethodBuilder method{compiled_view.CreateMethod( + layout_name, + Prototype{TypeDescriptor::FromClassname("android.view.View"), + TypeDescriptor::FromClassname("android.content.Context"), + TypeDescriptor::Int()})}; + startop::DexViewBuilder builder{&method}; + CompileLayout(&xml, &builder); + method.Encode(); + + slicer::MemView image{dex_file.CreateImage()}; + + (is_stdout ? std::cout : outfile).write(image.ptr<const char>(), image.size()); + } else { + // Generate Java language output. + JavaLangViewBuilder builder{FLAGS_package, layout_name, is_stdout ? std::cout : outfile}; + + CompileLayout(&xml, &builder); + } return 0; } |