| // Copyright 2015 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // TODO(jochen): Remove this after the setting is turned on globally. |
| #define V8_IMMINENT_DEPRECATION_WARNINGS |
| |
| #include <utility> |
| |
| #include "src/compiler/pipeline.h" |
| #include "src/execution.h" |
| #include "src/handles.h" |
| #include "src/interpreter/bytecode-array-builder.h" |
| #include "src/interpreter/interpreter.h" |
| #include "src/parser.h" |
| #include "test/cctest/cctest.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| |
| |
| static const char kFunctionName[] = "f"; |
| |
| |
| static MaybeHandle<Object> CallFunction(Isolate* isolate, |
| Handle<JSFunction> function) { |
| return Execution::Call(isolate, function, |
| isolate->factory()->undefined_value(), 0, nullptr); |
| } |
| |
| |
| template <class... A> |
| static MaybeHandle<Object> CallFunction(Isolate* isolate, |
| Handle<JSFunction> function, |
| A... args) { |
| Handle<Object> argv[] = {args...}; |
| return Execution::Call(isolate, function, |
| isolate->factory()->undefined_value(), sizeof...(args), |
| argv); |
| } |
| |
| |
| template <class... A> |
| class BytecodeGraphCallable { |
| public: |
| BytecodeGraphCallable(Isolate* isolate, Handle<JSFunction> function) |
| : isolate_(isolate), function_(function) {} |
| virtual ~BytecodeGraphCallable() {} |
| |
| MaybeHandle<Object> operator()(A... args) { |
| return CallFunction(isolate_, function_, args...); |
| } |
| |
| private: |
| Isolate* isolate_; |
| Handle<JSFunction> function_; |
| }; |
| |
| |
| class BytecodeGraphTester { |
| public: |
| BytecodeGraphTester(Isolate* isolate, Zone* zone, const char* script) |
| : isolate_(isolate), zone_(zone), script_(script) { |
| i::FLAG_ignition = true; |
| i::FLAG_always_opt = false; |
| i::FLAG_allow_natives_syntax = true; |
| // Set ignition filter flag via SetFlagsFromString to avoid double-free |
| // (or potential leak with StrDup() based on ownership confusion). |
| ScopedVector<char> ignition_filter(64); |
| SNPrintF(ignition_filter, "--ignition-filter=%s", kFunctionName); |
| FlagList::SetFlagsFromString(ignition_filter.start(), |
| ignition_filter.length()); |
| // Ensure handler table is generated. |
| isolate->interpreter()->Initialize(); |
| } |
| virtual ~BytecodeGraphTester() {} |
| |
| template <class... A> |
| BytecodeGraphCallable<A...> GetCallable() { |
| return BytecodeGraphCallable<A...>(isolate_, GetFunction()); |
| } |
| |
| static Handle<Object> NewObject(const char* script) { |
| return v8::Utils::OpenHandle(*CompileRun(script)); |
| } |
| |
| private: |
| Isolate* isolate_; |
| Zone* zone_; |
| const char* script_; |
| |
| Handle<JSFunction> GetFunction() { |
| CompileRun(script_); |
| Local<Function> api_function = Local<Function>::Cast( |
| CcTest::global() |
| ->Get(CcTest::isolate()->GetCurrentContext(), v8_str(kFunctionName)) |
| .ToLocalChecked()); |
| Handle<JSFunction> function = |
| Handle<JSFunction>::cast(v8::Utils::OpenHandle(*api_function)); |
| CHECK(function->shared()->HasBytecodeArray()); |
| |
| ParseInfo parse_info(zone_, function); |
| |
| CompilationInfo compilation_info(&parse_info); |
| compilation_info.SetOptimizing(BailoutId::None(), Handle<Code>()); |
| // TODO(mythria): Remove this step once parse_info is not needed. |
| CHECK(Compiler::ParseAndAnalyze(&parse_info)); |
| compiler::Pipeline pipeline(&compilation_info); |
| Handle<Code> code = pipeline.GenerateCode(); |
| function->ReplaceCode(*code); |
| |
| return function; |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(BytecodeGraphTester); |
| }; |
| |
| |
| #define SPACE() |
| |
| #define REPEAT_2(SEP, ...) __VA_ARGS__ SEP() __VA_ARGS__ |
| #define REPEAT_4(SEP, ...) \ |
| REPEAT_2(SEP, __VA_ARGS__) SEP() REPEAT_2(SEP, __VA_ARGS__) |
| #define REPEAT_8(SEP, ...) \ |
| REPEAT_4(SEP, __VA_ARGS__) SEP() REPEAT_4(SEP, __VA_ARGS__) |
| #define REPEAT_16(SEP, ...) \ |
| REPEAT_8(SEP, __VA_ARGS__) SEP() REPEAT_8(SEP, __VA_ARGS__) |
| #define REPEAT_32(SEP, ...) \ |
| REPEAT_16(SEP, __VA_ARGS__) SEP() REPEAT_16(SEP, __VA_ARGS__) |
| #define REPEAT_64(SEP, ...) \ |
| REPEAT_32(SEP, __VA_ARGS__) SEP() REPEAT_32(SEP, __VA_ARGS__) |
| #define REPEAT_128(SEP, ...) \ |
| REPEAT_64(SEP, __VA_ARGS__) SEP() REPEAT_64(SEP, __VA_ARGS__) |
| #define REPEAT_256(SEP, ...) \ |
| REPEAT_128(SEP, __VA_ARGS__) SEP() REPEAT_128(SEP, __VA_ARGS__) |
| |
| #define REPEAT_127(SEP, ...) \ |
| REPEAT_64(SEP, __VA_ARGS__) \ |
| SEP() \ |
| REPEAT_32(SEP, __VA_ARGS__) \ |
| SEP() \ |
| REPEAT_16(SEP, __VA_ARGS__) \ |
| SEP() \ |
| REPEAT_8(SEP, __VA_ARGS__) \ |
| SEP() \ |
| REPEAT_4(SEP, __VA_ARGS__) SEP() REPEAT_2(SEP, __VA_ARGS__) SEP() __VA_ARGS__ |
| |
| |
| template <int N> |
| struct ExpectedSnippet { |
| const char* code_snippet; |
| Handle<Object> return_value_and_parameters[N + 1]; |
| |
| inline Handle<Object> return_value() const { |
| return return_value_and_parameters[0]; |
| } |
| |
| inline Handle<Object> parameter(int i) const { |
| DCHECK_GE(i, 0); |
| DCHECK_LT(i, N); |
| return return_value_and_parameters[1 + i]; |
| } |
| }; |
| |
| |
| TEST(BytecodeGraphBuilderReturnStatements) { |
| HandleAndZoneScope scope; |
| Isolate* isolate = scope.main_isolate(); |
| Zone* zone = scope.main_zone(); |
| Factory* factory = isolate->factory(); |
| |
| ExpectedSnippet<0> snippets[] = { |
| {"return;", {factory->undefined_value()}}, |
| {"return null;", {factory->null_value()}}, |
| {"return true;", {factory->true_value()}}, |
| {"return false;", {factory->false_value()}}, |
| {"return 0;", {factory->NewNumberFromInt(0)}}, |
| {"return +1;", {factory->NewNumberFromInt(1)}}, |
| {"return -1;", {factory->NewNumberFromInt(-1)}}, |
| {"return +127;", {factory->NewNumberFromInt(127)}}, |
| {"return -128;", {factory->NewNumberFromInt(-128)}}, |
| {"return 0.001;", {factory->NewNumber(0.001)}}, |
| {"return 3.7e-60;", {factory->NewNumber(3.7e-60)}}, |
| {"return -3.7e60;", {factory->NewNumber(-3.7e60)}}, |
| {"return '';", {factory->NewStringFromStaticChars("")}}, |
| {"return 'catfood';", {factory->NewStringFromStaticChars("catfood")}} |
| // TODO(oth): {"return NaN;", {factory->NewNumber(NAN)}} |
| }; |
| |
| size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); |
| for (size_t i = 0; i < num_snippets; i++) { |
| ScopedVector<char> script(1024); |
| SNPrintF(script, "function %s() { %s }\n%s();", kFunctionName, |
| snippets[i].code_snippet, kFunctionName); |
| |
| BytecodeGraphTester tester(isolate, zone, script.start()); |
| auto callable = tester.GetCallable<>(); |
| Handle<Object> return_value = callable().ToHandleChecked(); |
| CHECK(return_value->SameValue(*snippets[i].return_value())); |
| } |
| } |
| |
| |
| TEST(BytecodeGraphBuilderPrimitiveExpressions) { |
| HandleAndZoneScope scope; |
| Isolate* isolate = scope.main_isolate(); |
| Zone* zone = scope.main_zone(); |
| Factory* factory = isolate->factory(); |
| |
| ExpectedSnippet<0> snippets[] = { |
| {"return 1 + 1;", {factory->NewNumberFromInt(2)}}, |
| {"return 20 - 30;", {factory->NewNumberFromInt(-10)}}, |
| {"return 4 * 100;", {factory->NewNumberFromInt(400)}}, |
| {"return 100 / 5;", {factory->NewNumberFromInt(20)}}, |
| {"return 25 % 7;", {factory->NewNumberFromInt(4)}}, |
| }; |
| |
| size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); |
| for (size_t i = 0; i < num_snippets; i++) { |
| ScopedVector<char> script(1024); |
| SNPrintF(script, "function %s() { %s }\n%s();", kFunctionName, |
| snippets[i].code_snippet, kFunctionName); |
| |
| BytecodeGraphTester tester(isolate, zone, script.start()); |
| auto callable = tester.GetCallable<>(); |
| Handle<Object> return_value = callable().ToHandleChecked(); |
| CHECK(return_value->SameValue(*snippets[i].return_value())); |
| } |
| } |
| |
| |
| TEST(BytecodeGraphBuilderTwoParameterTests) { |
| HandleAndZoneScope scope; |
| Isolate* isolate = scope.main_isolate(); |
| Zone* zone = scope.main_zone(); |
| Factory* factory = isolate->factory(); |
| |
| ExpectedSnippet<2> snippets[] = { |
| // Integers |
| {"return p1 + p2;", |
| {factory->NewNumberFromInt(-70), factory->NewNumberFromInt(3), |
| factory->NewNumberFromInt(-73)}}, |
| {"return p1 + p2 + 3;", |
| {factory->NewNumberFromInt(1139044), factory->NewNumberFromInt(300), |
| factory->NewNumberFromInt(1138741)}}, |
| {"return p1 - p2;", |
| {factory->NewNumberFromInt(1100), factory->NewNumberFromInt(1000), |
| factory->NewNumberFromInt(-100)}}, |
| {"return p1 * p2;", |
| {factory->NewNumberFromInt(-100000), factory->NewNumberFromInt(1000), |
| factory->NewNumberFromInt(-100)}}, |
| {"return p1 / p2;", |
| {factory->NewNumberFromInt(-10), factory->NewNumberFromInt(1000), |
| factory->NewNumberFromInt(-100)}}, |
| {"return p1 % p2;", |
| {factory->NewNumberFromInt(5), factory->NewNumberFromInt(373), |
| factory->NewNumberFromInt(16)}}, |
| // Doubles |
| {"return p1 + p2;", |
| {factory->NewHeapNumber(9.999), factory->NewHeapNumber(3.333), |
| factory->NewHeapNumber(6.666)}}, |
| {"return p1 - p2;", |
| {factory->NewHeapNumber(-3.333), factory->NewHeapNumber(3.333), |
| factory->NewHeapNumber(6.666)}}, |
| {"return p1 * p2;", |
| {factory->NewHeapNumber(3.333 * 6.666), factory->NewHeapNumber(3.333), |
| factory->NewHeapNumber(6.666)}}, |
| {"return p1 / p2;", |
| {factory->NewHeapNumber(2.25), factory->NewHeapNumber(9), |
| factory->NewHeapNumber(4)}}, |
| // Strings |
| {"return p1 + p2;", |
| {factory->NewStringFromStaticChars("abcdef"), |
| factory->NewStringFromStaticChars("abc"), |
| factory->NewStringFromStaticChars("def")}}}; |
| |
| size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); |
| for (size_t i = 0; i < num_snippets; i++) { |
| ScopedVector<char> script(1024); |
| SNPrintF(script, "function %s(p1, p2) { %s }\n%s(0, 0);", kFunctionName, |
| snippets[i].code_snippet, kFunctionName); |
| |
| BytecodeGraphTester tester(isolate, zone, script.start()); |
| auto callable = tester.GetCallable<Handle<Object>, Handle<Object>>(); |
| Handle<Object> return_value = |
| callable(snippets[i].parameter(0), snippets[i].parameter(1)) |
| .ToHandleChecked(); |
| CHECK(return_value->SameValue(*snippets[i].return_value())); |
| } |
| } |
| |
| |
| TEST(BytecodeGraphBuilderNamedLoad) { |
| HandleAndZoneScope scope; |
| Isolate* isolate = scope.main_isolate(); |
| Zone* zone = scope.main_zone(); |
| Factory* factory = isolate->factory(); |
| |
| ExpectedSnippet<1> snippets[] = { |
| {"return p1.val;", |
| {factory->NewNumberFromInt(10), |
| BytecodeGraphTester::NewObject("({val : 10})")}}, |
| {"return p1[\"name\"];", |
| {factory->NewStringFromStaticChars("abc"), |
| BytecodeGraphTester::NewObject("({name : 'abc'})")}}, |
| {"'use strict'; return p1.val;", |
| {factory->NewNumberFromInt(10), |
| BytecodeGraphTester::NewObject("({val : 10 })")}}, |
| {"'use strict'; return p1[\"val\"];", |
| {factory->NewNumberFromInt(10), |
| BytecodeGraphTester::NewObject("({val : 10, name : 'abc'})")}}, |
| {"var b;\n" REPEAT_127(SPACE, " b = p1.name; ") " return p1.name;\n", |
| {factory->NewStringFromStaticChars("abc"), |
| BytecodeGraphTester::NewObject("({name : 'abc'})")}}, |
| {"'use strict'; var b;\n" |
| REPEAT_127(SPACE, " b = p1.name; ") |
| "return p1.name;\n", |
| {factory->NewStringFromStaticChars("abc"), |
| BytecodeGraphTester::NewObject("({ name : 'abc'})")}}, |
| }; |
| |
| size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); |
| for (size_t i = 0; i < num_snippets; i++) { |
| ScopedVector<char> script(2048); |
| SNPrintF(script, "function %s(p1) { %s };\n%s(0);", kFunctionName, |
| snippets[i].code_snippet, kFunctionName); |
| |
| BytecodeGraphTester tester(isolate, zone, script.start()); |
| auto callable = tester.GetCallable<Handle<Object>>(); |
| Handle<Object> return_value = |
| callable(snippets[i].parameter(0)).ToHandleChecked(); |
| CHECK(return_value->SameValue(*snippets[i].return_value())); |
| } |
| } |
| |
| |
| TEST(BytecodeGraphBuilderKeyedLoad) { |
| HandleAndZoneScope scope; |
| Isolate* isolate = scope.main_isolate(); |
| Zone* zone = scope.main_zone(); |
| Factory* factory = isolate->factory(); |
| |
| ExpectedSnippet<2> snippets[] = { |
| {"return p1[p2];", |
| {factory->NewNumberFromInt(10), |
| BytecodeGraphTester::NewObject("({val : 10})"), |
| factory->NewStringFromStaticChars("val")}}, |
| {"return p1[100];", |
| {factory->NewStringFromStaticChars("abc"), |
| BytecodeGraphTester::NewObject("({100 : 'abc'})"), |
| factory->NewNumberFromInt(0)}}, |
| {"var b = 100; return p1[b];", |
| {factory->NewStringFromStaticChars("abc"), |
| BytecodeGraphTester::NewObject("({100 : 'abc'})"), |
| factory->NewNumberFromInt(0)}}, |
| {"'use strict'; return p1[p2];", |
| {factory->NewNumberFromInt(10), |
| BytecodeGraphTester::NewObject("({val : 10 })"), |
| factory->NewStringFromStaticChars("val")}}, |
| {"'use strict'; return p1[100];", |
| {factory->NewNumberFromInt(10), |
| BytecodeGraphTester::NewObject("({100 : 10})"), |
| factory->NewNumberFromInt(0)}}, |
| {"'use strict'; var b = p2; return p1[b];", |
| {factory->NewStringFromStaticChars("abc"), |
| BytecodeGraphTester::NewObject("({100 : 'abc'})"), |
| factory->NewNumberFromInt(100)}}, |
| {"var b;\n" REPEAT_127(SPACE, " b = p1[p2]; ") " return p1[p2];\n", |
| {factory->NewStringFromStaticChars("abc"), |
| BytecodeGraphTester::NewObject("({100 : 'abc'})"), |
| factory->NewNumberFromInt(100)}}, |
| {"'use strict'; var b;\n" REPEAT_127(SPACE, |
| " b = p1[p2]; ") "return p1[p2];\n", |
| {factory->NewStringFromStaticChars("abc"), |
| BytecodeGraphTester::NewObject("({ 100 : 'abc'})"), |
| factory->NewNumberFromInt(100)}}, |
| }; |
| |
| size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); |
| for (size_t i = 0; i < num_snippets; i++) { |
| ScopedVector<char> script(2048); |
| SNPrintF(script, "function %s(p1, p2) { %s };\n%s(0);", kFunctionName, |
| snippets[i].code_snippet, kFunctionName); |
| |
| BytecodeGraphTester tester(isolate, zone, script.start()); |
| auto callable = tester.GetCallable<Handle<Object>, Handle<Object>>(); |
| Handle<Object> return_value = |
| callable(snippets[i].parameter(0), snippets[i].parameter(1)) |
| .ToHandleChecked(); |
| CHECK(return_value->SameValue(*snippets[i].return_value())); |
| } |
| } |
| |
| |
| TEST(BytecodeGraphBuilderNamedStore) { |
| HandleAndZoneScope scope; |
| Isolate* isolate = scope.main_isolate(); |
| Zone* zone = scope.main_zone(); |
| Factory* factory = isolate->factory(); |
| |
| ExpectedSnippet<1> snippets[] = { |
| {"return p1.val = 20;", |
| {factory->NewNumberFromInt(20), |
| BytecodeGraphTester::NewObject("({val : 10})")}}, |
| {"p1.type = 'int'; return p1.type;", |
| {factory->NewStringFromStaticChars("int"), |
| BytecodeGraphTester::NewObject("({val : 10})")}}, |
| {"p1.name = 'def'; return p1[\"name\"];", |
| {factory->NewStringFromStaticChars("def"), |
| BytecodeGraphTester::NewObject("({name : 'abc'})")}}, |
| {"'use strict'; p1.val = 20; return p1.val;", |
| {factory->NewNumberFromInt(20), |
| BytecodeGraphTester::NewObject("({val : 10 })")}}, |
| {"'use strict'; return p1.type = 'int';", |
| {factory->NewStringFromStaticChars("int"), |
| BytecodeGraphTester::NewObject("({val : 10})")}}, |
| {"'use strict'; p1.val = 20; return p1[\"val\"];", |
| {factory->NewNumberFromInt(20), |
| BytecodeGraphTester::NewObject("({val : 10, name : 'abc'})")}}, |
| {"var b = 'abc';\n" REPEAT_127( |
| SPACE, " p1.name = b; ") " p1.name = 'def'; return p1.name;\n", |
| {factory->NewStringFromStaticChars("def"), |
| BytecodeGraphTester::NewObject("({name : 'abc'})")}}, |
| {"'use strict'; var b = 'def';\n" REPEAT_127( |
| SPACE, " p1.name = 'abc'; ") "p1.name = b; return p1.name;\n", |
| {factory->NewStringFromStaticChars("def"), |
| BytecodeGraphTester::NewObject("({ name : 'abc'})")}}, |
| }; |
| |
| size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); |
| for (size_t i = 0; i < num_snippets; i++) { |
| ScopedVector<char> script(3072); |
| SNPrintF(script, "function %s(p1) { %s };\n%s({});", kFunctionName, |
| snippets[i].code_snippet, kFunctionName); |
| |
| BytecodeGraphTester tester(isolate, zone, script.start()); |
| auto callable = tester.GetCallable<Handle<Object>>(); |
| Handle<Object> return_value = |
| callable(snippets[i].parameter(0)).ToHandleChecked(); |
| CHECK(return_value->SameValue(*snippets[i].return_value())); |
| } |
| } |
| |
| |
| TEST(BytecodeGraphBuilderKeyedStore) { |
| HandleAndZoneScope scope; |
| Isolate* isolate = scope.main_isolate(); |
| Zone* zone = scope.main_zone(); |
| Factory* factory = isolate->factory(); |
| |
| ExpectedSnippet<2> snippets[] = { |
| {"p1[p2] = 20; return p1[p2];", |
| {factory->NewNumberFromInt(20), |
| BytecodeGraphTester::NewObject("({val : 10})"), |
| factory->NewStringFromStaticChars("val")}}, |
| {"return p1[100] = 'def';", |
| {factory->NewStringFromStaticChars("def"), |
| BytecodeGraphTester::NewObject("({100 : 'abc'})"), |
| factory->NewNumberFromInt(0)}}, |
| {"var b = 100; p1[b] = 'def'; return p1[b];", |
| {factory->NewStringFromStaticChars("def"), |
| BytecodeGraphTester::NewObject("({100 : 'abc'})"), |
| factory->NewNumberFromInt(0)}}, |
| {"'use strict'; p1[p2] = 20; return p1[p2];", |
| {factory->NewNumberFromInt(20), |
| BytecodeGraphTester::NewObject("({val : 10 })"), |
| factory->NewStringFromStaticChars("val")}}, |
| {"'use strict'; return p1[100] = 20;", |
| {factory->NewNumberFromInt(20), |
| BytecodeGraphTester::NewObject("({100 : 10})"), |
| factory->NewNumberFromInt(0)}}, |
| {"'use strict'; var b = p2; p1[b] = 'def'; return p1[b];", |
| {factory->NewStringFromStaticChars("def"), |
| BytecodeGraphTester::NewObject("({100 : 'abc'})"), |
| factory->NewNumberFromInt(100)}}, |
| {"var b;\n" REPEAT_127( |
| SPACE, " b = p1[p2]; ") " p1[p2] = 'def'; return p1[p2];\n", |
| {factory->NewStringFromStaticChars("def"), |
| BytecodeGraphTester::NewObject("({100 : 'abc'})"), |
| factory->NewNumberFromInt(100)}}, |
| {"'use strict'; var b;\n" REPEAT_127( |
| SPACE, " b = p1[p2]; ") " p1[p2] = 'def'; return p1[p2];\n", |
| {factory->NewStringFromStaticChars("def"), |
| BytecodeGraphTester::NewObject("({ 100 : 'abc'})"), |
| factory->NewNumberFromInt(100)}}, |
| }; |
| |
| size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); |
| for (size_t i = 0; i < num_snippets; i++) { |
| ScopedVector<char> script(2048); |
| SNPrintF(script, "function %s(p1, p2) { %s };\n%s({});", kFunctionName, |
| snippets[i].code_snippet, kFunctionName); |
| |
| BytecodeGraphTester tester(isolate, zone, script.start()); |
| auto callable = tester.GetCallable<Handle<Object>>(); |
| Handle<Object> return_value = |
| callable(snippets[i].parameter(0)).ToHandleChecked(); |
| CHECK(return_value->SameValue(*snippets[i].return_value())); |
| } |
| } |
| |
| |
| TEST(BytecodeGraphBuilderPropertyCall) { |
| HandleAndZoneScope scope; |
| Isolate* isolate = scope.main_isolate(); |
| Zone* zone = scope.main_zone(); |
| Factory* factory = isolate->factory(); |
| |
| ExpectedSnippet<1> snippets[] = { |
| {"return p1.func();", |
| {factory->NewNumberFromInt(25), |
| BytecodeGraphTester::NewObject("({func() { return 25; }})")}}, |
| {"return p1.func('abc');", |
| {factory->NewStringFromStaticChars("abc"), |
| BytecodeGraphTester::NewObject("({func(a) { return a; }})")}}, |
| {"return p1.func(1, 2, 3, 4, 5, 6, 7, 8);", |
| {factory->NewNumberFromInt(36), |
| BytecodeGraphTester::NewObject( |
| "({func(a, b, c, d, e, f, g, h) {\n" |
| " return a + b + c + d + e + f + g + h;}})")}}, |
| }; |
| |
| size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); |
| for (size_t i = 0; i < num_snippets; i++) { |
| ScopedVector<char> script(2048); |
| SNPrintF(script, "function %s(p1) { %s };\n%s({func() {}});", kFunctionName, |
| snippets[i].code_snippet, kFunctionName); |
| |
| BytecodeGraphTester tester(isolate, zone, script.start()); |
| auto callable = tester.GetCallable<Handle<Object>>(); |
| Handle<Object> return_value = |
| callable(snippets[i].parameter(0)).ToHandleChecked(); |
| CHECK(return_value->SameValue(*snippets[i].return_value())); |
| } |
| } |
| |
| |
| TEST(BytecodeGraphBuilderCallNew) { |
| HandleAndZoneScope scope; |
| Isolate* isolate = scope.main_isolate(); |
| Zone* zone = scope.main_zone(); |
| Factory* factory = isolate->factory(); |
| |
| ExpectedSnippet<0> snippets[] = { |
| {"function counter() { this.count = 20; }\n" |
| "function f() {\n" |
| " var c = new counter();\n" |
| " return c.count;\n" |
| "}; f()", |
| {factory->NewNumberFromInt(20)}}, |
| {"function counter(arg0) { this.count = 17; this.x = arg0; }\n" |
| "function f() {\n" |
| " var c = new counter(6);\n" |
| " return c.count + c.x;\n" |
| "}; f()", |
| {factory->NewNumberFromInt(23)}}, |
| {"function counter(arg0, arg1) {\n" |
| " this.count = 17; this.x = arg0; this.y = arg1;\n" |
| "}\n" |
| "function f() {\n" |
| " var c = new counter(3, 5);\n" |
| " return c.count + c.x + c.y;\n" |
| "}; f()", |
| {factory->NewNumberFromInt(25)}}, |
| }; |
| |
| size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); |
| for (size_t i = 0; i < num_snippets; i++) { |
| BytecodeGraphTester tester(isolate, zone, snippets[i].code_snippet); |
| auto callable = tester.GetCallable<>(); |
| Handle<Object> return_value = callable().ToHandleChecked(); |
| CHECK(return_value->SameValue(*snippets[i].return_value())); |
| } |
| } |
| |
| |
| TEST(BytecodeGraphBuilderCallRuntime) { |
| HandleAndZoneScope scope; |
| Isolate* isolate = scope.main_isolate(); |
| Zone* zone = scope.main_zone(); |
| Factory* factory = isolate->factory(); |
| |
| ExpectedSnippet<1> snippets[] = { |
| {"function f(arg0) { return %MaxSmi(); }\nf()", |
| {factory->NewNumberFromInt(Smi::kMaxValue), factory->undefined_value()}}, |
| {"function f(arg0) { return %IsArray(arg0) }\nf(undefined)", |
| {factory->true_value(), BytecodeGraphTester::NewObject("[1, 2, 3]")}}, |
| {"function f(arg0) { return %Add(arg0, 2) }\nf(1)", |
| {factory->NewNumberFromInt(5), factory->NewNumberFromInt(3)}}, |
| {"function f(arg0) { return %spread_arguments(arg0).length }\nf([])", |
| {factory->NewNumberFromInt(3), |
| BytecodeGraphTester::NewObject("[1, 2, 3]")}}, |
| }; |
| |
| size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); |
| for (size_t i = 0; i < num_snippets; i++) { |
| BytecodeGraphTester tester(isolate, zone, snippets[i].code_snippet); |
| auto callable = tester.GetCallable<Handle<Object>>(); |
| Handle<Object> return_value = |
| callable(snippets[i].parameter(0)).ToHandleChecked(); |
| CHECK(return_value->SameValue(*snippets[i].return_value())); |
| } |
| } |
| |
| |
| TEST(BytecodeGraphBuilderGlobals) { |
| HandleAndZoneScope scope; |
| Isolate* isolate = scope.main_isolate(); |
| Zone* zone = scope.main_zone(); |
| Factory* factory = isolate->factory(); |
| |
| ExpectedSnippet<0> snippets[] = { |
| {"var global = 321;\n function f() { return global; };\n f();", |
| {factory->NewNumberFromInt(321)}}, |
| {"var global = 321;\n" |
| "function f() { global = 123; return global };\n f();", |
| {factory->NewNumberFromInt(123)}}, |
| {"var global = function() { return 'abc'};\n" |
| "function f() { return global(); };\n f();", |
| {factory->NewStringFromStaticChars("abc")}}, |
| {"var global = 456;\n" |
| "function f() { 'use strict'; return global; };\n f();", |
| {factory->NewNumberFromInt(456)}}, |
| {"var global = 987;\n" |
| "function f() { 'use strict'; global = 789; return global };\n f();", |
| {factory->NewNumberFromInt(789)}}, |
| {"var global = function() { return 'xyz'};\n" |
| "function f() { 'use strict'; return global(); };\n f();", |
| {factory->NewStringFromStaticChars("xyz")}}, |
| {"var global = 'abc'; var global_obj = {val:123};\n" |
| "function f() {\n" REPEAT_127( |
| SPACE, " var b = global_obj.name;\n") "return global; };\n f();\n", |
| {factory->NewStringFromStaticChars("abc")}}, |
| {"var global = 'abc'; var global_obj = {val:123};\n" |
| "function f() { 'use strict';\n" REPEAT_127( |
| SPACE, " var b = global_obj.name;\n") "global = 'xyz'; return " |
| "global };\n f();\n", |
| {factory->NewStringFromStaticChars("xyz")}}, |
| // TODO(rmcilroy): Add tests for typeof_mode once we have typeof support. |
| }; |
| |
| size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); |
| for (size_t i = 0; i < num_snippets; i++) { |
| BytecodeGraphTester tester(isolate, zone, snippets[i].code_snippet); |
| auto callable = tester.GetCallable<>(); |
| Handle<Object> return_value = callable().ToHandleChecked(); |
| CHECK(return_value->SameValue(*snippets[i].return_value())); |
| } |
| } |
| |
| |
| TEST(BytecodeGraphBuilderLogicalNot) { |
| HandleAndZoneScope scope; |
| Isolate* isolate = scope.main_isolate(); |
| Zone* zone = scope.main_zone(); |
| Factory* factory = isolate->factory(); |
| |
| ExpectedSnippet<1> snippets[] = { |
| {"return !p1;", |
| {factory->false_value(), |
| BytecodeGraphTester::NewObject("({val : 10})")}}, |
| {"return !p1;", {factory->true_value(), factory->NewNumberFromInt(0)}}, |
| {"return !p1;", {factory->true_value(), factory->undefined_value()}}, |
| {"return !p1;", {factory->false_value(), factory->NewNumberFromInt(10)}}, |
| {"return !p1;", {factory->false_value(), factory->true_value()}}, |
| {"return !p1;", |
| {factory->false_value(), factory->NewStringFromStaticChars("abc")}}, |
| }; |
| |
| size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); |
| for (size_t i = 0; i < num_snippets; i++) { |
| ScopedVector<char> script(1024); |
| SNPrintF(script, "function %s(p1) { %s }\n%s({});", kFunctionName, |
| snippets[i].code_snippet, kFunctionName); |
| |
| BytecodeGraphTester tester(isolate, zone, script.start()); |
| auto callable = tester.GetCallable<Handle<Object>>(); |
| Handle<Object> return_value = |
| callable(snippets[i].parameter(0)).ToHandleChecked(); |
| CHECK(return_value->SameValue(*snippets[i].return_value())); |
| } |
| } |
| |
| |
| TEST(BytecodeGraphBuilderTypeOf) { |
| HandleAndZoneScope scope; |
| Isolate* isolate = scope.main_isolate(); |
| Zone* zone = scope.main_zone(); |
| Factory* factory = isolate->factory(); |
| |
| ExpectedSnippet<1> snippets[] = { |
| {"return typeof p1;", |
| {factory->NewStringFromStaticChars("object"), |
| BytecodeGraphTester::NewObject("({val : 10})")}}, |
| {"return typeof p1;", |
| {factory->NewStringFromStaticChars("undefined"), |
| factory->undefined_value()}}, |
| {"return typeof p1;", |
| {factory->NewStringFromStaticChars("number"), |
| factory->NewNumberFromInt(10)}}, |
| {"return typeof p1;", |
| {factory->NewStringFromStaticChars("boolean"), factory->true_value()}}, |
| {"return typeof p1;", |
| {factory->NewStringFromStaticChars("string"), |
| factory->NewStringFromStaticChars("abc")}}, |
| }; |
| |
| size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); |
| for (size_t i = 0; i < num_snippets; i++) { |
| ScopedVector<char> script(1024); |
| SNPrintF(script, "function %s(p1) { %s }\n%s({});", kFunctionName, |
| snippets[i].code_snippet, kFunctionName); |
| |
| BytecodeGraphTester tester(isolate, zone, script.start()); |
| auto callable = tester.GetCallable<Handle<Object>>(); |
| Handle<Object> return_value = |
| callable(snippets[i].parameter(0)).ToHandleChecked(); |
| CHECK(return_value->SameValue(*snippets[i].return_value())); |
| } |
| } |
| |
| |
| TEST(BytecodeGraphBuilderDelete) { |
| HandleAndZoneScope scope; |
| Isolate* isolate = scope.main_isolate(); |
| Zone* zone = scope.main_zone(); |
| Factory* factory = isolate->factory(); |
| |
| ExpectedSnippet<1> snippets[] = { |
| {"return delete p1.val;", |
| {factory->true_value(), BytecodeGraphTester::NewObject("({val : 10})")}}, |
| {"delete p1.val; return p1.val;", |
| {factory->undefined_value(), |
| BytecodeGraphTester::NewObject("({val : 10})")}}, |
| {"delete p1.name; return p1.val;", |
| {factory->NewNumberFromInt(10), |
| BytecodeGraphTester::NewObject("({val : 10, name:'abc'})")}}, |
| {"'use strict'; return delete p1.val;", |
| {factory->true_value(), BytecodeGraphTester::NewObject("({val : 10})")}}, |
| {"'use strict'; delete p1.val; return p1.val;", |
| {factory->undefined_value(), |
| BytecodeGraphTester::NewObject("({val : 10})")}}, |
| {"'use strict'; delete p1.name; return p1.val;", |
| {factory->NewNumberFromInt(10), |
| BytecodeGraphTester::NewObject("({val : 10, name:'abc'})")}}, |
| // TODO(mythria): Add tests for global and unallocated when we have |
| // support for LdaContextSlot |
| }; |
| |
| size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); |
| for (size_t i = 0; i < num_snippets; i++) { |
| ScopedVector<char> script(1024); |
| SNPrintF(script, "function %s(p1) { %s }\n%s({});", kFunctionName, |
| snippets[i].code_snippet, kFunctionName); |
| |
| BytecodeGraphTester tester(isolate, zone, script.start()); |
| auto callable = tester.GetCallable<Handle<Object>>(); |
| Handle<Object> return_value = |
| callable(snippets[i].parameter(0)).ToHandleChecked(); |
| CHECK(return_value->SameValue(*snippets[i].return_value())); |
| } |
| } |
| |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |