blob: 8c0edac0686522397b7f5dcdc3c46867305cb9a1 [file] [log] [blame]
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/compiler/backend/sexpression.h"
#include <cmath>
#include "platform/assert.h"
#include "vm/unit_test.h"
namespace dart {
#define EXPECT_SEXP_PARSE_ERROR(sexp, parser, pos, message) \
do { \
if (sexp != nullptr) { \
dart::Expect(__FILE__, __LINE__) \
.Fail("parse unexpectedly succeeded for \"%s\"", parser.Input()); \
} \
EXPECT_EQ(pos, parser.error_pos()); \
EXPECT_STREQ(message, parser.error_message()); \
} while (false);
#define EXPECT_SEXP_PARSE_SUCCESS(sexp, parser) \
do { \
if (sexp == nullptr) { \
EXPECT_NOTNULL(parser.error_message()); \
dart::Expect(__FILE__, __LINE__) \
.Fail("parse unexpectedly failed at \"%s\": %" Pd ": %s", \
parser.Input() + parser.error_pos(), parser.error_pos(), \
parser.error_message()); \
} \
} while (false);
static const char* const shared_sexp_cstr =
"(def v0 (Constant 3) { type (CompileType 147 { nullable false, name "
"\"T{Smi}\"}), })";
static void CheckDeserializedSExpParts(SExpression* sexp) {
EXPECT_NOTNULL(sexp);
EXPECT(sexp->IsList());
SExpList* list = sexp->AsList();
EXPECT_EQ(3, list->Length());
EXPECT_NOTNULL(list->At(0));
EXPECT(list->At(0)->IsSymbol());
EXPECT_STREQ("def", list->At(0)->AsSymbol()->value());
EXPECT_NOTNULL(list->At(1));
EXPECT(list->At(1)->IsSymbol());
EXPECT_STREQ("v0", list->At(1)->AsSymbol()->value());
EXPECT_NOTNULL(list->At(2));
EXPECT(list->At(2)->IsList());
SExpList* sublist = list->At(2)->AsList();
EXPECT_EQ(2, sublist->Length());
EXPECT_NOTNULL(sublist->At(0));
EXPECT(sublist->At(0)->IsSymbol());
EXPECT_STREQ("Constant", sublist->At(0)->AsSymbol()->value());
EXPECT_NOTNULL(sublist->At(1));
EXPECT(sublist->At(1)->IsInteger());
EXPECT_EQ(3, sublist->At(1)->AsInteger()->value());
EXPECT_EQ(0, sublist->ExtraLength());
EXPECT_EQ(1, list->ExtraLength());
EXPECT(list->ExtraHasKey("type"));
EXPECT(list->ExtraLookupValue("type")->IsList());
SExpList* ctype = list->ExtraLookupValue("type")->AsList();
EXPECT_EQ(2, ctype->Length());
EXPECT_NOTNULL(ctype->At(0));
EXPECT(ctype->At(0)->IsSymbol());
EXPECT_STREQ("CompileType", ctype->At(0)->AsSymbol()->value());
EXPECT_NOTNULL(ctype->At(1));
EXPECT(ctype->At(1)->IsInteger());
EXPECT_EQ(147, ctype->At(1)->AsInteger()->value());
EXPECT_EQ(2, ctype->ExtraLength());
EXPECT(ctype->ExtraHasKey("nullable"));
EXPECT(ctype->ExtraLookupValue("nullable")->IsBool());
EXPECT(!ctype->ExtraLookupValue("nullable")->AsBool()->value());
EXPECT(ctype->ExtraHasKey("name"));
EXPECT(ctype->ExtraLookupValue("name")->IsString());
EXPECT_STREQ(ctype->ExtraLookupValue("name")->AsString()->value(), "T{Smi}");
}
ISOLATE_UNIT_TEST_CASE(DeserializeSExp) {
Zone* const zone = Thread::Current()->zone();
SExpression* sexp = SExpression::FromCString(zone, shared_sexp_cstr);
CheckDeserializedSExpParts(sexp);
// Treating escaped backslash appropriately so string is terminated.
{
const char* const cstr = "(def v0 (Constant 3) { foo \"123\\\\\" })";
SExpParser parser(zone, cstr, strlen(cstr));
SExpression* const sexp = parser.Parse();
EXPECT_SEXP_PARSE_SUCCESS(sexp, parser);
EXPECT(sexp->IsList());
EXPECT_EQ(1, sexp->AsList()->ExtraLength());
EXPECT(sexp->AsList()->ExtraHasKey("foo"));
auto val = sexp->AsList()->ExtraLookupValue("foo");
EXPECT(val->IsString());
EXPECT_STREQ("123\\", val->AsString()->value());
}
// Valid unicode escapes are properly handled.
{
const char* const cstr =
"(def v0 (Constant 3) { foo \"\\u0001\\u0020\\u0054\" })";
SExpParser parser(zone, cstr, strlen(cstr));
SExpression* const sexp = parser.Parse();
EXPECT_SEXP_PARSE_SUCCESS(sexp, parser);
EXPECT(sexp->IsList());
EXPECT_EQ(1, sexp->AsList()->ExtraLength());
EXPECT(sexp->AsList()->ExtraHasKey("foo"));
auto val = sexp->AsList()->ExtraLookupValue("foo");
EXPECT(val->IsString());
EXPECT_STREQ("\x01 T", val->AsString()->value());
}
}
ISOLATE_UNIT_TEST_CASE(DeserializeSExpNumbers) {
Zone* const zone = Thread::Current()->zone();
// Negative integers are handled.
{
const char* const cstr = "(-4 -50 -1414243)";
SExpParser parser(zone, cstr, strlen(cstr));
SExpression* const sexp = parser.Parse();
EXPECT_SEXP_PARSE_SUCCESS(sexp, parser);
EXPECT(sexp->IsList());
auto list = sexp->AsList();
EXPECT_EQ(3, list->Length());
EXPECT_EQ(0, list->ExtraLength());
for (intptr_t i = 0; i < list->Length(); i++) {
EXPECT(list->At(i)->IsInteger());
EXPECT(list->At(i)->AsInteger()->value() < 0);
}
}
// Various decimal/exponent Doubles are appropriately handled.
{
const char* const cstr = "(1.05 0.05 .03 1e100 1e-100)";
SExpParser parser(zone, cstr, strlen(cstr));
SExpression* const sexp = parser.Parse();
EXPECT_SEXP_PARSE_SUCCESS(sexp, parser);
EXPECT(sexp->IsList());
auto list = sexp->AsList();
EXPECT_EQ(5, list->Length());
EXPECT_EQ(0, list->ExtraLength());
EXPECT(list->At(0)->IsDouble());
double val = list->At(0)->AsDouble()->value();
EXPECT(val > 1.04 && val < 1.06);
EXPECT(list->At(1)->IsDouble());
val = list->At(1)->AsDouble()->value();
EXPECT(val > 0.04 && val < 0.06);
EXPECT(list->At(2)->IsDouble());
val = list->At(2)->AsDouble()->value();
EXPECT(val > 0.02 && val < 0.04);
EXPECT(list->At(3)->IsDouble());
val = list->At(3)->AsDouble()->value();
EXPECT(val > 0.9e100 && val < 1.1e100);
EXPECT(list->At(4)->IsDouble());
val = list->At(4)->AsDouble()->value();
EXPECT(val > 0.9e-100 && val < 1.1e-100);
}
// Special Double symbols are appropriately handled.
{
const char* const cstr = "(NaN Infinity -Infinity)";
SExpParser parser(zone, cstr, strlen(cstr));
SExpression* const sexp = parser.Parse();
EXPECT_SEXP_PARSE_SUCCESS(sexp, parser);
EXPECT(sexp->IsList());
auto list = sexp->AsList();
EXPECT_EQ(3, list->Length());
EXPECT_EQ(0, list->ExtraLength());
EXPECT(list->At(0)->IsDouble());
double val = list->At(0)->AsDouble()->value();
EXPECT(isnan(val));
EXPECT(list->At(1)->IsDouble());
val = list->At(1)->AsDouble()->value();
EXPECT(val > 0.0);
EXPECT(isinf(val));
EXPECT(list->At(2)->IsDouble());
val = list->At(2)->AsDouble()->value();
EXPECT(val < 0.0);
EXPECT(isinf(val));
}
}
ISOLATE_UNIT_TEST_CASE(DeserializeSExpRoundTrip) {
Zone* const zone = Thread::Current()->zone();
SExpression* sexp = SExpression::FromCString(zone, shared_sexp_cstr);
TextBuffer buf(100);
sexp->SerializeTo(zone, &buf, "", 9999);
SExpression* round_trip = SExpression::FromCString(zone, buf.buffer());
CheckDeserializedSExpParts(round_trip);
EXPECT(sexp->Equals(round_trip));
char* const old_serialization = buf.Steal();
round_trip->SerializeTo(zone, &buf, "", 9999);
char* const new_serialization = buf.buffer();
EXPECT_STREQ(old_serialization, new_serialization);
free(old_serialization);
}
ISOLATE_UNIT_TEST_CASE(DeserializeSExpMapsJoined) {
Zone* const zone = Thread::Current()->zone();
// Same as shared_sexp_cstr except we split the map on the CompileType into
// two parts.
const char* const cstr =
"(def v0 (Constant 3) { type (CompileType { nullable false } 147 { name "
"\"T{Smi}\"}), })";
SExpression* sexp = SExpression::FromCString(zone, cstr);
CheckDeserializedSExpParts(sexp);
}
ISOLATE_UNIT_TEST_CASE(DeserializeSExpFailures) {
Zone* const zone = Thread::Current()->zone();
// Unterminated s-exp list
{
const char* const before_start = "(def v0 ";
const char* const after_start = "(Constant 3";
const char* const cstr =
OS::SCreate(zone, "%s%s", before_start, after_start);
const intptr_t start_pos = strlen(before_start);
const intptr_t error_pos = strlen(cstr);
SExpParser parser(zone, cstr, strlen(cstr));
SExpression* const sexp = parser.Parse();
const char* const expected_message =
OS::SCreate(zone, SExpParser::ErrorStrings::kOpenSExpList, start_pos);
EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message);
}
// Non-symbol label in map pair
{
const char* const before_error = "(def v0 (Constant 3) { ";
const intptr_t error_pos = strlen(before_error);
const char* const error = "3 4 })";
const char* const cstr = OS::SCreate(zone, "%s%s", before_error, error);
SExpParser parser(zone, cstr, strlen(cstr));
SExpression* const sexp = parser.Parse();
const char* const expected_message =
SExpParser::ErrorStrings::kNonSymbolLabel;
EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message);
}
// No values in a map pair
{
const char* const label = "foo";
const char* const before_error =
OS::SCreate(zone, "(def v0 (Constant 3) { %s ", label);
const intptr_t error_pos = strlen(before_error);
const char* const error = "})";
const char* const cstr = OS::SCreate(zone, "%s%s", before_error, error);
SExpParser parser(zone, cstr, strlen(cstr));
SExpression* const sexp = parser.Parse();
const char* const expected_message =
OS::SCreate(zone, SExpParser::ErrorStrings::kNoMapValue, label);
EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message);
}
// Multiple values in a map pair
{
const char* const label = "foo";
const char* const before_error =
OS::SCreate(zone, "(def v0 (Constant 3) { %s 4 ", label);
const intptr_t error_pos = strlen(before_error);
const char* const error = "5, })";
const char* const cstr = OS::SCreate(zone, "%s%s", before_error, error);
SExpParser parser(zone, cstr, strlen(cstr));
SExpression* const sexp = parser.Parse();
const char* const expected_message =
OS::SCreate(zone, SExpParser::ErrorStrings::kExtraMapValue, label);
EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message);
}
// Unterminated quoted string
{
const char* const before_string =
OS::SCreate(zone, "(def v0 (Constant 3) { foo ");
const intptr_t string_pos = strlen(before_string);
const char* const error = "\"abc })";
const char* const cstr = OS::SCreate(zone, "%s%s", before_string, error);
const intptr_t error_pos = strlen(cstr);
SExpParser parser(zone, cstr, strlen(cstr));
SExpression* const sexp = parser.Parse();
const char* const expected_message =
OS::SCreate(zone, SExpParser::ErrorStrings::kOpenString, string_pos);
EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message);
}
// Unterminated extra info map
{
const char* const before_map = "(def v0 (Constant 3) ";
const intptr_t map_pos = strlen(before_map);
const char* const map_start = "{ foo 3, ";
const char* const before_error =
OS::SCreate(zone, "%s%s", before_map, map_start);
const intptr_t error_pos = strlen(before_error);
const char* const error = ")";
const char* const cstr = OS::SCreate(zone, "%s%s", before_error, error);
SExpParser parser(zone, cstr, strlen(cstr));
SExpression* const sexp = parser.Parse();
const char* const expected_message =
OS::SCreate(zone, SExpParser::ErrorStrings::kOpenMap, map_pos);
EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message);
}
// Repeated extra info map label
{
const char* const label = "foo";
const char* const before_error =
OS::SCreate(zone, "(def v0 (Constant 3) { %s 3, ", label);
const intptr_t error_pos = strlen(before_error);
const char* const error = OS::SCreate(zone, "%s 4, })", label);
const char* const cstr = OS::SCreate(zone, "%s%s", before_error, error);
SExpParser parser(zone, cstr, strlen(cstr));
SExpression* const sexp = parser.Parse();
const char* const expected_message =
OS::SCreate(zone, SExpParser::ErrorStrings::kRepeatedMapLabel, label);
EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message);
}
// Unicode escape with non-hex digits.
{
const char* const before_error = "(def v0 (Constant 3) { foo \"123";
const intptr_t error_pos = strlen(before_error);
const char* const error = "\\u12FG\" })";
const char* const cstr = OS::SCreate(zone, "%s%s", before_error, error);
SExpParser parser(zone, cstr, strlen(cstr));
SExpression* const sexp = parser.Parse();
const char* const expected_message =
SExpParser::ErrorStrings::kBadUnicodeEscape;
EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message);
}
// Unicode escape with less than four hex digits.
{
const char* const before_error = "(def v0 (Constant 3) { foo \"123";
const intptr_t error_pos = strlen(before_error);
const char* const error = "\\u12\" })";
const char* const cstr = OS::SCreate(zone, "%s%s", before_error, error);
SExpParser parser(zone, cstr, strlen(cstr));
SExpression* const sexp = parser.Parse();
const char* const expected_message =
SExpParser::ErrorStrings::kBadUnicodeEscape;
EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message);
}
// Treating backslashed quote appropriately to detect unterminated string
{
const char* const before_string = "(def v0 (Constant 3) { foo ";
const intptr_t string_pos = strlen(before_string);
const char* const error = "\"123\\\" })";
const char* const cstr = OS::SCreate(zone, "%s%s", before_string, error);
const intptr_t error_pos = strlen(cstr);
SExpParser parser(zone, cstr, strlen(cstr));
SExpression* const sexp = parser.Parse();
const char* const expected_message =
OS::SCreate(zone, SExpParser::ErrorStrings::kOpenString, string_pos);
EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message);
}
}
} // namespace dart