[ESnext] Parse dynamic import expression
Rewrites import expression into a runtime call. Uses peekahead to determine if parsing an import declaration or import expression. The runtime call doesn't actually do the import yet, will be added in follow on patch. Adds a new --harmony-dynamic-import flag. Adds a ignore_error_msg parameter to the test runner to ignore the discrepancy in the error messages while parsing import expression with parser and pre parser. This discrepancy will actually never happen in real code. BUG=v8:5785 Review-Url: https://codereview.chromium.org/2661933003 Cr-Commit-Position: refs/heads/master@{#42820}
This commit is contained in:
parent
c41f5f2de6
commit
e791ded4cd
@ -3516,6 +3516,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_restrictive_generators)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_trailing_commas)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_class_fields)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_object_rest_spread)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_dynamic_import)
|
||||
|
||||
void InstallPublicSymbol(Factory* factory, Handle<Context> native_context,
|
||||
const char* name, Handle<Symbol> value) {
|
||||
@ -4085,6 +4086,7 @@ bool Genesis::InstallExperimentalNatives() {
|
||||
static const char* harmony_class_fields_natives[] = {nullptr};
|
||||
static const char* harmony_object_rest_spread_natives[] = {nullptr};
|
||||
static const char* harmony_async_iteration_natives[] = {nullptr};
|
||||
static const char* harmony_dynamic_import_natives[] = {nullptr};
|
||||
|
||||
for (int i = ExperimentalNatives::GetDebuggerCount();
|
||||
i < ExperimentalNatives::GetBuiltinsCount(); i++) {
|
||||
|
@ -194,16 +194,17 @@ DEFINE_IMPLICATION(es_staging, harmony_regexp_lookbehind)
|
||||
DEFINE_IMPLICATION(es_staging, move_object_start)
|
||||
|
||||
// Features that are still work in progress (behind individual flags).
|
||||
#define HARMONY_INPROGRESS(V) \
|
||||
V(harmony_array_prototype_values, "harmony Array.prototype.values") \
|
||||
V(harmony_function_sent, "harmony function.sent") \
|
||||
V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \
|
||||
V(harmony_simd, "harmony simd") \
|
||||
V(harmony_do_expressions, "harmony do-expressions") \
|
||||
V(harmony_regexp_named_captures, "harmony regexp named captures") \
|
||||
V(harmony_regexp_property, "harmony unicode regexp property classes") \
|
||||
V(harmony_class_fields, "harmony public fields in class literals") \
|
||||
V(harmony_async_iteration, "harmony async iteration")
|
||||
#define HARMONY_INPROGRESS(V) \
|
||||
V(harmony_array_prototype_values, "harmony Array.prototype.values") \
|
||||
V(harmony_function_sent, "harmony function.sent") \
|
||||
V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \
|
||||
V(harmony_simd, "harmony simd") \
|
||||
V(harmony_do_expressions, "harmony do-expressions") \
|
||||
V(harmony_regexp_named_captures, "harmony regexp named captures") \
|
||||
V(harmony_regexp_property, "harmony unicode regexp property classes") \
|
||||
V(harmony_class_fields, "harmony public fields in class literals") \
|
||||
V(harmony_async_iteration, "harmony async iteration") \
|
||||
V(harmony_dynamic_import, "harmony dynamic import")
|
||||
|
||||
// Features that are complete (but still behind --harmony/es-staging flag).
|
||||
#define HARMONY_STAGED(V) \
|
||||
|
@ -224,7 +224,8 @@ class ParserBase {
|
||||
allow_harmony_restrictive_generators_(false),
|
||||
allow_harmony_trailing_commas_(false),
|
||||
allow_harmony_class_fields_(false),
|
||||
allow_harmony_object_rest_spread_(false) {}
|
||||
allow_harmony_object_rest_spread_(false),
|
||||
allow_harmony_dynamic_import_(false) {}
|
||||
|
||||
#define ALLOW_ACCESSORS(name) \
|
||||
bool allow_##name() const { return allow_##name##_; } \
|
||||
@ -238,6 +239,7 @@ class ParserBase {
|
||||
ALLOW_ACCESSORS(harmony_trailing_commas);
|
||||
ALLOW_ACCESSORS(harmony_class_fields);
|
||||
ALLOW_ACCESSORS(harmony_object_rest_spread);
|
||||
ALLOW_ACCESSORS(harmony_dynamic_import);
|
||||
|
||||
#undef ALLOW_ACCESSORS
|
||||
|
||||
@ -1206,6 +1208,7 @@ class ParserBase {
|
||||
int class_token_pos, bool* ok);
|
||||
ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, bool* ok);
|
||||
ExpressionT ParseSuperExpression(bool is_new, bool* ok);
|
||||
ExpressionT ParseDynamicImportExpression(bool* ok);
|
||||
ExpressionT ParseNewTargetExpression(bool* ok);
|
||||
|
||||
void ParseFormalParameter(FormalParametersT* parameters, bool* ok);
|
||||
@ -1484,6 +1487,7 @@ class ParserBase {
|
||||
bool allow_harmony_trailing_commas_;
|
||||
bool allow_harmony_class_fields_;
|
||||
bool allow_harmony_object_rest_spread_;
|
||||
bool allow_harmony_dynamic_import_;
|
||||
|
||||
friend class DiscardableZoneScope;
|
||||
};
|
||||
@ -3350,7 +3354,11 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberExpression(
|
||||
// MemberExpression ::
|
||||
// (PrimaryExpression | FunctionLiteral | ClassLiteral)
|
||||
// ('[' Expression ']' | '.' Identifier | Arguments | TemplateLiteral)*
|
||||
|
||||
//
|
||||
// CallExpression ::
|
||||
// (SuperCall | ImportCall)
|
||||
// ('[' Expression ']' | '.' Identifier | Arguments | TemplateLiteral)*
|
||||
//
|
||||
// The '[' Expression ']' and '.' Identifier parts are parsed by
|
||||
// ParseMemberExpressionContinuation, and the Arguments part is parsed by the
|
||||
// caller.
|
||||
@ -3403,6 +3411,8 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberExpression(
|
||||
} else if (peek() == Token::SUPER) {
|
||||
const bool is_new = false;
|
||||
result = ParseSuperExpression(is_new, CHECK_OK);
|
||||
} else if (allow_harmony_dynamic_import() && peek() == Token::IMPORT) {
|
||||
result = ParseDynamicImportExpression(CHECK_OK);
|
||||
} else {
|
||||
result = ParsePrimaryExpression(is_async, CHECK_OK);
|
||||
}
|
||||
@ -3411,6 +3421,20 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberExpression(
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
typename ParserBase<Impl>::ExpressionT
|
||||
ParserBase<Impl>::ParseDynamicImportExpression(bool* ok) {
|
||||
DCHECK(allow_harmony_dynamic_import());
|
||||
Consume(Token::IMPORT);
|
||||
int pos = position();
|
||||
Expect(Token::LPAREN, CHECK_OK);
|
||||
ExpressionT arg = ParseAssignmentExpression(true, CHECK_OK);
|
||||
Expect(Token::RPAREN, CHECK_OK);
|
||||
ZoneList<ExpressionT>* args = new (zone()) ZoneList<ExpressionT>(1, zone());
|
||||
args->Add(arg, zone());
|
||||
return factory()->NewCallRuntime(Runtime::kDynamicImportCall, args, pos);
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseSuperExpression(
|
||||
bool is_new, bool* ok) {
|
||||
|
@ -553,6 +553,7 @@ Parser::Parser(ParseInfo* info)
|
||||
set_allow_harmony_trailing_commas(FLAG_harmony_trailing_commas);
|
||||
set_allow_harmony_class_fields(FLAG_harmony_class_fields);
|
||||
set_allow_harmony_object_rest_spread(FLAG_harmony_object_rest_spread);
|
||||
set_allow_harmony_dynamic_import(FLAG_harmony_dynamic_import);
|
||||
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
|
||||
++feature) {
|
||||
use_counts_[feature] = 0;
|
||||
@ -962,15 +963,21 @@ Statement* Parser::ParseModuleItem(bool* ok) {
|
||||
// ExportDeclaration
|
||||
// StatementListItem
|
||||
|
||||
switch (peek()) {
|
||||
case Token::IMPORT:
|
||||
ParseImportDeclaration(CHECK_OK);
|
||||
return factory()->NewEmptyStatement(kNoSourcePosition);
|
||||
case Token::EXPORT:
|
||||
return ParseExportDeclaration(ok);
|
||||
default:
|
||||
return ParseStatementListItem(ok);
|
||||
Token::Value next = peek();
|
||||
|
||||
if (next == Token::EXPORT) {
|
||||
return ParseExportDeclaration(ok);
|
||||
}
|
||||
|
||||
// We must be careful not to parse a dynamic import expression as an import
|
||||
// declaration.
|
||||
if (next == Token::IMPORT &&
|
||||
(!allow_harmony_dynamic_import() || PeekAhead() != Token::LPAREN)) {
|
||||
ParseImportDeclaration(CHECK_OK);
|
||||
return factory()->NewEmptyStatement(kNoSourcePosition);
|
||||
}
|
||||
|
||||
return ParseStatementListItem(ok);
|
||||
}
|
||||
|
||||
|
||||
@ -2795,6 +2802,7 @@ Parser::LazyParsingResult Parser::SkipFunction(
|
||||
SET_ALLOW(harmony_trailing_commas);
|
||||
SET_ALLOW(harmony_class_fields);
|
||||
SET_ALLOW(harmony_object_rest_spread);
|
||||
SET_ALLOW(harmony_dynamic_import);
|
||||
#undef SET_ALLOW
|
||||
}
|
||||
// Aborting inner function preparsing would leave scopes in an inconsistent
|
||||
|
@ -759,6 +759,12 @@ class PreParserFactory {
|
||||
return PreParserStatement::Default();
|
||||
}
|
||||
|
||||
PreParserExpression NewCallRuntime(Runtime::FunctionId id,
|
||||
ZoneList<PreParserExpression>* arguments,
|
||||
int pos) {
|
||||
return PreParserExpression::Default();
|
||||
}
|
||||
|
||||
// Return the object itself as AstVisitor and implement the needed
|
||||
// dummy method right in this class.
|
||||
PreParserFactory* visitor() { return this; }
|
||||
|
@ -9,6 +9,13 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DynamicImportCall) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
// TODO(gsathya): Implement ImportCall.
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_GetModuleNamespace) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
|
@ -350,6 +350,7 @@ namespace internal {
|
||||
#define FOR_EACH_INTRINSIC_MATHS(F) F(GenerateRandomNumbers, 0, 1)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_MODULE(F) \
|
||||
F(DynamicImportCall, 1, 1) \
|
||||
F(GetModuleNamespace, 1, 1) \
|
||||
F(LoadModuleVariable, 1, 1) \
|
||||
F(StoreModuleVariable, 2, 1)
|
||||
|
@ -1285,6 +1285,7 @@ enum ParserFlag {
|
||||
kAllowHarmonyTrailingCommas,
|
||||
kAllowHarmonyClassFields,
|
||||
kAllowHarmonyObjectRestSpread,
|
||||
kAllowHarmonyDynamicImport,
|
||||
};
|
||||
|
||||
enum ParserSyncTestResult {
|
||||
@ -1302,6 +1303,7 @@ void SetGlobalFlags(i::EnumSet<ParserFlag> flags) {
|
||||
i::FLAG_harmony_class_fields = flags.Contains(kAllowHarmonyClassFields);
|
||||
i::FLAG_harmony_object_rest_spread =
|
||||
flags.Contains(kAllowHarmonyObjectRestSpread);
|
||||
i::FLAG_harmony_dynamic_import = flags.Contains(kAllowHarmonyDynamicImport);
|
||||
}
|
||||
|
||||
void SetParserFlags(i::PreParser* parser, i::EnumSet<ParserFlag> flags) {
|
||||
@ -1316,13 +1318,15 @@ void SetParserFlags(i::PreParser* parser, i::EnumSet<ParserFlag> flags) {
|
||||
flags.Contains(kAllowHarmonyClassFields));
|
||||
parser->set_allow_harmony_object_rest_spread(
|
||||
flags.Contains(kAllowHarmonyObjectRestSpread));
|
||||
parser->set_allow_harmony_dynamic_import(
|
||||
flags.Contains(kAllowHarmonyDynamicImport));
|
||||
}
|
||||
|
||||
void TestParserSyncWithFlags(i::Handle<i::String> source,
|
||||
i::EnumSet<ParserFlag> flags,
|
||||
ParserSyncTestResult result,
|
||||
bool is_module = false,
|
||||
bool test_preparser = true) {
|
||||
bool is_module = false, bool test_preparser = true,
|
||||
bool ignore_error_msg = false) {
|
||||
i::Isolate* isolate = CcTest::i_isolate();
|
||||
i::Factory* factory = isolate->factory();
|
||||
|
||||
@ -1399,7 +1403,7 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
|
||||
CHECK(false);
|
||||
}
|
||||
// Check that preparser and parser produce the same error.
|
||||
if (test_preparser) {
|
||||
if (test_preparser && !ignore_error_msg) {
|
||||
i::Handle<i::String> preparser_message =
|
||||
pending_error_handler.FormatMessage(CcTest::i_isolate());
|
||||
if (!i::String::Equals(message_string, preparser_message)) {
|
||||
@ -1446,7 +1450,6 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TestParserSync(const char* source, const ParserFlag* varying_flags,
|
||||
size_t varying_flags_length,
|
||||
ParserSyncTestResult result = kSuccessOrError,
|
||||
@ -1454,7 +1457,8 @@ void TestParserSync(const char* source, const ParserFlag* varying_flags,
|
||||
size_t always_true_flags_length = 0,
|
||||
const ParserFlag* always_false_flags = NULL,
|
||||
size_t always_false_flags_length = 0,
|
||||
bool is_module = false, bool test_preparser = true) {
|
||||
bool is_module = false, bool test_preparser = true,
|
||||
bool ignore_error_msg = false) {
|
||||
i::Handle<i::String> str =
|
||||
CcTest::i_isolate()->factory()->NewStringFromAsciiChecked(source);
|
||||
for (int bits = 0; bits < (1 << varying_flags_length); bits++) {
|
||||
@ -1471,7 +1475,8 @@ void TestParserSync(const char* source, const ParserFlag* varying_flags,
|
||||
++flag_index) {
|
||||
flags.Remove(always_false_flags[flag_index]);
|
||||
}
|
||||
TestParserSyncWithFlags(str, flags, result, is_module, test_preparser);
|
||||
TestParserSyncWithFlags(str, flags, result, is_module, test_preparser,
|
||||
ignore_error_msg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1611,16 +1616,13 @@ TEST(StrictOctal) {
|
||||
*exception));
|
||||
}
|
||||
|
||||
|
||||
void RunParserSyncTest(const char* context_data[][2],
|
||||
const char* statement_data[],
|
||||
ParserSyncTestResult result,
|
||||
const ParserFlag* flags = NULL, int flags_len = 0,
|
||||
const ParserFlag* always_true_flags = NULL,
|
||||
int always_true_len = 0,
|
||||
const ParserFlag* always_false_flags = NULL,
|
||||
int always_false_len = 0, bool is_module = false,
|
||||
bool test_preparser = true) {
|
||||
void RunParserSyncTest(
|
||||
const char* context_data[][2], const char* statement_data[],
|
||||
ParserSyncTestResult result, const ParserFlag* flags = NULL,
|
||||
int flags_len = 0, const ParserFlag* always_true_flags = NULL,
|
||||
int always_true_len = 0, const ParserFlag* always_false_flags = NULL,
|
||||
int always_false_len = 0, bool is_module = false,
|
||||
bool test_preparser = true, bool ignore_error_msg = false) {
|
||||
v8::HandleScope handles(CcTest::isolate());
|
||||
v8::Local<v8::Context> context = v8::Context::New(CcTest::isolate());
|
||||
v8::Context::Scope context_scope(context);
|
||||
@ -1675,25 +1677,23 @@ void RunParserSyncTest(const char* context_data[][2],
|
||||
CHECK(length == kProgramSize);
|
||||
TestParserSync(program.start(), flags, flags_len, result,
|
||||
always_true_flags, always_true_len, always_false_flags,
|
||||
always_false_len, is_module, test_preparser);
|
||||
always_false_len, is_module, test_preparser,
|
||||
ignore_error_msg);
|
||||
}
|
||||
}
|
||||
delete[] generated_flags;
|
||||
}
|
||||
|
||||
|
||||
void RunModuleParserSyncTest(const char* context_data[][2],
|
||||
const char* statement_data[],
|
||||
ParserSyncTestResult result,
|
||||
const ParserFlag* flags = NULL, int flags_len = 0,
|
||||
const ParserFlag* always_true_flags = NULL,
|
||||
int always_true_len = 0,
|
||||
const ParserFlag* always_false_flags = NULL,
|
||||
int always_false_len = 0,
|
||||
bool test_preparser = true) {
|
||||
void RunModuleParserSyncTest(
|
||||
const char* context_data[][2], const char* statement_data[],
|
||||
ParserSyncTestResult result, const ParserFlag* flags = NULL,
|
||||
int flags_len = 0, const ParserFlag* always_true_flags = NULL,
|
||||
int always_true_len = 0, const ParserFlag* always_false_flags = NULL,
|
||||
int always_false_len = 0, bool test_preparser = true,
|
||||
bool ignore_error_msg = false) {
|
||||
RunParserSyncTest(context_data, statement_data, result, flags, flags_len,
|
||||
always_true_flags, always_true_len, always_false_flags,
|
||||
always_false_len, true, test_preparser);
|
||||
always_false_len, true, test_preparser, ignore_error_msg);
|
||||
}
|
||||
|
||||
|
||||
@ -4145,6 +4145,140 @@ TEST(SuperErrors) {
|
||||
RunParserSyncTest(context_data, expression_data, kError);
|
||||
}
|
||||
|
||||
TEST(ImportExpressionSuccess) {
|
||||
// clang-format off
|
||||
const char* context_data[][2] = {
|
||||
{"", ""},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
const char* data[] = {
|
||||
"new import(x)",
|
||||
"import(1)",
|
||||
"import(y=x)",
|
||||
"f(...[import(y=x)])",
|
||||
"x = {[import(y=x)]: 1}",
|
||||
"var {[import(y=x)]: x} = {}",
|
||||
"({[import(y=x)]: x} = {})",
|
||||
"async () => { await import(x) }",
|
||||
"() => { import(x) }",
|
||||
"(import(y=x))",
|
||||
"{import(y=x)}",
|
||||
"import(import(x))",
|
||||
"x = import(x)",
|
||||
"var x = import(x)",
|
||||
"let x = import(x)",
|
||||
"for(x of import(x)) {}",
|
||||
"import(x).then()",
|
||||
NULL
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
|
||||
// We ignore test error messages because the error message from the
|
||||
// parser/preparser is different for the same data depending on the
|
||||
// context.
|
||||
// For example, a top level "import(" is parsed as an
|
||||
// import declaration. The parser parses the import token correctly
|
||||
// and then shows an "Unexpected token (" error message. The
|
||||
// preparser does not understand the import keyword (this test is
|
||||
// run without kAllowHarmonyDynamicImport flag), so this results in
|
||||
// an "Unexpected token import" error.
|
||||
RunParserSyncTest(context_data, data, kError);
|
||||
RunModuleParserSyncTest(context_data, data, kError, NULL, 0, NULL, 0, NULL, 0,
|
||||
true, true);
|
||||
static const ParserFlag flags[] = {kAllowHarmonyDynamicImport};
|
||||
RunParserSyncTest(context_data, data, kSuccess, NULL, 0, flags,
|
||||
arraysize(flags));
|
||||
RunModuleParserSyncTest(context_data, data, kSuccess, NULL, 0, flags,
|
||||
arraysize(flags));
|
||||
}
|
||||
|
||||
TEST(ImportExpressionErrors) {
|
||||
{
|
||||
// clang-format off
|
||||
const char* context_data[][2] = {
|
||||
{"", ""},
|
||||
{"var ", ""},
|
||||
{"let ", ""},
|
||||
{"new ", ""},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
const char* data[] = {
|
||||
"import(",
|
||||
"import)",
|
||||
"import()",
|
||||
"import('x",
|
||||
"import('x']",
|
||||
"import['x')",
|
||||
"import = x",
|
||||
"import[",
|
||||
"import[]",
|
||||
"import]",
|
||||
"import[x]",
|
||||
"import{",
|
||||
"import{x",
|
||||
"import{x}",
|
||||
"import(x, y)",
|
||||
"import(...y)",
|
||||
"import(x,)",
|
||||
"import(,)",
|
||||
"import(,y)",
|
||||
"import(;)",
|
||||
"[import]",
|
||||
"{import}",
|
||||
"import+",
|
||||
"import = 1",
|
||||
"import.wat",
|
||||
NULL
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
RunParserSyncTest(context_data, data, kError);
|
||||
// We ignore the error messages for the reason explained in the
|
||||
// ImportExpressionSuccess test.
|
||||
RunModuleParserSyncTest(context_data, data, kError, NULL, 0, NULL, 0, NULL,
|
||||
0, true, true);
|
||||
static const ParserFlag flags[] = {kAllowHarmonyDynamicImport};
|
||||
RunParserSyncTest(context_data, data, kError, NULL, 0, flags,
|
||||
arraysize(flags));
|
||||
|
||||
// We ignore test error messages because the error message from
|
||||
// the parser/preparser is different for the same data depending
|
||||
// on the context. For example, a top level "import{" is parsed
|
||||
// as an import declaration. The parser parses the import token
|
||||
// correctly and then shows an "Unexpected end of input" error
|
||||
// message because of the '{'. The preparser shows an "Unexpected
|
||||
// token {" because it's not a valid token in a CallExpression.
|
||||
RunModuleParserSyncTest(context_data, data, kError, NULL, 0, flags,
|
||||
arraysize(flags), NULL, 0, true, true);
|
||||
}
|
||||
|
||||
{
|
||||
// clang-format off
|
||||
const char* context_data[][2] = {
|
||||
{"var ", ""},
|
||||
{"let ", ""},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
const char* data[] = {
|
||||
"import('x')",
|
||||
NULL
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
RunParserSyncTest(context_data, data, kError);
|
||||
RunModuleParserSyncTest(context_data, data, kError);
|
||||
|
||||
static const ParserFlag flags[] = {kAllowHarmonyDynamicImport};
|
||||
RunParserSyncTest(context_data, data, kError, NULL, 0, flags,
|
||||
arraysize(flags));
|
||||
RunModuleParserSyncTest(context_data, data, kError, NULL, 0, flags,
|
||||
arraysize(flags));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SuperCall) {
|
||||
const char* context_data[][2] = {{"", ""},
|
||||
|
Loading…
Reference in New Issue
Block a user