Skip to content

Commit

Permalink
Starter Task #2: Add support to IRAssembler for interface field values
Browse files Browse the repository at this point in the history
Summary: Implemented checking for s-expression patterns that can be a string or a number, and added test cases which check these. Additionally, added validation of types and error checking.

Reviewed By: wsanville

Differential Revision: D58156222

fbshipit-source-id: 553574f1baf63ea41ba046b6a55dafc7dbcff2ce
  • Loading branch information
nithyaarunmeta authored and facebook-github-bot committed Jun 6, 2024
1 parent b700534 commit 78741a0
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 9 deletions.
72 changes: 65 additions & 7 deletions libredex/IRAssembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#include <boost/functional/hash.hpp>
#include <boost/optional/optional.hpp>
#include <cstdint>
#include <cstdio>
#include <sstream>
#include <tuple>
#include <unordered_map>
Expand Down Expand Up @@ -1027,6 +1029,63 @@ DexAccessFlags parse_access_flags(const s_expr& access_tokens) {

} // namespace

DexField* create_concrete_field(const std::string& field_name,
const DexAccessFlags& access_flags,
s_expr tail) {
auto field = DexField::make_field(field_name);

DexField* ret = field->make_concrete(access_flags);

// If we have an additional paramter, adding that data in as well
if (!tail.is_nil()) {
s_expr code_expr;
s_patn({s_patn(code_expr)}, tail).match_with(tail);
auto ret_type = ret->get_type();

// CASE 1: is an integer (prefixed be #)
if (code_expr.is_int32()) {
always_assert_log(type::is_primitive(ret_type),
"Inputted primitive but did not expect primitive");
ret->get_static_value()->value(code_expr.get_int32());
}

// CASE 2: is a string (no prefix of #)
else if (code_expr.is_string()) {
// turning s-expression into string
const auto& code_expr_str = code_expr.get_string();

// BOOLEAN
if (type::is_boolean(ret_type)) {
ret->get_static_value()->value(code_expr_str == "true");
}

// PRIMITIVE TYPE
else if (type::is_primitive(ret_type)) {
uint64_t val;
auto result = std::from_chars(
code_expr_str.data(), code_expr_str.data() + code_expr_str.size(),
val, 16);
always_assert_log(result.ec != std::errc::invalid_argument,
"Invalid payload: \"%s\"", code_expr_str.c_str());
ret->get_static_value()->value(val);
}

// REGULAR STRING
else {
auto dex_string = DexString::make_string(code_expr_str);
always_assert_log(ret_type == type::java_lang_String(),
"Inputted string but did not expect string");
auto encoded_string = new DexEncodedValueString(dex_string);
ret->set_value(std::unique_ptr<DexEncodedValue>(encoded_string));
}
} else {
always_assert_log(false, "Invalid code expression for field");
}
}
always_assert(tail.is_nil());
return ret;
}

DexMethod* method_from_s_expr(const s_expr& e) {
s_expr tail;
s_patn({s_patn("method")}, tail)
Expand Down Expand Up @@ -1122,18 +1181,17 @@ std::variant<DexField*, DexMethod*> interface_member_from_s_expr(
std::move(no_code), true /* is_virtual */);
}
}

s_patn({s_patn("field")}, tail)
.must_match(e, "field definitions must start with 'field'");

std::string field_name;
s_patn({s_patn(&field_name)}, tail).must_match(tail, "Expecting field name");
always_assert(tail.is_nil());

auto field = DexField::make_field(field_name);
// Fields should be public, static, final
return field->make_concrete(DexAccessFlags::ACC_PUBLIC |
DexAccessFlags::ACC_STATIC |
DexAccessFlags::ACC_FINAL);
return create_concrete_field(field_name,
DexAccessFlags::ACC_PUBLIC |
DexAccessFlags::ACC_STATIC |
DexAccessFlags::ACC_FINAL,
tail);
}

} // namespace
Expand Down
43 changes: 41 additions & 2 deletions test/unit/IRAssemblerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "IRAssembler.h"

#include <cstdint>
#include <gtest/gtest.h>

#include "DexAnnotation.h"
Expand Down Expand Up @@ -757,6 +758,14 @@ TEST_F(IRAssemblerTest, assembleInterfaceFromString) {
(method "LIface;.two:(Ljava/lang/String;)I")
(field "LIface;.three:I")
(field "LIface;.four:Ljava/lang/String;")
(field "LIface;.five:I" #5)
(field "LIface;.six:I" #123)
(field "LIface;.seven:Ljava/lang/String;" hello)
(field "LIface;.eight:Z" true)
(field "LIface;.nine:Z" false)
(field "LIface;.ten:I;" a)
(field "LIface;.eleven:I;" b)
(field "LIface;.twelve:I;" ab)
)
)");
EXPECT_TRUE(is_interface(iface));
Expand All @@ -777,13 +786,16 @@ TEST_F(IRAssemblerTest, assembleInterfaceFromString) {

EXPECT_TRUE(iface->get_ifields().empty());
const auto& fields = iface->get_sfields();
EXPECT_EQ(fields.size(), 2);
EXPECT_EQ(fields.size(), 10);
for (const auto& f : fields) {
EXPECT_EQ(f->get_access(),
DexAccessFlags::ACC_PUBLIC | DexAccessFlags::ACC_STATIC |
DexAccessFlags::ACC_FINAL);
auto name = f->str();
EXPECT_TRUE(name == "three" || name == "four")
EXPECT_TRUE(name == "three" || name == "four" || name == "five" ||
name == "six" || name == "seven" || name == "eight" ||
name == "nine" || name == "ten" || name == "eleven" ||
name == "twelve")
<< "Got unexpected field: " << name;
if (name == "three") {
auto static_value = f->get_static_value();
Expand All @@ -793,6 +805,33 @@ TEST_F(IRAssemblerTest, assembleInterfaceFromString) {
auto static_value = f->get_static_value();
EXPECT_EQ(static_value->value(), false);
EXPECT_EQ(static_value->evtype(), DEVT_NULL);
} else if (name == "five") {
auto static_value = f->get_static_value();
EXPECT_EQ(static_value->value(), 5);
EXPECT_EQ(static_value->evtype(), DEVT_INT);
} else if (name == "six") {
auto static_value = f->get_static_value();
EXPECT_EQ(static_value->value(), 123);
EXPECT_EQ(static_value->evtype(), DEVT_INT);
} else if (name == "seven") {
auto static_value = f->get_static_value();
EXPECT_EQ(static_value->evtype(), DEVT_STRING);
EXPECT_EQ(static_value->show(), "hello");
} else if (name == "eight") {
auto static_value = f->get_static_value();
EXPECT_EQ(static_value->value(), 1);
} else if (name == "nine") {
auto static_value = f->get_static_value();
EXPECT_EQ(static_value->value(), 0);
} else if (name == "ten") {
auto static_value = f->get_static_value();
EXPECT_EQ(static_value->value(), 10);
} else if (name == "eleven") {
auto static_value = f->get_static_value();
EXPECT_EQ(static_value->value(), 11);
} else if (name == "twelve") {
auto static_value = f->get_static_value();
EXPECT_EQ(static_value->value(), 171);
}
}

Expand Down

0 comments on commit 78741a0

Please sign in to comment.