diff options
author | Ali Ijaz Sheikh <ofrobots@google.com> | 2016-01-20 09:45:45 -0800 |
---|---|---|
committer | Ali Ijaz Sheikh <ofrobots@google.com> | 2016-01-21 16:53:58 -0800 |
commit | ef4170ea03a80b21b2d8a65ce432efaa370fe2fa (patch) | |
tree | e382b1b38b729cd8155b56b441c3a563914854a3 /deps/v8/test/cctest/interpreter | |
parent | 5f6dfab832979999d2f806fc1a2f1c11a25b0f35 (diff) | |
download | android-node-v8-ef4170ea03a80b21b2d8a65ce432efaa370fe2fa.tar.gz android-node-v8-ef4170ea03a80b21b2d8a65ce432efaa370fe2fa.tar.bz2 android-node-v8-ef4170ea03a80b21b2d8a65ce432efaa370fe2fa.zip |
deps: upgrade to V8 4.8.271.17
Pick up V8 4.8 branch-head. This branch brings in @@isConcatSpreadable,
@@toPrimitive and ToLength ES6 changes. For full details see:
http://v8project.blogspot.de/2015/11/v8-release-48.html
https://github.com/v8/v8/commit/fa163e2
Ref: https://github.com/nodejs/node/pull/4399
PR-URL: https://github.com/nodejs/node/pull/4785
Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl>
Diffstat (limited to 'deps/v8/test/cctest/interpreter')
-rw-r--r-- | deps/v8/test/cctest/interpreter/test-bytecode-generator.cc | 5001 | ||||
-rw-r--r-- | deps/v8/test/cctest/interpreter/test-interpreter.cc | 1840 |
2 files changed, 6411 insertions, 430 deletions
diff --git a/deps/v8/test/cctest/interpreter/test-bytecode-generator.cc b/deps/v8/test/cctest/interpreter/test-bytecode-generator.cc index 23c6486f4f..c29eb9659d 100644 --- a/deps/v8/test/cctest/interpreter/test-bytecode-generator.cc +++ b/deps/v8/test/cctest/interpreter/test-bytecode-generator.cc @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(rmcilroy): Remove this define after this flag is turned on globally +#define V8_IMMINENT_DEPRECATION_WARNINGS + #include "src/v8.h" #include "src/compiler.h" @@ -9,6 +12,7 @@ #include "src/interpreter/bytecode-generator.h" #include "src/interpreter/interpreter.h" #include "test/cctest/cctest.h" +#include "test/cctest/test-feedback-vector.h" namespace v8 { namespace internal { @@ -24,37 +28,60 @@ class BytecodeGeneratorHelper { BytecodeGeneratorHelper() { i::FLAG_vector_stores = true; i::FLAG_ignition = true; + i::FLAG_ignition_fake_try_catch = true; i::FLAG_ignition_filter = StrDup(kFunctionName); i::FLAG_always_opt = false; + i::FLAG_allow_natives_syntax = true; CcTest::i_isolate()->interpreter()->Initialize(); } - + Isolate* isolate() { return CcTest::i_isolate(); } Factory* factory() { return CcTest::i_isolate()->factory(); } + Handle<BytecodeArray> MakeTopLevelBytecode(const char* source) { + const char* old_ignition_filter = i::FLAG_ignition_filter; + i::FLAG_ignition_filter = "*"; + Local<v8::Script> script = v8_compile(source); + i::FLAG_ignition_filter = old_ignition_filter; + i::Handle<i::JSFunction> js_function = v8::Utils::OpenHandle(*script); + return handle(js_function->shared()->bytecode_array(), CcTest::i_isolate()); + } Handle<BytecodeArray> MakeBytecode(const char* script, const char* function_name) { CompileRun(script); - Local<Function> function = - Local<Function>::Cast(CcTest::global()->Get(v8_str(function_name))); - i::Handle<i::JSFunction> js_function = v8::Utils::OpenHandle(*function); + v8::Local<v8::Context> context = + v8::Isolate::GetCurrent()->GetCurrentContext(); + Local<Function> function = Local<Function>::Cast( + CcTest::global()->Get(context, v8_str(function_name)).ToLocalChecked()); + i::Handle<i::JSFunction> js_function = + i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*function)); return handle(js_function->shared()->bytecode_array(), CcTest::i_isolate()); } - Handle<BytecodeArray> MakeBytecodeForFunctionBody(const char* body) { - ScopedVector<char> program(1024); + ScopedVector<char> program(3072); SNPrintF(program, "function %s() { %s }\n%s();", kFunctionName, body, kFunctionName); return MakeBytecode(program.start(), kFunctionName); } Handle<BytecodeArray> MakeBytecodeForFunction(const char* function) { - ScopedVector<char> program(1024); + ScopedVector<char> program(3072); SNPrintF(program, "%s\n%s();", function, kFunctionName); return MakeBytecode(program.start(), kFunctionName); } + + Handle<BytecodeArray> MakeBytecodeForFunctionNoFilter(const char* function) { + const char* old_ignition_filter = i::FLAG_ignition_filter; + i::FLAG_ignition_filter = "*"; + ScopedVector<char> program(3072); + SNPrintF(program, "%s\n%s();", function, kFunctionName); + Handle<BytecodeArray> return_val = + MakeBytecode(program.start(), kFunctionName); + i::FLAG_ignition_filter = old_ignition_filter; + return return_val; + } }; @@ -62,19 +89,54 @@ class BytecodeGeneratorHelper { #define B(x) static_cast<uint8_t>(Bytecode::k##x) #define U8(x) static_cast<uint8_t>((x) & 0xff) #define R(x) static_cast<uint8_t>(-(x) & 0xff) -#define _ static_cast<uint8_t>(0x5a) +#define A(x, n) R(helper.kLastParamIndex - (n) + 1 + (x)) +#define THIS(n) A(0, n) +#if defined(V8_TARGET_LITTLE_ENDIAN) +#define U16(x) static_cast<uint8_t>((x) & 0xff), \ + static_cast<uint8_t>(((x) >> kBitsPerByte) & 0xff) +#elif defined(V8_TARGET_BIG_ENDIAN) +#define U16(x) static_cast<uint8_t>(((x) >> kBitsPerByte) & 0xff), \ + static_cast<uint8_t>((x) & 0xff) +#else +#error Unknown byte ordering +#endif + +#define COMMA() , +#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__ // Structure for containing expected bytecode snippets. -template<typename T> +template<typename T, int C = 6> struct ExpectedSnippet { const char* code_snippet; int frame_size; int parameter_count; int bytecode_length; - const uint8_t bytecode[512]; + const uint8_t bytecode[2048]; int constant_count; - T constants[4]; + T constants[C]; }; @@ -100,17 +162,21 @@ static void CheckConstant(Handle<Object> expected, Object* actual) { } -template <typename T> -static void CheckBytecodeArrayEqual(struct ExpectedSnippet<T> expected, - Handle<BytecodeArray> actual, - bool has_unknown = false) { - CHECK_EQ(actual->frame_size(), expected.frame_size); - CHECK_EQ(actual->parameter_count(), expected.parameter_count); - CHECK_EQ(actual->length(), expected.bytecode_length); +static void CheckConstant(InstanceType expected, Object* actual) { + CHECK_EQ(expected, HeapObject::cast(actual)->map()->instance_type()); +} + + +template <typename T, int C> +static void CheckBytecodeArrayEqual(const ExpectedSnippet<T, C>& expected, + Handle<BytecodeArray> actual) { + CHECK_EQ(expected.frame_size, actual->frame_size()); + CHECK_EQ(expected.parameter_count, actual->parameter_count()); + CHECK_EQ(expected.bytecode_length, actual->length()); if (expected.constant_count == 0) { - CHECK_EQ(actual->constant_pool(), CcTest::heap()->empty_fixed_array()); + CHECK_EQ(CcTest::heap()->empty_fixed_array(), actual->constant_pool()); } else { - CHECK_EQ(actual->constant_pool()->length(), expected.constant_count); + CHECK_EQ(expected.constant_count, actual->constant_pool()->length()); for (int i = 0; i < expected.constant_count; i++) { CheckConstant(expected.constants[i], actual->constant_pool()->get(i)); } @@ -129,22 +195,33 @@ static void CheckBytecodeArrayEqual(struct ExpectedSnippet<T> expected, << " but got " << Bytecodes::ToString(bytecode); FATAL(stream.str().c_str()); } - for (int j = 0; j < Bytecodes::NumberOfOperands(bytecode); ++j, ++i) { - uint8_t raw_operand = - iterator.GetRawOperand(j, Bytecodes::GetOperandType(bytecode, j)); - if (has_unknown) { - // Check actual bytecode array doesn't have the same byte as the - // one we use to specify an unknown byte. - CHECK_NE(raw_operand, _); - if (expected.bytecode[i] == _) { - continue; - } + for (int j = 0; j < Bytecodes::NumberOfOperands(bytecode); ++j) { + OperandType operand_type = Bytecodes::GetOperandType(bytecode, j); + int operand_index = i; + i += static_cast<int>(Bytecodes::SizeOfOperand(operand_type)); + uint32_t raw_operand = iterator.GetRawOperand(j, operand_type); + uint32_t expected_operand; + switch (Bytecodes::SizeOfOperand(operand_type)) { + case OperandSize::kNone: + UNREACHABLE(); + return; + case OperandSize::kByte: + expected_operand = + static_cast<uint32_t>(expected.bytecode[operand_index]); + break; + case OperandSize::kShort: + expected_operand = + ReadUnalignedUInt16(&expected.bytecode[operand_index]); + break; + default: + UNREACHABLE(); + return; } - if (raw_operand != expected.bytecode[i]) { + if (raw_operand != expected_operand) { std::ostringstream stream; stream << "Check failed: expected operand [" << j << "] for bytecode [" << bytecode_index << "] to be " - << static_cast<unsigned int>(expected.bytecode[i]) << " but got " + << static_cast<unsigned int>(expected_operand) << " but got " << static_cast<unsigned int>(raw_operand); FATAL(stream.str().c_str()); } @@ -188,29 +265,341 @@ TEST(PrimitiveExpressions) { kPointerSize, 1, 6, - { - B(LdaZero), // - B(Star), R(0), // - B(Ldar), R(0), // - B(Return) // - }, - 0 - }, + {B(LdaZero), // + B(Star), R(0), // + B(Ldar), R(0), // + B(Return)}, + 0}, {"var x = 0; return x + 3;", - 2 * kPointerSize, + kPointerSize, + 1, + 8, + {B(LdaZero), // + B(Star), R(0), // + B(LdaSmi8), U8(3), // + B(Add), R(0), // + B(Return)}, + 0}, + {"var x = 0; return x - 3;", + kPointerSize, + 1, + 8, + {B(LdaZero), // + B(Star), R(0), // + B(LdaSmi8), U8(3), // + B(Sub), R(0), // + B(Return)}, + 0}, + {"var x = 4; return x * 3;", + kPointerSize, + 1, + 9, + {B(LdaSmi8), U8(4), // + B(Star), R(0), // + B(LdaSmi8), U8(3), // + B(Mul), R(0), // + B(Return)}, + 0}, + {"var x = 4; return x / 3;", + kPointerSize, + 1, + 9, + {B(LdaSmi8), U8(4), // + B(Star), R(0), // + B(LdaSmi8), U8(3), // + B(Div), R(0), // + B(Return)}, + 0}, + {"var x = 4; return x % 3;", + kPointerSize, + 1, + 9, + {B(LdaSmi8), U8(4), // + B(Star), R(0), // + B(LdaSmi8), U8(3), // + B(Mod), R(0), // + B(Return)}, + 0}, + {"var x = 1; return x | 2;", + kPointerSize, + 1, + 9, + {B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaSmi8), U8(2), // + B(BitwiseOr), R(0), // + B(Return)}, + 0}, + {"var x = 1; return x ^ 2;", + kPointerSize, + 1, + 9, + {B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaSmi8), U8(2), // + B(BitwiseXor), R(0), // + B(Return)}, + 0}, + {"var x = 1; return x & 2;", + kPointerSize, + 1, + 9, + {B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaSmi8), U8(2), // + B(BitwiseAnd), R(0), // + B(Return)}, + 0}, + {"var x = 10; return x << 3;", + kPointerSize, + 1, + 9, + {B(LdaSmi8), U8(10), // + B(Star), R(0), // + B(LdaSmi8), U8(3), // + B(ShiftLeft), R(0), // + B(Return)}, + 0}, + {"var x = 10; return x >> 3;", + kPointerSize, + 1, + 9, + {B(LdaSmi8), U8(10), // + B(Star), R(0), // + B(LdaSmi8), U8(3), // + B(ShiftRight), R(0), // + B(Return)}, + 0}, + {"var x = 10; return x >>> 3;", + kPointerSize, + 1, + 9, + {B(LdaSmi8), U8(10), // + B(Star), R(0), // + B(LdaSmi8), U8(3), // + B(ShiftRightLogical), R(0), // + B(Return)}, + 0}, + {"var x = 0; return (x, 3);", + kPointerSize, + 1, + 6, + {B(LdaZero), // + B(Star), R(0), // + B(LdaSmi8), U8(3), // + B(Return)}, + 0}}; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(LogicalExpressions) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + + ExpectedSnippet<int> snippets[] = { + {"var x = 0; return x || 3;", + 1 * kPointerSize, + 1, + 10, + {B(LdaZero), // + B(Star), R(0), // + B(Ldar), R(0), // + B(JumpIfToBooleanTrue), U8(4), // + B(LdaSmi8), U8(3), // + B(Return)}, + 0}, + {"var x = 0; return (x == 1) || 3;", + 1 * kPointerSize, 1, 12, - { - B(LdaZero), // - B(Star), R(0), // - B(Ldar), R(0), // Easy to spot r1 not really needed here. - B(Star), R(1), // Dead store. - B(LdaSmi8), U8(3), // - B(Add), R(1), // - B(Return) // - }, - 0 - }}; + {B(LdaZero), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(TestEqual), R(0), // + B(JumpIfTrue), U8(4), // + B(LdaSmi8), U8(3), // + B(Return)}, + 0}, + {"var x = 0; return x && 3;", + 1 * kPointerSize, + 1, + 10, + {B(LdaZero), // + B(Star), R(0), // + B(Ldar), R(0), // + B(JumpIfToBooleanFalse), U8(4), // + B(LdaSmi8), U8(3), // + B(Return)}, + 0}, + {"var x = 0; return (x == 0) && 3;", + 1 * kPointerSize, + 1, + 11, + {B(LdaZero), // + B(Star), R(0), // + B(LdaZero), // + B(TestEqual), R(0), // + B(JumpIfFalse), U8(4), // + B(LdaSmi8), U8(3), // + B(Return)}, + 0}, + {"var x = 0; return x || (1, 2, 3);", + 1 * kPointerSize, + 1, + 10, + {B(LdaZero), // + B(Star), R(0), // + B(Ldar), R(0), // + B(JumpIfToBooleanTrue), U8(4), // + B(LdaSmi8), U8(3), // + B(Return)}, + 0}, + {"var a = 2, b = 3, c = 4; return a || (a, b, a, b, c = 5, 3);", + 3 * kPointerSize, + 1, + 23, + {B(LdaSmi8), U8(2), // + B(Star), R(0), // + B(LdaSmi8), U8(3), // + B(Star), R(1), // + B(LdaSmi8), U8(4), // + B(Star), R(2), // + B(Ldar), R(0), // + B(JumpIfToBooleanTrue), U8(8), // + B(LdaSmi8), U8(5), // + B(Star), R(2), // + B(LdaSmi8), U8(3), // + B(Return)}, + 0}, + {"var x = 1; var a = 2, b = 3; return x || (" + REPEAT_32(SPACE, "a = 1, b = 2, ") + "3);", + 3 * kPointerSize, + 1, + 275, + {B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaSmi8), U8(2), // + B(Star), R(1), // + B(LdaSmi8), U8(3), // + B(Star), R(2), // + B(Ldar), R(0), // + B(JumpIfToBooleanTrueConstant), U8(0), // + REPEAT_32(COMMA, // + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(LdaSmi8), U8(2), // + B(Star), R(2)), // + B(LdaSmi8), U8(3), // + B(Return)}, + 1, + {260, 0, 0, 0}}, + {"var x = 0; var a = 2, b = 3; return x && (" + REPEAT_32(SPACE, "a = 1, b = 2, ") + "3);", + 3 * kPointerSize, + 1, + 274, + {B(LdaZero), // + B(Star), R(0), // + B(LdaSmi8), U8(2), // + B(Star), R(1), // + B(LdaSmi8), U8(3), // + B(Star), R(2), // + B(Ldar), R(0), // + B(JumpIfToBooleanFalseConstant), U8(0), // + REPEAT_32(COMMA, // + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(LdaSmi8), U8(2), // + B(Star), R(2)), // + B(LdaSmi8), U8(3), // + B(Return)}, // + 1, + {260, 0, 0, 0}}, + {"var x = 1; var a = 2, b = 3; return (x > 3) || (" + REPEAT_32(SPACE, "a = 1, b = 2, ") + "3);", + 3 * kPointerSize, + 1, + 277, + {B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaSmi8), U8(2), // + B(Star), R(1), // + B(LdaSmi8), U8(3), // + B(Star), R(2), // + B(LdaSmi8), U8(3), // + B(TestGreaterThan), R(0), // + B(JumpIfTrueConstant), U8(0), // + REPEAT_32(COMMA, // + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(LdaSmi8), U8(2), // + B(Star), R(2)), // + B(LdaSmi8), U8(3), // + B(Return)}, + 1, + {260, 0, 0, 0}}, + {"var x = 0; var a = 2, b = 3; return (x < 5) && (" + REPEAT_32(SPACE, "a = 1, b = 2, ") + "3);", + 3 * kPointerSize, + 1, + 276, + {B(LdaZero), // + B(Star), R(0), // + B(LdaSmi8), U8(2), // + B(Star), R(1), // + B(LdaSmi8), U8(3), // + B(Star), R(2), // + B(LdaSmi8), U8(5), // + B(TestLessThan), R(0), // + B(JumpIfFalseConstant), U8(0), // + REPEAT_32(COMMA, // + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(LdaSmi8), U8(2), // + B(Star), R(2)), // + B(LdaSmi8), U8(3), // + B(Return)}, + 1, + {260, 0, 0, 0}}, + {"return 0 && 3;", + 0 * kPointerSize, + 1, + 2, + {B(LdaZero), // + B(Return)}, + 0}, + {"return 1 || 3;", + 0 * kPointerSize, + 1, + 3, + {B(LdaSmi8), U8(1), // + B(Return)}, + 0}, + {"var x = 1; return x && 3 || 0, 1;", + 1 * kPointerSize, + 1, + 16, + {B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(Ldar), R(0), // + B(JumpIfToBooleanFalse), U8(4), // + B(LdaSmi8), U8(3), // + B(JumpIfToBooleanTrue), U8(3), // + B(LdaZero), // + B(LdaSmi8), U8(1), // + B(Return)}, + 0}}; for (size_t i = 0; i < arraysize(snippets); i++) { Handle<BytecodeArray> bytecode_array = @@ -226,15 +615,53 @@ TEST(Parameters) { ExpectedSnippet<int> snippets[] = { {"function f() { return this; }", - 0, 1, 3, {B(Ldar), R(helper.kLastParamIndex), B(Return)}, 0}, + 0, + 1, + 3, + {B(Ldar), THIS(1), B(Return)}, + 0}, {"function f(arg1) { return arg1; }", - 0, 2, 3, {B(Ldar), R(helper.kLastParamIndex), B(Return)}, 0}, + 0, + 2, + 3, + {B(Ldar), A(1, 2), B(Return)}, + 0}, {"function f(arg1) { return this; }", - 0, 2, 3, {B(Ldar), R(helper.kLastParamIndex - 1), B(Return)}, 0}, + 0, + 2, + 3, + {B(Ldar), THIS(2), B(Return)}, + 0}, {"function f(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { return arg4; }", - 0, 8, 3, {B(Ldar), R(helper.kLastParamIndex - 3), B(Return)}, 0}, + 0, + 8, + 3, + {B(Ldar), A(4, 8), B(Return)}, + 0}, {"function f(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { return this; }", - 0, 8, 3, {B(Ldar), R(helper.kLastParamIndex - 7), B(Return)}, 0} + 0, + 8, + 3, + {B(Ldar), THIS(8), B(Return)}, + 0}, + {"function f(arg1) { arg1 = 1; }", + 0, + 2, + 6, + {B(LdaSmi8), U8(1), // + B(Star), A(1, 2), // + B(LdaUndefined), // + B(Return)}, + 0}, + {"function f(arg1, arg2, arg3, arg4) { arg2 = 1; }", + 0, + 5, + 6, + {B(LdaSmi8), U8(1), // + B(Star), A(2, 5), // + B(LdaUndefined), // + B(Return)}, + 0}, }; for (size_t i = 0; i < arraysize(snippets); i++) { @@ -297,7 +724,9 @@ TEST(HeapNumberConstants) { InitializedHandleScope handle_scope; BytecodeGeneratorHelper helper; - ExpectedSnippet<double> snippets[] = { + int wide_idx = 0; + + ExpectedSnippet<double, 257> snippets[] = { {"return 1.2;", 0, 1, @@ -331,7 +760,26 @@ TEST(HeapNumberConstants) { B(Return) // }, 2, - {3.14, 3.14}}}; + {3.14, 3.14}}, + {"var a;" + REPEAT_256(SPACE, " a = 1.414;") + " a = 3.14;", + 1 * kPointerSize, + 1, + 1031, + { + REPEAT_256(COMMA, // + B(LdaConstant), U8(wide_idx++), // + B(Star), R(0)), // + B(LdaConstantWide), U16(wide_idx), // + B(Star), R(0), // + B(LdaUndefined), // + B(Return), // + }, + 257, + {REPEAT_256(COMMA, 1.414), + 3.14}} + }; for (size_t i = 0; i < arraysize(snippets); i++) { Handle<BytecodeArray> bytecode_array = helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); @@ -391,83 +839,171 @@ TEST(StringConstants) { TEST(PropertyLoads) { InitializedHandleScope handle_scope; BytecodeGeneratorHelper helper; + Zone zone; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot1 = feedback_spec.AddLoadICSlot(); + FeedbackVectorSlot slot2 = feedback_spec.AddLoadICSlot(); - FeedbackVectorSlotKind ic_kinds[] = {i::FeedbackVectorSlotKind::LOAD_IC, - i::FeedbackVectorSlotKind::LOAD_IC}; - StaticFeedbackVectorSpec feedback_spec(0, 2, ic_kinds); Handle<i::TypeFeedbackVector> vector = - helper.factory()->NewTypeFeedbackVector(&feedback_spec); + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + // These are a hack used by the LoadICXXXWide tests below. + int wide_idx_1 = vector->GetIndex(slot1) - 2; + int wide_idx_2 = vector->GetIndex(slot1) - 2; + int wide_idx_3 = vector->GetIndex(slot1) - 2; + int wide_idx_4 = vector->GetIndex(slot1) - 2; ExpectedSnippet<const char*> snippets[] = { {"function f(a) { return a.name; }\nf({name : \"test\"})", - 1 * kPointerSize, + 0, 2, - 10, + 5, { - B(Ldar), R(helper.kLastParamIndex), // - B(Star), R(0), // - B(LdaConstant), U8(0), // - B(LoadIC), R(0), U8(vector->first_ic_slot_index()), // - B(Return) // + B(LoadICSloppy), A(1, 2), U8(0), U8(vector->GetIndex(slot1)), // + B(Return), // }, 1, {"name"}}, {"function f(a) { return a[\"key\"]; }\nf({key : \"test\"})", - 1 * kPointerSize, + 0, 2, - 10, + 5, { - B(Ldar), R(helper.kLastParamIndex), // - B(Star), R(0), // - B(LdaConstant), U8(0), // - B(LoadIC), R(0), U8(vector->first_ic_slot_index()), // - B(Return) // + B(LoadICSloppy), A(1, 2), U8(0), U8(vector->GetIndex(slot1)), // + B(Return) // }, 1, {"key"}}, {"function f(a) { return a[100]; }\nf({100 : \"test\"})", - 1 * kPointerSize, + 0, 2, - 10, + 6, { - B(Ldar), R(helper.kLastParamIndex), // - B(Star), R(0), // - B(LdaSmi8), U8(100), // - B(KeyedLoadIC), R(0), U8(vector->first_ic_slot_index()), // - B(Return) // + B(LdaSmi8), U8(100), // + B(KeyedLoadICSloppy), A(1, 2), U8(vector->GetIndex(slot1)), // + B(Return) // }, 0}, {"function f(a, b) { return a[b]; }\nf({arg : \"test\"}, \"arg\")", - 1 * kPointerSize, + 0, 3, - 10, + 6, { - B(Ldar), R(helper.kLastParamIndex - 1), // - B(Star), R(0), // - B(Ldar), R(helper.kLastParamIndex), // - B(KeyedLoadIC), R(0), U8(vector->first_ic_slot_index()), // - B(Return) // + B(Ldar), A(1, 2), // + B(KeyedLoadICSloppy), A(1, 3), U8(vector->GetIndex(slot1)), // + B(Return) // }, 0}, {"function f(a) { var b = a.name; return a[-124]; }\n" "f({\"-124\" : \"test\", name : 123 })", - 2 * kPointerSize, + kPointerSize, 2, - 21, + 12, { - B(Ldar), R(helper.kLastParamIndex), // - B(Star), R(1), // - B(LdaConstant), U8(0), // - B(LoadIC), R(1), U8(vector->first_ic_slot_index()), // - B(Star), R(0), // - B(Ldar), R(helper.kLastParamIndex), // - B(Star), R(1), // - B(LdaSmi8), U8(-124), // - B(KeyedLoadIC), R(1), U8(vector->first_ic_slot_index() + 2), // - B(Return) // + B(LoadICSloppy), A(1, 2), U8(0), U8(vector->GetIndex(slot1)), // + B(Star), R(0), // + B(LdaSmi8), U8(-124), // + B(KeyedLoadICSloppy), A(1, 2), U8(vector->GetIndex(slot2)), // + B(Return), // }, 1, - {"name"}}}; + {"name"}}, + {"function f(a) { \"use strict\"; return a.name; }\nf({name : \"test\"})", + 0, + 2, + 5, + { + B(LoadICStrict), A(1, 2), U8(0), U8(vector->GetIndex(slot1)), // + B(Return), // + }, + 1, + {"name"}}, + { + "function f(a, b) { \"use strict\"; return a[b]; }\n" + "f({arg : \"test\"}, \"arg\")", + 0, + 3, + 6, + { + B(Ldar), A(2, 3), // + B(KeyedLoadICStrict), A(1, 3), U8(vector->GetIndex(slot1)), // + B(Return), // + }, + 0}, + { + "function f(a) {\n" + " var b;\n" + REPEAT_127(SPACE, " b = a.name; ") + " return a.name; }\n" + "f({name : \"test\"})\n", + 1 * kPointerSize, + 2, + 769, + { + REPEAT_127(COMMA, // + B(LoadICSloppy), A(1, 2), U8(0), U8((wide_idx_1 += 2)), // + B(Star), R(0)), // + B(LoadICSloppyWide), A(1, 2), U16(0), U16(wide_idx_1 + 2), // + B(Return), // + }, + 1, + {"name"}}, + { + "function f(a) {\n" + " 'use strict'; var b;\n" + REPEAT_127(SPACE, " b = a.name; ") + " return a.name; }\n" + "f({name : \"test\"})\n", + 1 * kPointerSize, + 2, + 769, + { + REPEAT_127(COMMA, // + B(LoadICStrict), A(1, 2), U8(0), U8((wide_idx_2 += 2)), // + B(Star), R(0)), // + B(LoadICStrictWide), A(1, 2), U16(0), U16(wide_idx_2 + 2), // + B(Return), // + }, + 1, + {"name"}}, + { + "function f(a, b) {\n" + " var c;\n" + REPEAT_127(SPACE, " c = a[b]; ") + " return a[b]; }\n" + "f({name : \"test\"}, \"name\")\n", + 1 * kPointerSize, + 3, + 896, + { + REPEAT_127(COMMA, // + B(Ldar), A(2, 3), // + B(KeyedLoadICSloppy), A(1, 3), U8((wide_idx_3 += 2)), // + B(Star), R(0)), // + B(Ldar), A(2, 3), // + B(KeyedLoadICSloppyWide), A(1, 3), U16(wide_idx_3 + 2), // + B(Return), // + }}, + { + "function f(a, b) {\n" + " 'use strict'; var c;\n" + REPEAT_127(SPACE, " c = a[b]; ") + " return a[b]; }\n" + "f({name : \"test\"}, \"name\")\n", + 1 * kPointerSize, + 3, + 896, + { + REPEAT_127(COMMA, // + B(Ldar), A(2, 3), // + B(KeyedLoadICStrict), A(1, 3), U8((wide_idx_4 += 2)), // + B(Star), R(0)), // + B(Ldar), A(2, 3), // + B(KeyedLoadICStrictWide), A(1, 3), U16(wide_idx_4 + 2), // + B(Return), // + }}, + }; for (size_t i = 0; i < arraysize(snippets); i++) { Handle<BytecodeArray> bytecode_array = helper.MakeBytecode(snippets[i].code_snippet, helper.kFunctionName); @@ -479,98 +1015,189 @@ TEST(PropertyLoads) { TEST(PropertyStores) { InitializedHandleScope handle_scope; BytecodeGeneratorHelper helper; + Zone zone; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot1 = feedback_spec.AddStoreICSlot(); + FeedbackVectorSlot slot2 = feedback_spec.AddStoreICSlot(); - FeedbackVectorSlotKind ic_kinds[] = {i::FeedbackVectorSlotKind::STORE_IC, - i::FeedbackVectorSlotKind::STORE_IC}; - StaticFeedbackVectorSpec feedback_spec(0, 2, ic_kinds); Handle<i::TypeFeedbackVector> vector = - helper.factory()->NewTypeFeedbackVector(&feedback_spec); + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + // These are a hack used by the StoreICXXXWide tests below. + int wide_idx_1 = vector->GetIndex(slot1) - 2; + int wide_idx_2 = vector->GetIndex(slot1) - 2; + int wide_idx_3 = vector->GetIndex(slot1) - 2; + int wide_idx_4 = vector->GetIndex(slot1) - 2; ExpectedSnippet<const char*> snippets[] = { {"function f(a) { a.name = \"val\"; }\nf({name : \"test\"})", - 2 * kPointerSize, + 0, 2, - 16, + 8, { - B(Ldar), R(helper.kLastParamIndex), // - B(Star), R(0), // - B(LdaConstant), U8(0), // - B(Star), R(1), // - B(LdaConstant), U8(1), // - B(StoreIC), R(0), R(1), U8(vector->first_ic_slot_index()), // - B(LdaUndefined), // - B(Return) // + B(LdaConstant), U8(1), // + B(StoreICSloppy), A(1, 2), U8(0), U8(vector->GetIndex(slot1)), // + B(LdaUndefined), // + B(Return), // }, 2, {"name", "val"}}, {"function f(a) { a[\"key\"] = \"val\"; }\nf({key : \"test\"})", - 2 * kPointerSize, + 0, 2, - 16, + 8, { - B(Ldar), R(helper.kLastParamIndex), // - B(Star), R(0), // - B(LdaConstant), U8(0), // - B(Star), R(1), // - B(LdaConstant), U8(1), // - B(StoreIC), R(0), R(1), U8(vector->first_ic_slot_index()), // - B(LdaUndefined), // - B(Return) // + B(LdaConstant), U8(1), // + B(StoreICSloppy), A(1, 2), U8(0), U8(vector->GetIndex(slot1)), // + B(LdaUndefined), // + B(Return), // }, 2, {"key", "val"}}, {"function f(a) { a[100] = \"val\"; }\nf({100 : \"test\"})", - 2 * kPointerSize, + kPointerSize, 2, - 16, + 12, { - B(Ldar), R(helper.kLastParamIndex), // - B(Star), R(0), // - B(LdaSmi8), U8(100), // - B(Star), R(1), // - B(LdaConstant), U8(0), // - B(KeyedStoreIC), R(0), R(1), U8(vector->first_ic_slot_index()), // - B(LdaUndefined), // - B(Return) // + B(LdaSmi8), U8(100), // + B(Star), R(0), // + B(LdaConstant), U8(0), // + B(KeyedStoreICSloppy), A(1, 2), R(0), // + U8(vector->GetIndex(slot1)), // + B(LdaUndefined), // + B(Return), // }, 1, {"val"}}, {"function f(a, b) { a[b] = \"val\"; }\nf({arg : \"test\"}, \"arg\")", - 2 * kPointerSize, + 0, 3, - 16, + 8, { - B(Ldar), R(helper.kLastParamIndex - 1), // - B(Star), R(0), // - B(Ldar), R(helper.kLastParamIndex), // - B(Star), R(1), // - B(LdaConstant), U8(0), // - B(KeyedStoreIC), R(0), R(1), U8(vector->first_ic_slot_index()), // - B(LdaUndefined), // - B(Return) // + B(LdaConstant), U8(0), // + B(KeyedStoreICSloppy), A(1, 3), A(2, 3), // + U8(vector->GetIndex(slot1)), // + B(LdaUndefined), // + B(Return), // }, 1, {"val"}}, {"function f(a) { a.name = a[-124]; }\n" "f({\"-124\" : \"test\", name : 123 })", - 3 * kPointerSize, + 0, 2, - 23, + 11, { - B(Ldar), R(helper.kLastParamIndex), // - B(Star), R(0), // - B(LdaConstant), U8(0), // - B(Star), R(1), // - B(Ldar), R(helper.kLastParamIndex), // - B(Star), R(2), // B(LdaSmi8), U8(-124), // - B(KeyedLoadIC), R(2), U8(vector->first_ic_slot_index()), // - B(StoreIC), R(0), R(1), U8(vector->first_ic_slot_index() + 2), // + B(KeyedLoadICSloppy), A(1, 2), U8(vector->GetIndex(slot1)), // + B(StoreICSloppy), A(1, 2), U8(0), U8(vector->GetIndex(slot2)), // B(LdaUndefined), // - B(Return) // + B(Return), // }, 1, - {"name"}}}; + {"name"}}, + {"function f(a) { \"use strict\"; a.name = \"val\"; }\n" + "f({name : \"test\"})", + 0, + 2, + 8, + { + B(LdaConstant), U8(1), // + B(StoreICStrict), A(1, 2), U8(0), U8(vector->GetIndex(slot1)), // + B(LdaUndefined), // + B(Return), // + }, + 2, + {"name", "val"}}, + {"function f(a, b) { \"use strict\"; a[b] = \"val\"; }\n" + "f({arg : \"test\"}, \"arg\")", + 0, + 3, + 8, + { + B(LdaConstant), U8(0), // + B(KeyedStoreICStrict), A(1, 3), A(2, 3), // + U8(vector->GetIndex(slot1)), // + B(LdaUndefined), // + B(Return), // + }, + 1, + {"val"}}, + {"function f(a) {\n" + REPEAT_127(SPACE, " a.name = 1; ") + " a.name = 2; }\n" + "f({name : \"test\"})\n", + 0, + 2, + 772, + { + REPEAT_127(COMMA, // + B(LdaSmi8), U8(1), // + B(StoreICSloppy), A(1, 2), U8(0), U8((wide_idx_1 += 2))), // + B(LdaSmi8), U8(2), // + B(StoreICSloppyWide), A(1, 2), U16(0), U16(wide_idx_1 + 2), // + B(LdaUndefined), // + B(Return), // + }, + 1, + {"name"}}, + {"function f(a) {\n" + "'use strict';\n" + REPEAT_127(SPACE, " a.name = 1; ") + " a.name = 2; }\n" + "f({name : \"test\"})\n", + 0, + 2, + 772, + { + REPEAT_127(COMMA, // + B(LdaSmi8), U8(1), // + B(StoreICStrict), A(1, 2), U8(0), U8((wide_idx_2 += 2))), // + B(LdaSmi8), U8(2), // + B(StoreICStrictWide), A(1, 2), U16(0), U16(wide_idx_2 + 2), // + B(LdaUndefined), // + B(Return), // + }, + 1, + {"name"}}, + {"function f(a, b) {\n" + REPEAT_127(SPACE, " a[b] = 1; ") + " a[b] = 2; }\n" + "f({name : \"test\"})\n", + 0, + 3, + 771, + { + REPEAT_127(COMMA, // + B(LdaSmi8), U8(1), // + B(KeyedStoreICSloppy), A(1, 3), A(2, 3), // + U8((wide_idx_3 += 2))), // + B(LdaSmi8), U8(2), // + B(KeyedStoreICSloppyWide), A(1, 3), A(2, 3), // + U16(wide_idx_3 + 2), // + B(LdaUndefined), // + B(Return), // + }}, + {"function f(a, b) {\n" + "'use strict';\n" + REPEAT_127(SPACE, " a[b] = 1; ") + " a[b] = 2; }\n" + "f({name : \"test\"})\n", + 0, + 3, + 771, + { + REPEAT_127(COMMA, // + B(LdaSmi8), U8(1), // + B(KeyedStoreICStrict), A(1, 3), A(2, 3), // + U8((wide_idx_4 += 2))), // + B(LdaSmi8), U8(2), // + B(KeyedStoreICStrictWide), A(1, 3), A(2, 3), // + U16(wide_idx_4 + 2), // + B(LdaUndefined), // + B(Return), // + }}}; for (size_t i = 0; i < arraysize(snippets); i++) { Handle<BytecodeArray> bytecode_array = helper.MakeBytecode(snippets[i].code_snippet, helper.kFunctionName); @@ -584,68 +1211,66 @@ TEST(PropertyStores) { TEST(PropertyCall) { InitializedHandleScope handle_scope; - BytecodeGeneratorHelper helper; // + BytecodeGeneratorHelper helper; + Zone zone; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot1 = feedback_spec.AddLoadICSlot(); + FeedbackVectorSlot slot2 = feedback_spec.AddLoadICSlot(); + USE(slot1); - FeedbackVectorSlotKind ic_kinds[] = {i::FeedbackVectorSlotKind::LOAD_IC, - i::FeedbackVectorSlotKind::LOAD_IC}; - StaticFeedbackVectorSpec feedback_spec(0, 2, ic_kinds); Handle<i::TypeFeedbackVector> vector = - helper.factory()->NewTypeFeedbackVector(&feedback_spec); + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); ExpectedSnippet<const char*> snippets[] = { {"function f(a) { return a.func(); }\nf(" FUNC_ARG ")", 2 * kPointerSize, 2, - 16, + 15, { - B(Ldar), R(helper.kLastParamIndex), // - B(Star), R(1), // - B(LdaConstant), U8(0), // - B(LoadIC), R(1), U8(vector->first_ic_slot_index() + 2), // - B(Star), R(0), // - B(Call), R(0), R(1), U8(0), // - B(Return) // + B(Ldar), A(1, 2), // + B(Star), R(1), // + B(LoadICSloppy), R(1), U8(0), U8(vector->GetIndex(slot2)), // + B(Star), R(0), // + B(Call), R(0), R(1), U8(0), // + B(Return), // }, 1, {"func"}}, {"function f(a, b, c) { return a.func(b, c); }\nf(" FUNC_ARG ", 1, 2)", 4 * kPointerSize, 4, - 24, + 23, { - B(Ldar), R(helper.kLastParamIndex - 2), // - B(Star), R(1), // - B(LdaConstant), U8(0), // - B(LoadIC), R(1), U8(vector->first_ic_slot_index() + 2), // - B(Star), R(0), // - B(Ldar), R(helper.kLastParamIndex - 1), // - B(Star), R(2), // - B(Ldar), R(helper.kLastParamIndex), // - B(Star), R(3), // - B(Call), R(0), R(1), U8(2), // - B(Return) // + B(Ldar), A(1, 4), // + B(Star), R(1), // + B(LoadICSloppy), R(1), U8(0), U8(vector->GetIndex(slot2)), // + B(Star), R(0), // + B(Ldar), A(2, 4), // + B(Star), R(2), // + B(Ldar), A(3, 4), // + B(Star), R(3), // + B(Call), R(0), R(1), U8(2), // + B(Return) // }, 1, {"func"}}, {"function f(a, b) { return a.func(b + b, b); }\nf(" FUNC_ARG ", 1)", 4 * kPointerSize, 3, - 30, + 25, { - B(Ldar), R(helper.kLastParamIndex - 1), // - B(Star), R(1), // - B(LdaConstant), U8(0), // - B(LoadIC), R(1), U8(vector->first_ic_slot_index() + 2), // - B(Star), R(0), // - B(Ldar), R(helper.kLastParamIndex), // - B(Star), R(2), // - B(Ldar), R(helper.kLastParamIndex), // - B(Add), R(2), // - B(Star), R(2), // - B(Ldar), R(helper.kLastParamIndex), // - B(Star), R(3), // - B(Call), R(0), R(1), U8(2), // - B(Return) // + B(Ldar), A(1, 3), // + B(Star), R(1), // + B(LoadICSloppy), R(1), U8(0), U8(vector->GetIndex(slot2)), // + B(Star), R(0), // + B(Ldar), A(2, 3), // + B(Add), A(2, 3), // + B(Star), R(2), // + B(Ldar), A(2, 3), // + B(Star), R(3), // + B(Call), R(0), R(1), U8(2), // + B(Return), // }, 1, {"func"}}}; @@ -660,28 +1285,201 @@ TEST(PropertyCall) { TEST(LoadGlobal) { InitializedHandleScope handle_scope; BytecodeGeneratorHelper helper; + Zone zone; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot = feedback_spec.AddLoadICSlot(); + + Handle<i::TypeFeedbackVector> vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + // These are a hack used by the LdaGlobalXXXWide tests below. + int wide_idx_1 = vector->GetIndex(slot) - 2; + int wide_idx_2 = vector->GetIndex(slot) - 2; ExpectedSnippet<const char*> snippets[] = { {"var a = 1;\nfunction f() { return a; }\nf()", - 0, 1, 3, + 0, + 1, + 4, { - B(LdaGlobal), _, - B(Return) + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot)), // + B(Return) // }, - }, + 1, + {"a"}}, {"function t() { }\nfunction f() { return t; }\nf()", - 0, 1, 3, + 0, + 1, + 4, { - B(LdaGlobal), _, - B(Return) + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot)), // + B(Return) // }, - }, + 1, + {"t"}}, + {"'use strict'; var a = 1;\nfunction f() { return a; }\nf()", + 0, + 1, + 4, + { + B(LdaGlobalStrict), U8(0), U8(vector->GetIndex(slot)), // + B(Return) // + }, + 1, + {"a"}}, + {"a = 1;\nfunction f() { return a; }\nf()", + 0, + 1, + 4, + { + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot)), // + B(Return) // + }, + 1, + {"a"}}, + {"a = 1; function f(b) {\n" + REPEAT_127(SPACE, "b.name; ") + " return a; }\nf({name: 1});", + 0, + 2, + 514, + { + REPEAT_127(COMMA, // + B(LoadICSloppy), A(1, 2), U8(0), U8(wide_idx_1 += 2)), // + B(LdaGlobalSloppyWide), U16(1), U16(wide_idx_1 + 2), // + B(Return), // + }, + 2, + {"name", "a"}}, + {"a = 1; function f(b) {\n" + " 'use strict';\n" + REPEAT_127(SPACE, "b.name; ") + " return a; }\nf({name: 1});", + 0, + 2, + 514, + { + REPEAT_127(COMMA, // + B(LoadICStrict), A(1, 2), U8(0), U8(wide_idx_2 += 2)), // + B(LdaGlobalStrictWide), U16(1), U16(wide_idx_2 + 2), // + B(Return), // + }, + 2, + {"name", "a"}}, }; for (size_t i = 0; i < arraysize(snippets); i++) { Handle<BytecodeArray> bytecode_array = helper.MakeBytecode(snippets[i].code_snippet, "f"); - CheckBytecodeArrayEqual(snippets[i], bytecode_array, true); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(StoreGlobal) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot = feedback_spec.AddStoreICSlot(); + + Handle<i::TypeFeedbackVector> vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + // These are a hack used by the StaGlobalXXXWide tests below. + int wide_idx_1 = vector->GetIndex(slot) - 2; + int wide_idx_2 = vector->GetIndex(slot) - 2; + + ExpectedSnippet<const char*> snippets[] = { + {"var a = 1;\nfunction f() { a = 2; }\nf()", + 0, + 1, + 7, + { + B(LdaSmi8), U8(2), // + B(StaGlobalSloppy), U8(0), U8(vector->GetIndex(slot)), // + B(LdaUndefined), // + B(Return) // + }, + 1, + {"a"}}, + {"var a = \"test\"; function f(b) { a = b; }\nf(\"global\")", + 0, + 2, + 7, + { + B(Ldar), R(helper.kLastParamIndex), // + B(StaGlobalSloppy), U8(0), U8(vector->GetIndex(slot)), // + B(LdaUndefined), // + B(Return) // + }, + 1, + {"a"}}, + {"'use strict'; var a = 1;\nfunction f() { a = 2; }\nf()", + 0, + 1, + 7, + { + B(LdaSmi8), U8(2), // + B(StaGlobalStrict), U8(0), U8(vector->GetIndex(slot)), // + B(LdaUndefined), // + B(Return) // + }, + 1, + {"a"}}, + {"a = 1;\nfunction f() { a = 2; }\nf()", + 0, + 1, + 7, + { + B(LdaSmi8), U8(2), // + B(StaGlobalSloppy), U8(0), U8(vector->GetIndex(slot)), // + B(LdaUndefined), // + B(Return) // + }, + 1, + {"a"}}, + {"a = 1; function f(b) {\n" + REPEAT_127(SPACE, "b.name; ") + " a = 2; }\nf({name: 1});", + 0, + 2, + 517, + { + REPEAT_127(COMMA, // + B(LoadICSloppy), A(1, 2), U8(0), U8(wide_idx_1 += 2)), // + B(LdaSmi8), U8(2), // + B(StaGlobalSloppyWide), U16(1), U16(wide_idx_1 + 2), // + B(LdaUndefined), // + B(Return), // + }, + 2, + {"name", "a"}}, + {"a = 1; function f(b) {\n" + " 'use strict';\n" + REPEAT_127(SPACE, "b.name; ") + " a = 2; }\nf({name: 1});", + 0, + 2, + 517, + { + REPEAT_127(COMMA, // + B(LoadICStrict), A(1, 2), U8(0), U8(wide_idx_2 += 2)), // + B(LdaSmi8), U8(2), // + B(StaGlobalStrictWide), U16(1), U16(wide_idx_2 + 2), // + B(LdaUndefined), // + B(Return), // + }, + 2, + {"name", "a"}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecode(snippets[i].code_snippet, "f"); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); } } @@ -689,42 +1487,128 @@ TEST(LoadGlobal) { TEST(CallGlobal) { InitializedHandleScope handle_scope; BytecodeGeneratorHelper helper; + Zone zone; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot1 = feedback_spec.AddLoadICSlot(); + FeedbackVectorSlot slot2 = feedback_spec.AddLoadICSlot(); + USE(slot1); + + Handle<i::TypeFeedbackVector> vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); ExpectedSnippet<const char*> snippets[] = { {"function t() { }\nfunction f() { return t(); }\nf()", - 2 * kPointerSize, 1, 12, + 2 * kPointerSize, + 1, + 13, { - B(LdaUndefined), - B(Star), R(1), - B(LdaGlobal), _, - B(Star), R(0), - B(Call), R(0), R(1), U8(0), - B(Return) + B(LdaUndefined), // + B(Star), R(1), // + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot2)), // + B(Star), R(0), // + B(Call), R(0), R(1), U8(0), // + B(Return) // }, - }, + 1, + {"t"}}, {"function t(a, b, c) { }\nfunction f() { return t(1, 2, 3); }\nf()", - 5 * kPointerSize, 1, 24, - { - B(LdaUndefined), - B(Star), R(1), - B(LdaGlobal), _, - B(Star), R(0), - B(LdaSmi8), U8(1), - B(Star), R(2), - B(LdaSmi8), U8(2), - B(Star), R(3), - B(LdaSmi8), U8(3), - B(Star), R(4), - B(Call), R(0), R(1), U8(3), - B(Return) + 5 * kPointerSize, + 1, + 25, + { + B(LdaUndefined), // + B(Star), R(1), // + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot2)), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(Star), R(2), // + B(LdaSmi8), U8(2), // + B(Star), R(3), // + B(LdaSmi8), U8(3), // + B(Star), R(4), // + B(Call), R(0), R(1), U8(3), // + B(Return) // }, + 1, + {"t"}}, + }; + + size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); + for (size_t i = 0; i < num_snippets; i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecode(snippets[i].code_snippet, "f"); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(CallRuntime) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + ExpectedSnippet<InstanceType> snippets[] = { + { + "function f() { %TheHole() }\nf()", + 0, + 1, + 7, + { + B(CallRuntime), U16(Runtime::kTheHole), R(0), U8(0), // + B(LdaUndefined), // + B(Return) // + }, + }, + { + "function f(a) { return %IsArray(a) }\nf(undefined)", + 1 * kPointerSize, + 2, + 10, + { + B(Ldar), A(1, 2), // + B(Star), R(0), // + B(CallRuntime), U16(Runtime::kIsArray), R(0), U8(1), // + B(Return) // + }, + }, + { + "function f() { return %Add(1, 2) }\nf()", + 2 * kPointerSize, + 1, + 14, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaSmi8), U8(2), // + B(Star), R(1), // + B(CallRuntime), U16(Runtime::kAdd), R(0), U8(2), // + B(Return) // + }, + }, + { + "function f() { return %spread_iterable([1]) }\nf()", + 2 * kPointerSize, + 1, + 16, + { + B(LdaUndefined), // + B(Star), R(0), // + B(LdaConstant), U8(0), // + B(CreateArrayLiteral), U8(0), U8(3), // + B(Star), R(1), // + B(CallJSRuntime), U16(Context::SPREAD_ITERABLE_INDEX), R(0), // + U8(1), // + B(Return), // + }, + 1, + {InstanceType::FIXED_ARRAY_TYPE}, }, }; for (size_t i = 0; i < arraysize(snippets); i++) { Handle<BytecodeArray> bytecode_array = helper.MakeBytecode(snippets[i].code_snippet, "f"); - CheckBytecodeArrayEqual(snippets[i], bytecode_array, true); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); } } @@ -739,121 +1623,159 @@ TEST(IfConditions) { {"function f() { if (0) { return 1; } else { return -1; } } f()", 0, 1, - 14, - {B(LdaZero), // - B(ToBoolean), // - B(JumpIfFalse), U8(7), // - B(LdaSmi8), U8(1), // - B(Return), // - B(Jump), U8(5), // TODO(oth): Unreachable jump after return - B(LdaSmi8), U8(-1), // - B(Return), // - B(LdaUndefined), // - B(Return)}, // - 0, - {unused, unused, unused, unused}}, + 3, + { + B(LdaSmi8), U8(-1), // + B(Return), // + }, + 0, + {unused, unused, unused, unused, unused, unused}}, {"function f() { if ('lucky') { return 1; } else { return -1; } } f();", 0, 1, - 15, - {B(LdaConstant), U8(0), // - B(ToBoolean), // - B(JumpIfFalse), U8(7), // - B(LdaSmi8), U8(1), // - B(Return), // - B(Jump), U8(5), // TODO(oth): Unreachable jump after return - B(LdaSmi8), U8(-1), // - B(Return), // - B(LdaUndefined), // - B(Return)}, // - 1, - {helper.factory()->NewStringFromStaticChars("lucky"), unused, unused, - unused}}, + 3, + { + B(LdaSmi8), U8(1), // + B(Return), // + }, + 0, + {unused, unused, unused, unused, unused, unused}}, {"function f() { if (false) { return 1; } else { return -1; } } f();", 0, 1, - 13, - {B(LdaFalse), // - B(JumpIfFalse), U8(7), // - B(LdaSmi8), U8(1), // - B(Return), // - B(Jump), U8(5), // TODO(oth): Unreachable jump after return - B(LdaSmi8), U8(-1), // - B(Return), // - B(LdaUndefined), // - B(Return)}, // - 0, - {unused, unused, unused, unused}}, + 3, + { + B(LdaSmi8), U8(-1), // + B(Return), // + }, + 0, + {unused, unused, unused, unused, unused, unused}}, + {"function f() { if (false) { return 1; } } f();", + 0, + 1, + 2, + { + B(LdaUndefined), // + B(Return), // + }, + 0, + {unused, unused, unused, unused, unused, unused}}, + {"function f() { var a = 1; if (a) { a += 1; } else { return 2; } } f();", + 1 * kPointerSize, + 1, + 21, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(Ldar), R(0), // + B(JumpIfToBooleanFalse), U8(10), // + B(LdaSmi8), U8(1), // + B(Add), R(0), // + B(Star), R(0), // + B(Jump), U8(5), // + B(LdaSmi8), U8(2), // + B(Return), // + B(LdaUndefined), // + B(Return), // + }, + 0, + {unused, unused, unused, unused, unused, unused}}, {"function f(a) { if (a <= 0) { return 200; } else { return -200; } }" "f(99);", - kPointerSize, + 0, 2, - 19, - {B(Ldar), R(helper.kLastParamIndex), // - B(Star), R(0), // - B(LdaZero), // - B(TestLessThanOrEqual), R(0), // - B(JumpIfFalse), U8(7), // - B(LdaConstant), U8(0), // - B(Return), // - B(Jump), U8(5), // TODO(oth): Unreachable jump after return - B(LdaConstant), U8(1), // - B(Return), // - B(LdaUndefined), // - B(Return)}, // + 13, + { + B(LdaZero), // + B(TestLessThanOrEqual), A(1, 2), // + B(JumpIfFalse), U8(5), // + B(LdaConstant), U8(0), // + B(Return), // + B(LdaConstant), U8(1), // + B(Return), // + B(LdaUndefined), // + B(Return), // + }, 2, {helper.factory()->NewNumberFromInt(200), - helper.factory()->NewNumberFromInt(-200), unused, unused}}, + helper.factory()->NewNumberFromInt(-200), unused, unused, unused, + unused}}, {"function f(a, b) { if (a in b) { return 200; } }" "f('prop', { prop: 'yes'});", - kPointerSize, + 0, 3, - 17, - {B(Ldar), R(helper.kLastParamIndex - 1), // - B(Star), R(0), // - B(Ldar), R(helper.kLastParamIndex), // - B(TestIn), R(0), // - B(JumpIfFalse), U8(7), // - B(LdaConstant), U8(0), // - B(Return), // - B(Jump), U8(2), // TODO(oth): Unreachable jump after return - B(LdaUndefined), // - B(Return)}, // - 1, - {helper.factory()->NewNumberFromInt(200), unused, unused, unused}}, + 11, + { + B(Ldar), A(2, 3), // + B(TestIn), A(1, 3), // + B(JumpIfFalse), U8(5), // + B(LdaConstant), U8(0), // + B(Return), // + B(LdaUndefined), // + B(Return), // + }, + 1, + {helper.factory()->NewNumberFromInt(200), unused, unused, unused, unused, + unused}}, {"function f(z) { var a = 0; var b = 0; if (a === 0.01) { " -#define X "b = a; a = b; " - X X X X X X X X X X X X X X X X X X X X X X X X -#undef X + REPEAT_32(SPACE, "b = a; a = b; ") " return 200; } else { return -200; } } f(0.001)", - 3 * kPointerSize, + 2 * kPointerSize, 2, - 218, - {B(LdaZero), // - B(Star), R(0), // - B(LdaZero), // - B(Star), R(1), // - B(Ldar), R(0), // - B(Star), R(2), // - B(LdaConstant), U8(0), // - B(TestEqualStrict), R(2), // - B(JumpIfFalseConstant), U8(2), // -#define X B(Ldar), R(0), B(Star), R(1), B(Ldar), R(1), B(Star), R(0), - X X X X X X X X X X X X X X X X X X X X X X X X -#undef X - B(LdaConstant), - U8(1), // - B(Return), // - B(Jump), U8(5), // TODO(oth): Unreachable jump after return - B(LdaConstant), U8(3), // - B(Return), // - B(LdaUndefined), // - B(Return)}, // + 276, + { + B(LdaZero), // + B(Star), R(0), // + B(LdaZero), // + B(Star), R(1), // + B(LdaConstant), U8(0), // + B(TestEqualStrict), R(0), // + B(JumpIfFalseConstant), U8(2), // + REPEAT_32(COMMA, // + B(Ldar), R(0), // + B(Star), R(1), // + B(Ldar), R(1), // + B(Star), R(0)), // + B(LdaConstant), U8(1), // + B(Return), // + B(LdaConstant), U8(3), // + B(Return), // + B(LdaUndefined), // + B(Return)}, // 4, {helper.factory()->NewHeapNumber(0.01), helper.factory()->NewNumberFromInt(200), - helper.factory()->NewNumberFromInt(199), - helper.factory()->NewNumberFromInt(-200)}}, + helper.factory()->NewNumberFromInt(261), + helper.factory()->NewNumberFromInt(-200), unused, unused}}, + {"function f() { var a = 0; var b = 0; if (a) { " + REPEAT_32(SPACE, "b = a; a = b; ") + " return 200; } else { return -200; } } f()", + 2 * kPointerSize, + 1, + 274, + { + B(LdaZero), // + B(Star), R(0), // + B(LdaZero), // + B(Star), R(1), // + B(Ldar), R(0), // + B(JumpIfToBooleanFalseConstant), U8(1), // + REPEAT_32(COMMA, // + B(Ldar), R(0), // + B(Star), R(1), // + B(Ldar), R(1), // + B(Star), R(0)), // + B(LdaConstant), U8(0), // + B(Return), // + B(LdaConstant), U8(2), // + B(Return), // + B(LdaUndefined), // + B(Return)}, // + 3, + {helper.factory()->NewNumberFromInt(200), + helper.factory()->NewNumberFromInt(261), + helper.factory()->NewNumberFromInt(-200), unused, unused, unused}}, + {"function f(a, b) {\n" " if (a == b) { return 1; }\n" " if (a === b) { return 1; }\n" @@ -863,23 +1785,18 @@ TEST(IfConditions) { " if (a >= b) { return 1; }\n" " if (a in b) { return 1; }\n" " if (a instanceof b) { return 1; }\n" - " /* if (a != b) { return 1; } */" // TODO(oth) Ast visitor yields - " /* if (a !== b) { return 1; } */" // UNARY NOT, rather than !=/!==. " return 0;\n" "} f(1, 1);", - kPointerSize, + 0, 3, - 122, - { -#define IF_CONDITION_RETURN(condition) \ - B(Ldar), R(helper.kLastParamIndex - 1), \ - B(Star), R(0), \ - B(Ldar), R(helper.kLastParamIndex), \ - B(condition), R(0), \ - B(JumpIfFalse), U8(7), \ - B(LdaSmi8), U8(1), \ - B(Return), \ - B(Jump), U8(2), + 74, + { +#define IF_CONDITION_RETURN(condition) \ + B(Ldar), A(2, 3), \ + B(condition), A(1, 3), \ + B(JumpIfFalse), U8(5), \ + B(LdaSmi8), U8(1), \ + B(Return), IF_CONDITION_RETURN(TestEqual) // IF_CONDITION_RETURN(TestEqualStrict) // IF_CONDITION_RETURN(TestLessThan) // @@ -888,11 +1805,36 @@ TEST(IfConditions) { IF_CONDITION_RETURN(TestGreaterThanOrEqual) // IF_CONDITION_RETURN(TestIn) // IF_CONDITION_RETURN(TestInstanceOf) // + B(LdaZero), // + B(Return)}, // #undef IF_CONDITION_RETURN - B(LdaZero), // - B(Return)}, // 0, - {unused, unused, unused, unused}}, + {unused, unused, unused, unused, unused, unused}}, + {"function f() {" + " var a = 0;" + " if (a) {" + " return 20;" + "} else {" + " return -20;}" + "};" + "f();", + 1 * kPointerSize, + 1, + 15, + { + B(LdaZero), // + B(Star), R(0), // + B(Ldar), R(0), // + B(JumpIfToBooleanFalse), U8(5), // + B(LdaSmi8), U8(20), // + B(Return), // + B(LdaSmi8), U8(-20), // + B(Return), // + B(LdaUndefined), // + B(Return) + }, + 0, + {unused, unused, unused, unused, unused, unused}} }; for (size_t i = 0; i < arraysize(snippets); i++) { @@ -903,6 +1845,3375 @@ TEST(IfConditions) { } +TEST(DeclareGlobals) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + // Create different feedback vector specs to be precise on slot numbering. + FeedbackVectorSpec feedback_spec_stores(&zone); + FeedbackVectorSlot store_slot_1 = feedback_spec_stores.AddStoreICSlot(); + FeedbackVectorSlot store_slot_2 = feedback_spec_stores.AddStoreICSlot(); + USE(store_slot_1); + + Handle<i::TypeFeedbackVector> store_vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec_stores); + + FeedbackVectorSpec feedback_spec_loads(&zone); + FeedbackVectorSlot load_slot_1 = feedback_spec_loads.AddLoadICSlot(); + + Handle<i::TypeFeedbackVector> load_vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec_loads); + + ExpectedSnippet<InstanceType> snippets[] = { + {"var a = 1;", + 4 * kPointerSize, + 1, + 30, + { + B(LdaConstant), U8(0), // + B(Star), R(1), // + B(LdaZero), // + B(Star), R(2), // + B(CallRuntime), U16(Runtime::kDeclareGlobals), R(1), U8(2), // + B(LdaConstant), U8(1), // + B(Star), R(1), // + B(LdaZero), // + B(Star), R(2), // + B(LdaSmi8), U8(1), // + B(Star), R(3), // + B(CallRuntime), U16(Runtime::kInitializeVarGlobal), R(1), U8(3), // + B(LdaUndefined), // + B(Return) // + }, + 2, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"function f() {}", + 2 * kPointerSize, + 1, + 14, + { + B(LdaConstant), U8(0), // + B(Star), R(0), // + B(LdaZero), // + B(Star), R(1), // + B(CallRuntime), U16(Runtime::kDeclareGlobals), R(0), U8(2), // + B(LdaUndefined), // + B(Return) // + }, + 1, + {InstanceType::FIXED_ARRAY_TYPE}}, + {"var a = 1;\na=2;", + 4 * kPointerSize, + 1, + 38, + { + B(LdaConstant), U8(0), // + B(Star), R(1), // + B(LdaZero), // + B(Star), R(2), // + B(CallRuntime), U16(Runtime::kDeclareGlobals), R(1), U8(2), // + B(LdaConstant), U8(1), // + B(Star), R(1), // + B(LdaZero), // + B(Star), R(2), // + B(LdaSmi8), U8(1), // + B(Star), R(3), // + B(CallRuntime), U16(Runtime::kInitializeVarGlobal), R(1), U8(3), // + B(LdaSmi8), U8(2), // + B(StaGlobalSloppy), U8(1), // + U8(store_vector->GetIndex(store_slot_2)), // + B(Star), R(0), // + B(Ldar), R(0), // + B(Return) // + }, + 2, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"function f() {}\nf();", + 3 * kPointerSize, + 1, + 29, + { + B(LdaConstant), U8(0), // + B(Star), R(1), // + B(LdaZero), // + B(Star), R(2), // + B(CallRuntime), U16(Runtime::kDeclareGlobals), R(1), U8(2), // + B(LdaUndefined), // + B(Star), R(2), // + B(LdaGlobalSloppy), U8(1), // + U8(load_vector->GetIndex(load_slot_1)), // + B(Star), R(1), // + B(Call), R(1), R(2), U8(0), // + B(Star), R(0), // + B(Ldar), R(0), // + B(Return) // + }, + 2, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeTopLevelBytecode(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(BasicLoops) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + ExpectedSnippet<int> snippets[] = { + {"var x = 0;" + "var y = 1;" + "while (x < 10) {" + " y = y * 12;" + " x = x + 1;" + "}" + "return y;", + 2 * kPointerSize, + 1, + 30, + { + B(LdaZero), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(Jump), U8(14), // + B(LdaSmi8), U8(12), // + B(Mul), R(1), // + B(Star), R(1), // + B(LdaSmi8), U8(1), // + B(Add), R(0), // + B(Star), R(0), // + B(LdaSmi8), U8(10), // + B(TestLessThan), R(0), // + B(JumpIfTrue), U8(-16), // + B(Ldar), R(1), // + B(Return), // + }, + 0}, + {"var i = 0;" + "while(true) {" + " if (i < 0) continue;" + " if (i == 3) break;" + " if (i == 4) break;" + " if (i == 10) continue;" + " if (i == 5) break;" + " i = i + 1;" + "}" + "return i;", + 1 * kPointerSize, + 1, + 53, + { + B(LdaZero), // + B(Star), R(0), // + B(LdaZero), // + B(TestLessThan), R(0), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(40), // + B(LdaSmi8), U8(3), // + B(TestEqual), R(0), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(34), // + B(LdaSmi8), U8(4), // + B(TestEqual), R(0), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(26), // + B(LdaSmi8), U8(10), // + B(TestEqual), R(0), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(16), // + B(LdaSmi8), U8(5), // + B(TestEqual), R(0), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(10), // + B(LdaSmi8), U8(1), // + B(Add), R(0), // + B(Star), R(0), // + B(Jump), U8(-45), // + B(Ldar), R(0), // + B(Return), // + }, + 0}, + {"var x = 0; var y = 1;" + "do {" + " y = y * 10;" + " if (x == 5) break;" + " if (x == 6) continue;" + " x = x + 1;" + "} while (x < 10);" + "return y;", + 2 * kPointerSize, + 1, + 44, + { + B(LdaZero), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(LdaSmi8), U8(10), // + B(Mul), R(1), // + B(Star), R(1), // + B(LdaSmi8), U8(5), // + B(TestEqual), R(0), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(22), // + B(LdaSmi8), U8(6), // + B(TestEqual), R(0), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(8), // + B(LdaSmi8), U8(1), // + B(Add), R(0), // + B(Star), R(0), // + B(LdaSmi8), U8(10), // + B(TestLessThan), R(0), // + B(JumpIfTrue), U8(-32), // + B(Ldar), R(1), // + B(Return), // + }, + 0}, + {"var x = 0; " + "for(;;) {" + " if (x == 1) break;" + " x = x + 1;" + "}", + 1 * kPointerSize, + 1, + 21, + { + B(LdaZero), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(TestEqual), R(0), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(10), // + B(LdaSmi8), U8(1), // + B(Add), R(0), // + B(Star), R(0), // + B(Jump), U8(-14), // + B(LdaUndefined), // + B(Return), // + }, + 0}, + {"var u = 0;" + "for(var i = 0; i < 100; i = i + 1) {" + " u = u + 1;" + " continue;" + "}", + 2 * kPointerSize, + 1, + 30, + { + B(LdaZero), // + B(Star), R(0), // + B(LdaZero), // + B(Star), R(1), // + B(Jump), U8(16), // + B(LdaSmi8), U8(1), // + B(Add), R(0), // + B(Star), R(0), // + B(Jump), U8(2), // + B(LdaSmi8), U8(1), // + B(Add), R(1), // + B(Star), R(1), // + B(LdaSmi8), U8(100), // + B(TestLessThan), R(1), // + B(JumpIfTrue), U8(-18), // + B(LdaUndefined), // + B(Return), // + }, + 0}, + {"var i = 0;" + "while(true) {" + " while (i < 3) {" + " if (i == 2) break;" + " i = i + 1;" + " }" + " i = i + 1;" + " break;" + "}" + "return i;", + 1 * kPointerSize, + 1, + 38, + { + B(LdaZero), // + B(Star), R(0), // + B(Jump), U8(16), // + B(LdaSmi8), U8(2), // + B(TestEqual), R(0), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(14), // + B(LdaSmi8), U8(1), // + B(Add), R(0), // + B(Star), R(0), // + B(LdaSmi8), U8(3), // + B(TestLessThan), R(0), // + B(JumpIfTrue), U8(-18), // + B(LdaSmi8), U8(1), // + B(Add), R(0), // + B(Star), R(0), // + B(Jump), U8(4), // + B(Jump), U8(-30), // + B(Ldar), R(0), // + B(Return), // + }, + 0}, + {"var x = 10;" + "var y = 1;" + "while (x) {" + " y = y * 12;" + " x = x - 1;" + "}" + "return y;", + 2 * kPointerSize, + 1, + 29, + { + B(LdaSmi8), U8(10), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(Jump), U8(14), // + B(LdaSmi8), U8(12), // + B(Mul), R(1), // + B(Star), R(1), // + B(LdaSmi8), U8(1), // + B(Sub), R(0), // + B(Star), R(0), // + B(Ldar), R(0), // + B(JumpIfToBooleanTrue), U8(-14), // + B(Ldar), R(1), // + B(Return), // + }, + 0}, + {"var x = 10;" + "var y = 1;" + "do {" + " y = y * 12;" + " x = x - 1;" + "} while(x);" + "return y;", + 2 * kPointerSize, + 1, + 27, + { + B(LdaSmi8), U8(10), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(LdaSmi8), U8(12), // + B(Mul), R(1), // + B(Star), R(1), // + B(LdaSmi8), U8(1), // + B(Sub), R(0), // + B(Star), R(0), // + B(Ldar), R(0), // + B(JumpIfToBooleanTrue), U8(-14), // + B(Ldar), R(1), // + B(Return), // + }, + 0}, + {"var y = 1;" + "for (var x = 10; x; --x) {" + " y = y * 12;" + "}" + "return y;", + 2 * kPointerSize, + 1, + 29, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaSmi8), U8(10), // + B(Star), R(1), // + B(Jump), U8(14), // + B(LdaSmi8), U8(12), // + B(Mul), R(0), // + B(Star), R(0), // + B(Ldar), R(1), // + B(ToNumber), // + B(Dec), // + B(Star), R(1), // + B(Ldar), R(1), // + B(JumpIfToBooleanTrue), U8(-14), // + B(Ldar), R(0), // + B(Return), // + }, + 0}, + {"var x = 0; var y = 1;" + "do {" + " y = y * 10;" + " if (x == 5) break;" + " x = x + 1;" + " if (x == 6) continue;" + "} while (false);" + "return y;", + 2 * kPointerSize, + 1, + 38, + { + B(LdaZero), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(LdaSmi8), U8(10), // + B(Mul), R(1), // + B(Star), R(1), // + B(LdaSmi8), U8(5), // + B(TestEqual), R(0), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(16), // + B(LdaSmi8), U8(1), // + B(Add), R(0), // + B(Star), R(0), // + B(LdaSmi8), U8(6), // + B(TestEqual), R(0), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(2), // + B(Ldar), R(1), // + B(Return), // + }, + 0}, + {"var x = 0; var y = 1;" + "do {" + " y = y * 10;" + " if (x == 5) break;" + " x = x + 1;" + " if (x == 6) continue;" + "} while (true);" + "return y;", + 2 * kPointerSize, + 1, + 40, + { + B(LdaZero), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(LdaSmi8), U8(10), // + B(Mul), R(1), // + B(Star), R(1), // + B(LdaSmi8), U8(5), // + B(TestEqual), R(0), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(18), // + B(LdaSmi8), U8(1), // + B(Add), R(0), // + B(Star), R(0), // + B(LdaSmi8), U8(6), // + B(TestEqual), R(0), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(2), // + B(Jump), U8(-28), // + B(Ldar), R(1), // + B(Return), // + }, + 0}, + {"var x = 0;" + "while(false) {" + " x = x + 1;" + "};" + "return x;", + 1 * kPointerSize, + 1, + 6, + { + B(LdaZero), // + B(Star), R(0), // + B(Ldar), R(0), // + B(Return), // + }, + 0}, + {"var x = 0;" + "for( var i = 0; false; i++) {" + " x = x + 1;" + "};" + "return x;", + 2 * kPointerSize, + 1, + 9, + { + B(LdaZero), // + B(Star), R(0), // + B(LdaZero), // + B(Star), R(1), // + B(Ldar), R(0), // + B(Return), // + }, + 0}, + {"var x = 0;" + "for( var i = 0; true; ++i) {" + " x = x + 1;" + " if (x == 20) break;" + "};" + "return x;", + 2 * kPointerSize, + 1, + 31, + { + B(LdaZero), // + B(Star), R(0), // + B(LdaZero), // + B(Star), R(1), // + B(LdaSmi8), U8(1), // + B(Add), R(0), // + B(Star), R(0), // + B(LdaSmi8), U8(20), // + B(TestEqual), R(0), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(10), // + B(Ldar), R(1), // + B(ToNumber), // + B(Inc), // + B(Star), R(1), // + B(Jump), U8(-20), // + B(Ldar), R(0), // + B(Return), // + }, + 0}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(UnaryOperators) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + ExpectedSnippet<int> snippets[] = { + {"var x = 0;" + "while (x != 10) {" + " x = x + 10;" + "}" + "return x;", + kPointerSize, + 1, + 21, + { + B(LdaZero), // + B(Star), R(0), // + B(Jump), U8(8), // + B(LdaSmi8), U8(10), // + B(Add), R(0), // + B(Star), R(0), // + B(LdaSmi8), U8(10), // + B(TestEqual), R(0), // + B(LogicalNot), // + B(JumpIfTrue), U8(-11), // + B(Ldar), R(0), // + B(Return), // + }, + 0}, + {"var x = false;" + "do {" + " x = !x;" + "} while(x == false);" + "return x;", + kPointerSize, + 1, + 16, + { + B(LdaFalse), // + B(Star), R(0), // + B(Ldar), R(0), // + B(LogicalNot), // + B(Star), R(0), // + B(LdaFalse), // + B(TestEqual), R(0), // + B(JumpIfTrue), U8(-8), // + B(Ldar), R(0), // + B(Return), // + }, + 0}, + {"var x = 101;" + "return void(x * 3);", + kPointerSize, + 1, + 10, + { + B(LdaSmi8), U8(101), // + B(Star), R(0), // + B(LdaSmi8), U8(3), // + B(Mul), R(0), // + B(LdaUndefined), // + B(Return), // + }, + 0}, + {"var x = 1234;" + "var y = void (x * x - 1);" + "return y;", + 3 * kPointerSize, + 1, + 20, + { + B(LdaConstant), U8(0), // + B(Star), R(0), // + B(Ldar), R(0), // + B(Mul), R(0), // + B(Star), R(2), // + B(LdaSmi8), U8(1), // + B(Sub), R(2), // + B(LdaUndefined), // + B(Star), R(1), // + B(Ldar), R(1), // + B(Return), // + }, + 1, + {1234}}, + {"var x = 13;" + "return ~x;", + 1 * kPointerSize, + 1, + 9, + { + B(LdaSmi8), U8(13), // + B(Star), R(0), // + B(LdaSmi8), U8(-1), // + B(BitwiseXor), R(0), // + B(Return), // + }, + 0}, + {"var x = 13;" + "return +x;", + 1 * kPointerSize, + 1, + 9, + { + B(LdaSmi8), U8(13), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(Mul), R(0), // + B(Return), // + }, + 0}, + {"var x = 13;" + "return -x;", + 1 * kPointerSize, + 1, + 9, + { + B(LdaSmi8), U8(13), // + B(Star), R(0), // + B(LdaSmi8), U8(-1), // + B(Mul), R(0), // + B(Return), // + }, + 0}}; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(Typeof) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot = feedback_spec.AddLoadICSlot(); + + Handle<i::TypeFeedbackVector> vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + ExpectedSnippet<const char*> snippets[] = { + {"function f() {\n" + " var x = 13;\n" + " return typeof(x);\n" + "}; f();", + kPointerSize, + 1, + 8, + { + B(LdaSmi8), U8(13), // + B(Star), R(0), // TODO(oth): Ldar R(X) following Star R(X) + B(Ldar), R(0), // could be culled in bytecode array builder. + B(TypeOf), // + B(Return), // + }}, + {"var x = 13;\n" + "function f() {\n" + " return typeof(x);\n" + "}; f();", + 0, + 1, + 5, + { + B(LdaGlobalInsideTypeofSloppy), U8(0), // + U8(vector->GetIndex(slot)), // + B(TypeOf), // + B(Return), // + }, + 1, + {"x"}}, + {"var x = 13;\n" + "function f() {\n" + " 'use strict';\n" + " return typeof(x);\n" + "}; f();", + 0, + 1, + 5, + { + B(LdaGlobalInsideTypeofStrict), U8(0), // + U8(vector->GetIndex(slot)), // + B(TypeOf), // + B(Return), // + }, + 1, + {"x"}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunction(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(Delete) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + int deep_elements_flags = + ObjectLiteral::kFastElements | ObjectLiteral::kDisableMementos; + int closure = Register::function_closure().index(); + int first_context_slot = Context::MIN_CONTEXT_SLOTS; + + ExpectedSnippet<InstanceType> snippets[] = { + {"var a = {x:13, y:14}; return delete a.x;", + 1 * kPointerSize, + 1, + 12, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(DeletePropertySloppy), R(0), // + B(Return) + }, + 2, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"'use strict'; var a = {x:13, y:14}; return delete a.x;", + 1 * kPointerSize, + 1, + 12, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(DeletePropertyStrict), R(0), // + B(Return) + }, + 2, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"var a = {1:13, 2:14}; return delete a[2];", + 1 * kPointerSize, + 1, + 12, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), // + B(Star), R(0), // + B(LdaSmi8), U8(2), // + B(DeletePropertySloppy), R(0), // + B(Return) + }, + 1, + {InstanceType::FIXED_ARRAY_TYPE}}, + {"var a = 10; return delete a;", + 1 * kPointerSize, + 1, + 6, + { + B(LdaSmi8), U8(10), // + B(Star), R(0), // + B(LdaFalse), // + B(Return) + }, + 0}, + {"'use strict';" + "var a = {1:10};" + "(function f1() {return a;});" + "return delete a[1];", + 2 * kPointerSize, + 1, + 29, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), // + R(closure), U8(1), // + B(PushContext), R(0), // + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), // + B(StaContextSlot), R(0), U8(first_context_slot), // + B(LdaConstant), U8(1), // + B(CreateClosure), U8(0), // + B(LdaContextSlot), R(0), U8(first_context_slot), // + B(Star), R(1), // + B(LdaSmi8), U8(1), // + B(DeletePropertyStrict), R(1), // + B(Return) + }, + 2, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"return delete 'test';", + 0 * kPointerSize, + 1, + 2, + { + B(LdaTrue), // + B(Return) + }, + 0}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(GlobalDelete) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + int context = Register::function_context().index(); + int global_object_index = Context::GLOBAL_OBJECT_INDEX; + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot = feedback_spec.AddLoadICSlot(); + + Handle<i::TypeFeedbackVector> vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + ExpectedSnippet<InstanceType> snippets[] = { + {"var a = {x:13, y:14};\n function f() { return delete a.x; };\n f();", + 1 * kPointerSize, + 1, + 10, + { + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot)), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(DeletePropertySloppy), R(0), // + B(Return) + }, + 2, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"a = {1:13, 2:14};\n" + "function f() {'use strict'; return delete a[1];};\n f();", + 1 * kPointerSize, + 1, + 10, + { + B(LdaGlobalStrict), U8(0), U8(vector->GetIndex(slot)), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(DeletePropertyStrict), R(0), // + B(Return) + }, + 1, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"var a = {x:13, y:14};\n function f() { return delete a; };\n f();", + 1 * kPointerSize, + 1, + 10, + { + B(LdaContextSlot), R(context), U8(global_object_index), // + B(Star), R(0), // + B(LdaConstant), U8(0), // + B(DeletePropertySloppy), R(0), // + B(Return) + }, + 1, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"b = 30;\n function f() { return delete b; };\n f();", + 1 * kPointerSize, + 1, + 10, + { + B(LdaContextSlot), R(context), U8(global_object_index), // + B(Star), R(0), // + B(LdaConstant), U8(0), // + B(DeletePropertySloppy), R(0), // + B(Return) + }, + 1, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}}; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecode(snippets[i].code_snippet, "f"); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(FunctionLiterals) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + ExpectedSnippet<InstanceType> snippets[] = { + {"return function(){ }", + 0, + 1, + 5, + { + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Return) // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"return (function(){ })()", + 2 * kPointerSize, + 1, + 14, + { + B(LdaUndefined), // + B(Star), R(1), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Star), R(0), // + B(Call), R(0), R(1), U8(0), // + B(Return) // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"return (function(x){ return x; })(1)", + 3 * kPointerSize, + 1, + 18, + { + B(LdaUndefined), // + B(Star), R(1), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(Star), R(2), // + B(Call), R(0), R(1), U8(1), // + B(Return) // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(RegExpLiterals) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + FeedbackVectorSpec feedback_spec(&zone); + feedback_spec.AddLoadICSlot(); + FeedbackVectorSlot slot2 = feedback_spec.AddLoadICSlot(); + + Handle<i::TypeFeedbackVector> vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + ExpectedSnippet<const char*> snippets[] = { + {"return /ab+d/;", + 1 * kPointerSize, + 1, + 10, + { + B(LdaConstant), U8(0), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(CreateRegExpLiteral), U8(0), R(0), // + B(Return), // + }, + 2, + {"", "ab+d"}}, + {"return /(\\w+)\\s(\\w+)/i;", + 1 * kPointerSize, + 1, + 10, + { + B(LdaConstant), U8(0), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(CreateRegExpLiteral), U8(0), R(0), // + B(Return), // + }, + 2, + {"i", "(\\w+)\\s(\\w+)"}}, + {"return /ab+d/.exec('abdd');", + 3 * kPointerSize, + 1, + 26, + { + B(LdaConstant), U8(0), // + B(Star), R(2), // + B(LdaConstant), U8(1), // + B(CreateRegExpLiteral), U8(0), R(2), // + B(Star), R(1), // + B(LoadICSloppy), R(1), U8(2), U8(vector->GetIndex(slot2)), // + B(Star), R(0), // + B(LdaConstant), U8(3), // + B(Star), R(2), // + B(Call), R(0), R(1), U8(1), // + B(Return), // + }, + 4, + {"", "ab+d", "exec", "abdd"}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(ArrayLiterals) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot1 = feedback_spec.AddKeyedStoreICSlot(); + FeedbackVectorSlot slot2 = feedback_spec.AddKeyedStoreICSlot(); + FeedbackVectorSlot slot3 = feedback_spec.AddKeyedStoreICSlot(); + + Handle<i::TypeFeedbackVector> vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + int simple_flags = + ArrayLiteral::kDisableMementos | ArrayLiteral::kShallowElements; + int deep_elements_flags = ArrayLiteral::kDisableMementos; + ExpectedSnippet<InstanceType> snippets[] = { + {"return [ 1, 2 ];", + 0, + 1, + 6, + { + B(LdaConstant), U8(0), // + B(CreateArrayLiteral), U8(0), U8(simple_flags), // + B(Return) // + }, + 1, + {InstanceType::FIXED_ARRAY_TYPE}}, + {"var a = 1; return [ a, a + 1 ];", + 3 * kPointerSize, + 1, + 35, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaConstant), U8(0), // + B(CreateArrayLiteral), U8(0), U8(3), // + B(Star), R(2), // + B(LdaZero), // + B(Star), R(1), // + B(Ldar), R(0), // + B(KeyedStoreICSloppy), R(2), R(1), U8(vector->GetIndex(slot1)), // + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(LdaSmi8), U8(1), // + B(Add), R(0), // + B(KeyedStoreICSloppy), R(2), R(1), U8(vector->GetIndex(slot1)), // + B(Ldar), R(2), // + B(Return), // + }, + 1, + {InstanceType::FIXED_ARRAY_TYPE}}, + {"return [ [ 1, 2 ], [ 3 ] ];", + 0, + 1, + 6, + { + B(LdaConstant), U8(0), // + B(CreateArrayLiteral), U8(2), U8(deep_elements_flags), // + B(Return) // + }, + 1, + {InstanceType::FIXED_ARRAY_TYPE}}, + {"var a = 1; return [ [ a, 2 ], [ a + 2 ] ];", + 5 * kPointerSize, + 1, + 67, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaConstant), U8(0), // + B(CreateArrayLiteral), U8(2), U8(deep_elements_flags), // + B(Star), R(2), // + B(LdaZero), // + B(Star), R(1), // + B(LdaConstant), U8(1), // + B(CreateArrayLiteral), U8(0), U8(simple_flags), // + B(Star), R(4), // + B(LdaZero), // + B(Star), R(3), // + B(Ldar), R(0), // + B(KeyedStoreICSloppy), R(4), R(3), U8(vector->GetIndex(slot1)), // + B(Ldar), R(4), // + B(KeyedStoreICSloppy), R(2), R(1), U8(vector->GetIndex(slot3)), // + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(LdaConstant), U8(2), // + B(CreateArrayLiteral), U8(1), U8(simple_flags), // + B(Star), R(4), // + B(LdaZero), // + B(Star), R(3), // + B(LdaSmi8), U8(2), // + B(Add), R(0), // + B(KeyedStoreICSloppy), R(4), R(3), U8(vector->GetIndex(slot2)), // + B(Ldar), R(4), // + B(KeyedStoreICSloppy), R(2), R(1), U8(vector->GetIndex(slot3)), // + B(Ldar), R(2), // + B(Return), // + }, + 3, + {InstanceType::FIXED_ARRAY_TYPE, InstanceType::FIXED_ARRAY_TYPE, + InstanceType::FIXED_ARRAY_TYPE}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(ObjectLiterals) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot1 = feedback_spec.AddStoreICSlot(); + + Handle<i::TypeFeedbackVector> vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + int simple_flags = ObjectLiteral::kFastElements | + ObjectLiteral::kShallowProperties | + ObjectLiteral::kDisableMementos; + int deep_elements_flags = + ObjectLiteral::kFastElements | ObjectLiteral::kDisableMementos; + ExpectedSnippet<InstanceType> snippets[] = { + {"return { };", + 0, + 1, + 6, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(simple_flags), // + B(Return) // + }, + 1, + {InstanceType::FIXED_ARRAY_TYPE}}, + {"return { name: 'string', val: 9.2 };", + 0, + 1, + 6, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), // + B(Return) // + }, + 1, + {InstanceType::FIXED_ARRAY_TYPE}}, + {"var a = 1; return { name: 'string', val: a };", + 2 * kPointerSize, + 1, + 20, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), // + B(Star), R(1), // + B(Ldar), R(0), // + B(StoreICSloppy), R(1), U8(1), U8(vector->GetIndex(slot1)), // + B(Ldar), R(1), // + B(Return), // + }, + 2, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"var a = 1; return { val: a, val: a + 1 };", + 2 * kPointerSize, + 1, + 22, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), // + B(Star), R(1), // + B(LdaSmi8), U8(1), // + B(Add), R(0), // + B(StoreICSloppy), R(1), U8(1), U8(vector->GetIndex(slot1)), // + B(Ldar), R(1), // + B(Return), // + }, + 2, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"return { func: function() { } };", + 1 * kPointerSize, + 1, + 18, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), // + B(Star), R(0), // + B(LdaConstant), U8(2), // + B(CreateClosure), U8(0), // + B(StoreICSloppy), R(0), U8(1), U8(vector->GetIndex(slot1)), // + B(Ldar), R(0), // + B(Return), // + }, + 3, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"return { func(a) { return a; } };", + 1 * kPointerSize, + 1, + 18, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), // + B(Star), R(0), // + B(LdaConstant), U8(2), // + B(CreateClosure), U8(0), // + B(StoreICSloppy), R(0), U8(1), U8(vector->GetIndex(slot1)), // + B(Ldar), R(0), // + B(Return), // + }, + 3, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"return { get a() { return 2; } };", + 5 * kPointerSize, + 1, + 31, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(Star), R(1), // + B(LdaConstant), U8(2), // + B(CreateClosure), U8(0), // + B(Star), R(2), // + B(LdaNull), // + B(Star), R(3), // + B(LdaZero), // + B(Star), R(4), // + B(CallRuntime), U16(Runtime::kDefineAccessorPropertyUnchecked), // + R(0), U8(5), // + B(Ldar), R(0), // + B(Return), // + }, + 3, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"return { get a() { return this.x; }, set a(val) { this.x = val } };", + 5 * kPointerSize, + 1, + 34, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(Star), R(1), // + B(LdaConstant), U8(2), // + B(CreateClosure), U8(0), // + B(Star), R(2), // + B(LdaConstant), U8(3), // + B(CreateClosure), U8(0), // + B(Star), R(3), // + B(LdaZero), // + B(Star), R(4), // + B(CallRuntime), U16(Runtime::kDefineAccessorPropertyUnchecked), // + R(0), U8(5), // + B(Ldar), R(0), // + B(Return), // + }, + 4, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::SHARED_FUNCTION_INFO_TYPE, + InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"return { set b(val) { this.y = val } };", + 5 * kPointerSize, + 1, + 31, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(Star), R(1), // + B(LdaNull), // + B(Star), R(2), // + B(LdaConstant), U8(2), // + B(CreateClosure), U8(0), // + B(Star), R(3), // + B(LdaZero), // + B(Star), R(4), // + B(CallRuntime), U16(Runtime::kDefineAccessorPropertyUnchecked), // + R(0), U8(5), // + B(Ldar), R(0), // + B(Return), // + }, + 3, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"var a = 1; return { 1: a };", + 5 * kPointerSize, + 1, + 30, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), // + B(Star), R(1), // + B(LdaSmi8), U8(1), // + B(Star), R(2), // + B(Ldar), R(0), // + B(Star), R(3), // + B(LdaZero), // + B(Star), R(4), // + B(CallRuntime), U16(Runtime::kSetProperty), R(1), U8(4), // + B(Ldar), R(1), // + B(Return), // + }, + 1, + {InstanceType::FIXED_ARRAY_TYPE}}, + {"return { __proto__: null }", + 2 * kPointerSize, + 1, + 18, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(simple_flags), // + B(Star), R(0), // + B(LdaNull), B(Star), R(1), // + B(CallRuntime), U16(Runtime::kInternalSetPrototype), R(0), U8(2), // + B(Ldar), R(0), // + B(Return), // + }, + 1, + {InstanceType::FIXED_ARRAY_TYPE}}, + {"var a = 'test'; return { [a]: 1 }", + 5 * kPointerSize, + 1, + 31, + { + B(LdaConstant), U8(0), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(CreateObjectLiteral), U8(0), U8(simple_flags), // + B(Star), R(1), // + B(Ldar), R(0), // + B(ToName), // + B(Star), R(2), // + B(LdaSmi8), U8(1), // + B(Star), R(3), // + B(LdaZero), // + B(Star), R(4), // + B(CallRuntime), U16(Runtime::kDefineDataPropertyUnchecked), R(1), // + U8(4), // + B(Ldar), R(1), // + B(Return), // + }, + 2, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::FIXED_ARRAY_TYPE}}, + {"var a = 'test'; return { val: a, [a]: 1 }", + 5 * kPointerSize, + 1, + 37, + { + B(LdaConstant), U8(0), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), // + B(Star), R(1), // + B(Ldar), R(0), // + B(StoreICSloppy), R(1), U8(2), U8(vector->GetIndex(slot1)), // + B(Ldar), R(0), // + B(ToName), // + B(Star), R(2), // + B(LdaSmi8), U8(1), // + B(Star), R(3), // + B(LdaZero), // + B(Star), R(4), // + B(CallRuntime), U16(Runtime::kDefineDataPropertyUnchecked), R(1), // + U8(4), // + B(Ldar), R(1), // + B(Return), // + }, + 3, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"var a = 'test'; return { [a]: 1, __proto__: {} }", + 5 * kPointerSize, + 1, + 43, + { + B(LdaConstant), U8(0), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(CreateObjectLiteral), U8(1), U8(simple_flags), // + B(Star), R(1), // + B(Ldar), R(0), // + B(ToName), // + B(Star), R(2), // + B(LdaSmi8), U8(1), // + B(Star), R(3), // + B(LdaZero), // + B(Star), R(4), // + B(CallRuntime), U16(Runtime::kDefineDataPropertyUnchecked), R(1), // + U8(4), // + B(LdaConstant), U8(1), // + B(CreateObjectLiteral), U8(0), U8(13), // + B(Star), R(2), // + B(CallRuntime), U16(Runtime::kInternalSetPrototype), R(1), U8(2), // + B(Ldar), R(1), // + B(Return), // + }, + 2, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::FIXED_ARRAY_TYPE}}, + {"var n = 'name'; return { [n]: 'val', get a() { }, set a(b) {} };", + 5 * kPointerSize, + 1, + 69, + { + B(LdaConstant), U8(0), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(CreateObjectLiteral), U8(0), U8(simple_flags), // + B(Star), R(1), // + B(Ldar), R(0), // + B(ToName), // + B(Star), R(2), // + B(LdaConstant), U8(2), // + B(Star), R(3), // + B(LdaZero), // + B(Star), R(4), // + B(CallRuntime), U16(Runtime::kDefineDataPropertyUnchecked), R(1), // + U8(4), // + B(LdaConstant), U8(3), // + B(ToName), // + B(Star), R(2), // + B(LdaConstant), U8(4), // + B(CreateClosure), U8(0), // + B(Star), R(3), // + B(LdaZero), // + B(Star), R(4), // + B(CallRuntime), U16(Runtime::kDefineGetterPropertyUnchecked), // + R(1), U8(4), // + B(LdaConstant), U8(3), // + B(ToName), // + B(Star), R(2), // + B(LdaConstant), U8(5), // + B(CreateClosure), U8(0), // + B(Star), R(3), // + B(LdaZero), // + B(Star), R(4), // + B(CallRuntime), U16(Runtime::kDefineSetterPropertyUnchecked), // + R(1), U8(4), // + B(Ldar), R(1), // + B(Return), // + }, + 6, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::SHARED_FUNCTION_INFO_TYPE, + InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(TopLevelObjectLiterals) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + int has_function_flags = ObjectLiteral::kFastElements | + ObjectLiteral::kHasFunction | + ObjectLiteral::kDisableMementos; + ExpectedSnippet<InstanceType> snippets[] = { + {"var a = { func: function() { } };", + 5 * kPointerSize, + 1, + 50, + { + B(LdaConstant), U8(0), // + B(Star), R(1), // + B(LdaZero), // + B(Star), R(2), // + B(CallRuntime), U16(Runtime::kDeclareGlobals), R(1), U8(2), // + B(LdaConstant), U8(1), // + B(Star), R(1), // + B(LdaZero), // + B(Star), R(2), // + B(LdaConstant), U8(2), // + B(CreateObjectLiteral), U8(0), U8(has_function_flags), // + B(Star), R(4), // + B(LdaConstant), U8(4), // + B(CreateClosure), U8(1), // + B(StoreICSloppy), R(4), U8(3), U8(5), // + B(CallRuntime), U16(Runtime::kToFastProperties), R(4), U8(1), // + B(Ldar), R(4), // + B(Star), R(3), // + B(CallRuntime), U16(Runtime::kInitializeVarGlobal), R(1), U8(3), // + B(LdaUndefined), // + B(Return), // + }, + 5, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeTopLevelBytecode(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(TryCatch) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + // TODO(rmcilroy): modify tests when we have real try catch support. + ExpectedSnippet<int> snippets[] = { + {"try { return 1; } catch(e) { return 2; }", + kPointerSize, + 1, + 3, + { + B(LdaSmi8), U8(1), // + B(Return), // + }, + 0}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(TryFinally) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + // TODO(rmcilroy): modify tests when we have real try finally support. + ExpectedSnippet<int> snippets[] = { + {"var a = 1; try { a = 2; } finally { a = 3; }", + kPointerSize, + 1, + 14, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaSmi8), U8(2), // + B(Star), R(0), // + B(LdaSmi8), U8(3), // + B(Star), R(0), // + B(LdaUndefined), // + B(Return), // + }, + 0}, + {"var a = 1; try { a = 2; } catch(e) { a = 20 } finally { a = 3; }", + 2 * kPointerSize, + 1, + 14, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaSmi8), U8(2), // + B(Star), R(0), // + B(LdaSmi8), U8(3), // + B(Star), R(0), // + B(LdaUndefined), // + B(Return), // + }, + 0}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(Throw) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + // TODO(rmcilroy): modify tests when we have real try catch support. + ExpectedSnippet<const char*> snippets[] = { + {"throw 1;", + 0, + 1, + 3, + { + B(LdaSmi8), U8(1), // + B(Throw), // + }, + 0}, + {"throw 'Error';", + 0, + 1, + 3, + { + B(LdaConstant), U8(0), // + B(Throw), // + }, + 1, + {"Error"}}, + {"var a = 1; if (a) { throw 'Error'; };", + 1 * kPointerSize, + 1, + 13, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(Ldar), R(0), // + B(JumpIfToBooleanFalse), U8(5), // + B(LdaConstant), U8(0), // + B(Throw), // + B(LdaUndefined), // + B(Return), // + }, + 1, + {"Error"}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(CallNew) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot1 = feedback_spec.AddGeneralSlot(); + FeedbackVectorSlot slot2 = feedback_spec.AddLoadICSlot(); + USE(slot1); + + Handle<i::TypeFeedbackVector> vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + ExpectedSnippet<InstanceType> snippets[] = { + {"function bar() { this.value = 0; }\n" + "function f() { return new bar(); }\n" + "f()", + 1 * kPointerSize, + 1, + 10, + { + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot2)), // + B(Star), R(0), // + B(New), R(0), R(0), U8(0), // + B(Return), // + }, + 1, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"function bar(x) { this.value = 18; this.x = x;}\n" + "function f() { return new bar(3); }\n" + "f()", + 2 * kPointerSize, + 1, + 14, + { + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot2)), // + B(Star), R(0), // + B(LdaSmi8), U8(3), // + B(Star), R(1), // + B(New), R(0), R(1), U8(1), // + B(Return), // + }, + 1, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"function bar(w, x, y, z) {\n" + " this.value = 18;\n" + " this.x = x;\n" + " this.y = y;\n" + " this.z = z;\n" + "}\n" + "function f() { return new bar(3, 4, 5); }\n" + "f()", + 4 * kPointerSize, + 1, + 22, + { + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot2)), // + B(Star), R(0), // + B(LdaSmi8), U8(3), // + B(Star), R(1), // + B(LdaSmi8), U8(4), // + B(Star), R(2), // + B(LdaSmi8), U8(5), // + B(Star), R(3), // + B(New), R(0), R(1), U8(3), // + B(Return), // + }, + 1, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecode(snippets[i].code_snippet, "f"); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(ContextVariables) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + int closure = Register::function_closure().index(); + int first_context_slot = Context::MIN_CONTEXT_SLOTS; + ExpectedSnippet<InstanceType> snippets[] = { + {"var a; return function() { a = 1; };", + 1 * kPointerSize, + 1, + 12, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), // + R(closure), U8(1), // + B(PushContext), R(0), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Return), // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"var a = 1; return function() { a = 2; };", + 1 * kPointerSize, + 1, + 17, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), // + R(closure), U8(1), // + B(PushContext), R(0), // + B(LdaSmi8), U8(1), // + B(StaContextSlot), R(0), U8(first_context_slot), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Return), // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"var a = 1; var b = 2; return function() { a = 2; b = 3 };", + 1 * kPointerSize, + 1, + 22, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), // + R(closure), U8(1), // + B(PushContext), R(0), // + B(LdaSmi8), U8(1), // + B(StaContextSlot), R(0), U8(first_context_slot), // + B(LdaSmi8), U8(2), // + B(StaContextSlot), R(0), U8(first_context_slot + 1), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Return), // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"var a; (function() { a = 2; })(); return a;", + 3 * kPointerSize, + 1, + 24, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), // + R(closure), U8(1), // + B(PushContext), R(0), // + B(LdaUndefined), // + B(Star), R(2), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Star), R(1), // + B(Call), R(1), R(2), U8(0), // + B(LdaContextSlot), R(0), U8(first_context_slot), // + B(Return), // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"'use strict'; let a = 1; { let b = 2; return function() { a + b; }; }", + 4 * kPointerSize, + 1, + 45, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), // + R(closure), U8(1), // + B(PushContext), R(0), // + B(LdaTheHole), // + B(StaContextSlot), R(0), U8(first_context_slot), // + B(LdaSmi8), U8(1), // + B(StaContextSlot), R(0), U8(first_context_slot), // + B(LdaConstant), U8(0), // + B(Star), R(2), // + B(Ldar), R(closure), // + B(Star), R(3), // + B(CallRuntime), U16(Runtime::kPushBlockContext), R(2), U8(2), // + B(PushContext), R(1), // + B(LdaTheHole), // + B(StaContextSlot), R(1), U8(first_context_slot), // + B(LdaSmi8), U8(2), // + B(StaContextSlot), R(1), U8(first_context_slot), // + B(LdaConstant), U8(1), // + B(CreateClosure), U8(0), // + B(Return), // + }, + 2, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(ContextParameters) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + int closure = Register::function_closure().index(); + int first_context_slot = Context::MIN_CONTEXT_SLOTS; + + ExpectedSnippet<InstanceType> snippets[] = { + {"function f(arg1) { return function() { arg1 = 2; }; }", + 1 * kPointerSize, + 2, + 17, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), // + R(closure), U8(1), // + B(PushContext), R(0), // + B(Ldar), R(helper.kLastParamIndex), // + B(StaContextSlot), R(0), U8(first_context_slot), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Return), // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"function f(arg1) { var a = function() { arg1 = 2; }; return arg1; }", + 2 * kPointerSize, + 2, + 22, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), // + R(closure), U8(1), // + B(PushContext), R(1), // + B(Ldar), R(helper.kLastParamIndex), // + B(StaContextSlot), R(1), U8(first_context_slot), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Star), R(0), // + B(LdaContextSlot), R(1), U8(first_context_slot), // + B(Return), // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"function f(a1, a2, a3, a4) { return function() { a1 = a3; }; }", + 1 * kPointerSize, + 5, + 22, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), // + R(closure), U8(1), // + B(PushContext), R(0), // + B(Ldar), R(helper.kLastParamIndex - 3), // + B(StaContextSlot), R(0), U8(first_context_slot + 1), // + B(Ldar), R(helper.kLastParamIndex -1), // + B(StaContextSlot), R(0), U8(first_context_slot), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Return), // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"function f() { var self = this; return function() { self = 2; }; }", + 1 * kPointerSize, + 1, + 17, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), // + R(closure), U8(1), // + B(PushContext), R(0), // + B(Ldar), R(helper.kLastParamIndex), // + B(StaContextSlot), R(0), U8(first_context_slot), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Return), // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunction(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(OuterContextVariables) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + int context = Register::function_context().index(); + int first_context_slot = Context::MIN_CONTEXT_SLOTS; + + ExpectedSnippet<InstanceType> snippets[] = { + {"function Outer() {" + " var outerVar = 1;" + " function Inner(innerArg) {" + " this.innerFunc = function() { return outerVar * innerArg; }" + " }" + " this.getInnerFunc = function() { return new Inner(1).innerFunc; }" + "}" + "var f = new Outer().getInnerFunc();", + 2 * kPointerSize, + 1, + 20, + { + B(Ldar), R(context), // + B(Star), R(0), // + B(LdaContextSlot), R(0), U8(Context::PREVIOUS_INDEX), // + B(Star), R(0), // + B(LdaContextSlot), R(0), U8(first_context_slot), // + B(Star), R(1), // + B(LdaContextSlot), R(context), U8(first_context_slot), // + B(Mul), R(1), // + B(Return), // + }}, + {"function Outer() {" + " var outerVar = 1;" + " function Inner(innerArg) {" + " this.innerFunc = function() { outerVar = innerArg; }" + " }" + " this.getInnerFunc = function() { return new Inner(1).innerFunc; }" + "}" + "var f = new Outer().getInnerFunc();", + 2 * kPointerSize, + 1, + 21, + { + B(LdaContextSlot), R(context), U8(first_context_slot), // + B(Star), R(0), // + B(Ldar), R(context), // + B(Star), R(1), // + B(LdaContextSlot), R(1), U8(Context::PREVIOUS_INDEX), // + B(Star), R(1), // + B(Ldar), R(0), // + B(StaContextSlot), R(1), U8(first_context_slot), // + B(LdaUndefined), // + B(Return), // + }}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionNoFilter(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(CountOperators) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot1 = feedback_spec.AddLoadICSlot(); + FeedbackVectorSlot slot2 = feedback_spec.AddStoreICSlot(); + Handle<i::TypeFeedbackVector> vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + FeedbackVectorSpec store_feedback_spec(&zone); + FeedbackVectorSlot store_slot = store_feedback_spec.AddStoreICSlot(); + Handle<i::TypeFeedbackVector> store_vector = + i::NewTypeFeedbackVector(helper.isolate(), &store_feedback_spec); + + int closure = Register::function_closure().index(); + int first_context_slot = Context::MIN_CONTEXT_SLOTS; + + int object_literal_flags = + ObjectLiteral::kFastElements | ObjectLiteral::kDisableMementos; + int array_literal_flags = + ArrayLiteral::kDisableMementos | ArrayLiteral::kShallowElements; + + ExpectedSnippet<InstanceType> snippets[] = { + {"var a = 1; return ++a;", + 1 * kPointerSize, + 1, + 11, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(Ldar), R(0), // + B(ToNumber), // + B(Inc), // + B(Star), R(0), // + B(Return), // + }}, + {"var a = 1; return a++;", + 2 * kPointerSize, + 1, + 15, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(Ldar), R(0), // + B(ToNumber), // + B(Star), R(1), // + B(Inc), // + B(Star), R(0), // + B(Ldar), R(1), // + B(Return), // + }}, + {"var a = 1; return --a;", + 1 * kPointerSize, + 1, + 11, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(Ldar), R(0), // + B(ToNumber), // + B(Dec), // + B(Star), R(0), // + B(Return), // + }}, + {"var a = 1; return a--;", + 2 * kPointerSize, + 1, + 15, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(Ldar), R(0), // + B(ToNumber), // + B(Star), R(1), // + B(Dec), // + B(Star), R(0), // + B(Ldar), R(1), // + B(Return), // + }}, + {"var a = { val: 1 }; return a.val++;", + 2 * kPointerSize, + 1, + 22, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(object_literal_flags), // + B(Star), R(0), // + B(LoadICSloppy), R(0), U8(1), U8(vector->GetIndex(slot1)), // + B(ToNumber), // + B(Star), R(1), // + B(Inc), // + B(StoreICSloppy), R(0), U8(1), U8(vector->GetIndex(slot2)), // + B(Ldar), R(1), // + B(Return), // + }, + 2, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"var a = { val: 1 }; return --a.val;", + 1 * kPointerSize, + 1, + 18, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(object_literal_flags), // + B(Star), R(0), // + B(LoadICSloppy), R(0), U8(1), U8(vector->GetIndex(slot1)), // + B(ToNumber), // + B(Dec), // + B(StoreICSloppy), R(0), U8(1), U8(vector->GetIndex(slot2)), // + B(Return), // + }, + 2, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"var name = 'var'; var a = { val: 1 }; return a[name]--;", + 4 * kPointerSize, + 1, + 29, + { + B(LdaConstant), U8(0), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(CreateObjectLiteral), U8(0), U8(object_literal_flags), // + B(Star), R(1), // + B(Ldar), R(0), // + B(Star), R(2), // + B(KeyedLoadICSloppy), R(1), U8(vector->GetIndex(slot1)), // + B(ToNumber), // + B(Star), R(3), // + B(Dec), // + B(KeyedStoreICSloppy), R(1), R(2), U8(vector->GetIndex(slot2)), // + B(Ldar), R(3), // + B(Return), // + }, + 2, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::FIXED_ARRAY_TYPE}}, + {"var name = 'var'; var a = { val: 1 }; return ++a[name];", + 3 * kPointerSize, + 1, + 25, + { + B(LdaConstant), U8(0), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(CreateObjectLiteral), U8(0), U8(object_literal_flags), // + B(Star), R(1), // + B(Ldar), R(0), // + B(Star), R(2), // + B(KeyedLoadICSloppy), R(1), U8(vector->GetIndex(slot1)), // + B(ToNumber), // + B(Inc), // + B(KeyedStoreICSloppy), R(1), R(2), U8(vector->GetIndex(slot2)), // + B(Return), // + }, + 2, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::FIXED_ARRAY_TYPE}}, + {"var a = 1; var b = function() { return a }; return ++a;", + 2 * kPointerSize, + 1, + 27, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), // + U8(1), // + B(PushContext), R(1), // + B(LdaSmi8), U8(1), // + B(StaContextSlot), R(1), U8(first_context_slot), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Star), R(0), // + B(LdaContextSlot), R(1), U8(first_context_slot), // + B(ToNumber), // + B(Inc), // + B(StaContextSlot), R(1), U8(first_context_slot), // + B(Return), // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"var a = 1; var b = function() { return a }; return a--;", + 3 * kPointerSize, + 1, + 31, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), // + U8(1), // + B(PushContext), R(1), // + B(LdaSmi8), U8(1), // + B(StaContextSlot), R(1), U8(first_context_slot), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Star), R(0), // + B(LdaContextSlot), R(1), U8(first_context_slot), // + B(ToNumber), // + B(Star), R(2), // + B(Dec), // + B(StaContextSlot), R(1), U8(first_context_slot), // + B(Ldar), R(2), // + B(Return), // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"var idx = 1; var a = [1, 2]; return a[idx++] = 2;", + 3 * kPointerSize, + 1, + 26, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaConstant), U8(0), // + B(CreateArrayLiteral), U8(0), U8(array_literal_flags), // + B(Star), R(1), // + B(Ldar), R(0), // + B(ToNumber), // + B(Star), R(2), // + B(Inc), // + B(Star), R(0), // + B(LdaSmi8), U8(2), // + B(KeyedStoreICSloppy), R(1), R(2), // + U8(store_vector->GetIndex(store_slot)), // + B(Return), // + }, + 1, + {InstanceType::FIXED_ARRAY_TYPE}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(GlobalCountOperators) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot1 = feedback_spec.AddLoadICSlot(); + FeedbackVectorSlot slot2 = feedback_spec.AddStoreICSlot(); + + Handle<i::TypeFeedbackVector> vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + ExpectedSnippet<const char*> snippets[] = { + {"var global = 1;\nfunction f() { return ++global; }\nf()", + 0, + 1, + 9, + { + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot1)), // + B(ToNumber), // + B(Inc), // + B(StaGlobalSloppy), U8(0), U8(vector->GetIndex(slot2)), // + B(Return), // + }, + 1, + {"global"}}, + {"var global = 1;\nfunction f() { return global--; }\nf()", + 1 * kPointerSize, + 1, + 13, + { + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot1)), // + B(ToNumber), // + B(Star), R(0), // + B(Dec), // + B(StaGlobalSloppy), U8(0), U8(vector->GetIndex(slot2)), // + B(Ldar), R(0), // + B(Return), + }, + 1, + {"global"}}, + {"unallocated = 1;\nfunction f() { 'use strict'; return --unallocated; }" + "f()", + 0, + 1, + 9, + { + B(LdaGlobalStrict), U8(0), U8(vector->GetIndex(slot1)), // + B(ToNumber), // + B(Dec), // + B(StaGlobalStrict), U8(0), U8(vector->GetIndex(slot2)), // + B(Return), // + }, + 1, + {"unallocated"}}, + {"unallocated = 1;\nfunction f() { return unallocated++; }\nf()", + 1 * kPointerSize, + 1, + 13, + { + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot1)), // + B(ToNumber), // + B(Star), R(0), // + B(Inc), // + B(StaGlobalSloppy), U8(0), U8(vector->GetIndex(slot2)), // + B(Ldar), R(0), // + B(Return), + }, + 1, + {"unallocated"}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecode(snippets[i].code_snippet, "f"); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(CompoundExpressions) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + int closure = Register::function_closure().index(); + int first_context_slot = Context::MIN_CONTEXT_SLOTS; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot1 = feedback_spec.AddLoadICSlot(); + FeedbackVectorSlot slot2 = feedback_spec.AddStoreICSlot(); + + Handle<i::TypeFeedbackVector> vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + int object_literal_flags = + ObjectLiteral::kFastElements | ObjectLiteral::kDisableMementos; + ExpectedSnippet<InstanceType> snippets[] = { + {"var a = 1; a += 2;", + 1 * kPointerSize, + 1, + 12, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaSmi8), U8(2), // + B(Add), R(0), // + B(Star), R(0), // + B(LdaUndefined), // + B(Return), // + }}, + {"var a = 1; a /= 2;", + 1 * kPointerSize, + 1, + 12, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaSmi8), U8(2), // + B(Div), R(0), // + B(Star), R(0), // + B(LdaUndefined), // + B(Return), // + }}, + {"var a = { val: 2 }; a.name *= 2;", + 2 * kPointerSize, + 1, + 23, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(object_literal_flags), // + B(Star), R(0), // + B(LoadICSloppy), R(0), U8(1), U8(vector->GetIndex(slot1)), // + B(Star), R(1), // + B(LdaSmi8), U8(2), // + B(Mul), R(1), // + B(StoreICSloppy), R(0), U8(1), U8(vector->GetIndex(slot2)), // + B(LdaUndefined), // + B(Return), // + }, + 2, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"var a = { 1: 2 }; a[1] ^= 2;", + 3 * kPointerSize, + 1, + 26, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(object_literal_flags), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(KeyedLoadICSloppy), R(0), U8(vector->GetIndex(slot1)), // + B(Star), R(2), // + B(LdaSmi8), U8(2), // + B(BitwiseXor), R(2), // + B(KeyedStoreICSloppy), R(0), R(1), U8(vector->GetIndex(slot2)), // + B(LdaUndefined), // + B(Return), // + }, + 1, + {InstanceType::FIXED_ARRAY_TYPE}}, + {"var a = 1; (function f() { return a; }); a |= 24;", + 2 * kPointerSize, + 1, + 30, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), // + U8(1), // + B(PushContext), R(0), // + B(LdaSmi8), U8(1), // + B(StaContextSlot), R(0), U8(first_context_slot), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(LdaContextSlot), R(0), U8(first_context_slot), // + B(Star), R(1), // + B(LdaSmi8), U8(24), // + B(BitwiseOr), R(1), // + B(StaContextSlot), R(0), U8(first_context_slot), // + B(LdaUndefined), // + B(Return), // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(GlobalCompoundExpressions) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot1 = feedback_spec.AddLoadICSlot(); + FeedbackVectorSlot slot2 = feedback_spec.AddStoreICSlot(); + + Handle<i::TypeFeedbackVector> vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + ExpectedSnippet<const char*> snippets[] = { + {"var global = 1;\nfunction f() { return global &= 1; }\nf()", + 1 * kPointerSize, + 1, + 13, + { + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot1)), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(BitwiseAnd), R(0), // + B(StaGlobalSloppy), U8(0), U8(vector->GetIndex(slot2)), // + B(Return), // + }, + 1, + {"global"}}, + {"unallocated = 1;\nfunction f() { return unallocated += 1; }\nf()", + 1 * kPointerSize, + 1, + 13, + { + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot1)), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(Add), R(0), // + B(StaGlobalSloppy), U8(0), U8(vector->GetIndex(slot2)), // + B(Return), // + }, + 1, + {"unallocated"}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecode(snippets[i].code_snippet, "f"); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(CreateArguments) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + int closure = Register::function_closure().index(); + int first_context_slot = Context::MIN_CONTEXT_SLOTS; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot = feedback_spec.AddKeyedLoadICSlot(); + + Handle<i::TypeFeedbackVector> vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + ExpectedSnippet<const char*> snippets[] = { + {"function f() { return arguments; }", + 1 * kPointerSize, + 1, + 6, + { + B(CreateMappedArguments), // + B(Star), R(0), // + B(Ldar), R(0), // + B(Return), // + }}, + {"function f() { return arguments[0]; }", + 1 * kPointerSize, + 1, + 8, + { + B(CreateMappedArguments), // + B(Star), R(0), // + B(LdaZero), // + B(KeyedLoadICSloppy), R(0), U8(vector->GetIndex(slot)), // + B(Return), // + }}, + {"function f() { 'use strict'; return arguments; }", + 1 * kPointerSize, + 1, + 6, + { + B(CreateUnmappedArguments), // + B(Star), R(0), // + B(Ldar), R(0), // + B(Return), // + }}, + {"function f(a) { return arguments[0]; }", + 2 * kPointerSize, + 2, + 20, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), // + U8(1), // + B(PushContext), R(1), // + B(Ldar), R(BytecodeGeneratorHelper::kLastParamIndex), // + B(StaContextSlot), R(1), U8(first_context_slot), // + B(CreateMappedArguments), // + B(Star), R(0), // + B(LdaZero), // + B(KeyedLoadICSloppy), R(0), U8(vector->GetIndex(slot)), // + B(Return), // + }}, + {"function f(a, b, c) { return arguments; }", + 2 * kPointerSize, + 4, + 28, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), // + U8(1), // + B(PushContext), R(1), // + B(Ldar), R(BytecodeGeneratorHelper::kLastParamIndex - 2), // + B(StaContextSlot), R(1), U8(first_context_slot + 2), // + B(Ldar), R(BytecodeGeneratorHelper::kLastParamIndex - 1), // + B(StaContextSlot), R(1), U8(first_context_slot + 1), // + B(Ldar), R(BytecodeGeneratorHelper::kLastParamIndex), // + B(StaContextSlot), R(1), U8(first_context_slot), // + B(CreateMappedArguments), // + B(Star), R(0), // + B(Ldar), R(0), // + B(Return), // + }}, + {"function f(a, b, c) { 'use strict'; return arguments; }", + 1 * kPointerSize, + 4, + 6, + { + B(CreateUnmappedArguments), // + B(Star), R(0), // + B(Ldar), R(0), // + B(Return), // + }}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunction(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(IllegalRedeclaration) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + ExpectedSnippet<const char*> snippets[] = { + {"const a = 1; { var a = 2; }", + 3 * kPointerSize, + 1, + 14, + { + B(LdaSmi8), U8(MessageTemplate::kVarRedeclaration), // + B(Star), R(1), // + B(LdaConstant), U8(0), // + B(Star), R(2), // + B(CallRuntime), U16(Runtime::kNewSyntaxError), R(1), U8(2), // + B(Throw), // + }, + 1, + {"a"}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(ForIn) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + int simple_flags = + ArrayLiteral::kDisableMementos | ArrayLiteral::kShallowElements; + int deep_elements_flags = + ObjectLiteral::kFastElements | ObjectLiteral::kDisableMementos; + + FeedbackVectorSpec feedback_spec(&zone); + feedback_spec.AddStoreICSlot(); + FeedbackVectorSlot slot2 = feedback_spec.AddStoreICSlot(); + FeedbackVectorSlot slot3 = feedback_spec.AddStoreICSlot(); + FeedbackVectorSlot slot4 = feedback_spec.AddStoreICSlot(); + Handle<i::TypeFeedbackVector> vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + ExpectedSnippet<InstanceType> snippets[] = { + {"for (var p in null) {}", + 2 * kPointerSize, + 1, + 2, + {B(LdaUndefined), B(Return)}, + 0}, + {"for (var p in undefined) {}", + 2 * kPointerSize, + 1, + 2, + {B(LdaUndefined), B(Return)}, + 0}, + {"var x = 'potatoes';\n" + "for (var p in x) { return p; }", + 5 * kPointerSize, + 1, + 52, + { + B(LdaConstant), U8(0), // + B(Star), R(1), // + B(Ldar), R(1), // + B(JumpIfUndefined), U8(44), // + B(JumpIfNull), U8(42), // + B(ToObject), // + B(Star), R(3), // + B(CallRuntime), U16(Runtime::kGetPropertyNamesFast), R(3), U8(1), // + B(ForInPrepare), R(3), // + B(JumpIfUndefined), U8(30), // + B(Star), R(4), // + B(LdaZero), // + B(Star), R(3), // + B(ForInDone), R(4), // + B(JumpIfTrue), U8(21), // + B(ForInNext), R(4), R(3), // + B(JumpIfUndefined), U8(11), // + B(Star), R(0), // + B(Ldar), R(0), // + B(Star), R(2), // + B(Ldar), R(2), // + B(Return), // + B(Ldar), R(3), // + B(Inc), // + B(Jump), U8(-23), // + B(LdaUndefined), // + B(Return), // + }, + 1, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"var x = 0;\n" + "for (var p in [1,2,3]) { x += p; }", + 5 * kPointerSize, + 1, + 57, + { + B(LdaZero), // + B(Star), R(1), // + B(LdaConstant), U8(0), // + B(CreateArrayLiteral), U8(0), U8(simple_flags), // + B(JumpIfUndefined), U8(47), // + B(JumpIfNull), U8(45), // + B(ToObject), // + B(Star), R(3), // + B(CallRuntime), U16(Runtime::kGetPropertyNamesFast), R(3), U8(1), // + B(ForInPrepare), R(3), // + B(JumpIfUndefined), U8(33), // + B(Star), R(4), // + B(LdaZero), // + B(Star), R(3), // + B(ForInDone), R(4), // + B(JumpIfTrue), U8(24), // + B(ForInNext), R(4), R(3), // + B(JumpIfUndefined), U8(14), // + B(Star), R(0), // + B(Ldar), R(0), // + B(Star), R(2), // + B(Ldar), R(2), // + B(Add), R(1), // + B(Star), R(1), // + B(Ldar), R(3), // + B(Inc), // + B(Jump), U8(-26), // + B(LdaUndefined), // + B(Return), // + }, + 1, + {InstanceType::FIXED_ARRAY_TYPE}}, + {"var x = { 'a': 1, 'b': 2 };\n" + "for (x['a'] in [10, 20, 30]) {\n" + " if (x['a'] == 10) continue;\n" + " if (x['a'] == 20) break;\n" + "}", + 4 * kPointerSize, + 1, + 83, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(CreateArrayLiteral), U8(1), U8(simple_flags), // + B(JumpIfUndefined), U8(69), // + B(JumpIfNull), U8(67), // + B(ToObject), // + B(Star), R(1), // + B(CallRuntime), U16(Runtime::kGetPropertyNamesFast), R(1), U8(1), // + B(ForInPrepare), R(1), // + B(JumpIfUndefined), U8(55), // + B(Star), R(2), // + B(LdaZero), // + B(Star), R(1), // + B(ForInDone), R(2), // + B(JumpIfTrue), U8(46), // + B(ForInNext), R(2), R(1), // + B(JumpIfUndefined), U8(36), // + B(Star), R(3), // + B(StoreICSloppy), R(0), U8(2), U8(vector->GetIndex(slot4)), // + B(LoadICSloppy), R(0), U8(2), U8(vector->GetIndex(slot2)), // + B(Star), R(3), // + B(LdaSmi8), U8(10), // + B(TestEqual), R(3), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(16), // + B(LoadICSloppy), R(0), U8(2), U8(vector->GetIndex(slot3)), // + B(Star), R(3), // + B(LdaSmi8), U8(20), // + B(TestEqual), R(3), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(7), // + B(Ldar), R(1), // + B(Inc), // + B(Jump), U8(-48), // + B(LdaUndefined), // + B(Return), // + }, + 3, + {InstanceType::FIXED_ARRAY_TYPE, InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"var x = [ 10, 11, 12 ] ;\n" + "for (x[0] in [1,2,3]) { return x[3]; }", + 5 * kPointerSize, + 1, + 66, + { + B(LdaConstant), U8(0), // + B(CreateArrayLiteral), U8(0), U8(simple_flags), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(CreateArrayLiteral), U8(1), U8(simple_flags), // + B(JumpIfUndefined), U8(52), // + B(JumpIfNull), U8(50), // + B(ToObject), // + B(Star), R(1), // + B(CallRuntime), U16(Runtime::kGetPropertyNamesFast), R(1), U8(1), // + B(ForInPrepare), R(1), // + B(JumpIfUndefined), U8(38), // + B(Star), R(2), // + B(LdaZero), // + B(Star), R(1), // + B(ForInDone), R(2), // + B(JumpIfTrue), U8(29), // + B(ForInNext), R(2), R(1), // + B(JumpIfUndefined), U8(19), // + B(Star), R(3), // + B(LdaZero), // + B(Star), R(4), // + B(Ldar), R(3), // + B(KeyedStoreICSloppy), R(0), R(4), U8(vector->GetIndex(slot3)), // + B(LdaSmi8), U8(3), // + B(KeyedLoadICSloppy), R(0), U8(vector->GetIndex(slot2)), // + B(Return), // + B(Ldar), R(1), // + B(Inc), // + B(Jump), U8(-31), // + B(LdaUndefined), // + B(Return), // + }, + 2, + {InstanceType::FIXED_ARRAY_TYPE, InstanceType::FIXED_ARRAY_TYPE}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(Conditional) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + ExpectedSnippet<int> snippets[] = { + {"return 1 ? 2 : 3;", + 0, + 1, + 11, + { + B(LdaSmi8), U8(1), // + B(JumpIfToBooleanFalse), U8(6), // + B(LdaSmi8), U8(2), // + B(Jump), U8(4), // + B(LdaSmi8), U8(3), // + B(Return), // + }}, + {"return 1 ? 2 ? 3 : 4 : 5;", + 0, + 1, + 19, + { + B(LdaSmi8), U8(1), // + B(JumpIfToBooleanFalse), U8(14), // + B(LdaSmi8), U8(2), // + B(JumpIfToBooleanFalse), U8(6), // + B(LdaSmi8), U8(3), // + B(Jump), U8(4), // + B(LdaSmi8), U8(4), // + B(Jump), U8(4), // + B(LdaSmi8), U8(5), // + B(Return), // + }}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(Switch) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + ExpectedSnippet<int> snippets[] = { + {"var a = 1;\n" + "switch(a) {\n" + " case 1: return 2;\n" + " case 2: return 3;\n" + "}\n", + 2 * kPointerSize, + 1, + 30, + { + B(LdaSmi8), U8(1), // + B(Star), R(1), // The tag variable is allocated as a + B(Ldar), R(1), // local by the parser, hence this + B(Star), R(0), // strange shuffling. + B(LdaSmi8), U8(1), // + B(TestEqualStrict), R(0), // + B(JumpIfTrue), U8(10), // + B(LdaSmi8), U8(2), // + B(TestEqualStrict), R(0), // + B(JumpIfTrue), U8(7), // + B(Jump), U8(8), // + B(LdaSmi8), U8(2), // + B(Return), // + B(LdaSmi8), U8(3), // + B(Return), // + B(LdaUndefined), // + B(Return), // + }}, + {"var a = 1;\n" + "switch(a) {\n" + " case 1: a = 2; break;\n" + " case 2: a = 3; break;\n" + "}\n", + 2 * kPointerSize, + 1, + 36, + { + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(Ldar), R(1), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(TestEqualStrict), R(0), // + B(JumpIfTrue), U8(10), // + B(LdaSmi8), U8(2), // + B(TestEqualStrict), R(0), // + B(JumpIfTrue), U8(10), // + B(Jump), U8(14), // + B(LdaSmi8), U8(2), // + B(Star), R(1), // + B(Jump), U8(8), // + B(LdaSmi8), U8(3), // + B(Star), R(1), // + B(Jump), U8(2), // + B(LdaUndefined), // + B(Return), // + }}, + {"var a = 1;\n" + "switch(a) {\n" + " case 1: a = 2; // fall-through\n" + " case 2: a = 3; break;\n" + "}\n", + 2 * kPointerSize, + 1, + 34, + { + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(Ldar), R(1), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(TestEqualStrict), R(0), // + B(JumpIfTrue), U8(10), // + B(LdaSmi8), U8(2), // + B(TestEqualStrict), R(0), // + B(JumpIfTrue), U8(8), // + B(Jump), U8(12), // + B(LdaSmi8), U8(2), // + B(Star), R(1), // + B(LdaSmi8), U8(3), // + B(Star), R(1), // + B(Jump), U8(2), // + B(LdaUndefined), // + B(Return), // + }}, + {"var a = 1;\n" + "switch(a) {\n" + " case 2: break;\n" + " case 3: break;\n" + " default: a = 1; break;\n" + "}\n", + 2 * kPointerSize, + 1, + 34, + { + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(Ldar), R(1), // + B(Star), R(0), // + B(LdaSmi8), U8(2), // + B(TestEqualStrict), R(0), // + B(JumpIfTrue), U8(10), // + B(LdaSmi8), U8(3), // + B(TestEqualStrict), R(0), // + B(JumpIfTrue), U8(6), // + B(Jump), U8(6), // + B(Jump), U8(10), // + B(Jump), U8(8), // + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(Jump), U8(2), // + B(LdaUndefined), // + B(Return), // + }}, + {"var a = 1;\n" + "switch(typeof(a)) {\n" + " case 2: a = 1; break;\n" + " case 3: a = 2; break;\n" + " default: a = 3; break;\n" + "}\n", + 2 * kPointerSize, + 1, + 43, + { + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(Ldar), R(1), // + B(TypeOf), // + B(Star), R(0), // + B(LdaSmi8), U8(2), // + B(TestEqualStrict), R(0), // + B(JumpIfTrue), U8(10), // + B(LdaSmi8), U8(3), // + B(TestEqualStrict), R(0), // + B(JumpIfTrue), U8(10), // + B(Jump), U8(14), // + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(Jump), U8(14), // + B(LdaSmi8), U8(2), // + B(Star), R(1), // + B(Jump), U8(8), // + B(LdaSmi8), U8(3), // + B(Star), R(1), // + B(Jump), U8(2), // + B(LdaUndefined), // + B(Return), // + }}, + {"var a = 1;\n" + "switch(a) {\n" + " case typeof(a): a = 1; break;\n" + " default: a = 2; break;\n" + "}\n", + 2 * kPointerSize, + 1, + 31, + { + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(Ldar), R(1), // + B(Star), R(0), // + B(Ldar), R(1), // + B(TypeOf), // + B(TestEqualStrict), R(0), // + B(JumpIfTrue), U8(4), // + B(Jump), U8(8), // + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(Jump), U8(8), // + B(LdaSmi8), U8(2), // + B(Star), R(1), // + B(Jump), U8(2), // + B(LdaUndefined), // + B(Return), // + }}, + {"var a = 1;\n" + "switch(a) {\n" + " case 1:\n" REPEAT_64(SPACE, " a = 2;") + "break;\n" + " case 2: a = 3; break;" + "}\n", + 2 * kPointerSize, + 1, + 288, + { + B(LdaSmi8), U8(1), // + B(Star), R(1), // + B(Ldar), R(1), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(TestEqualStrict), R(0), // + B(JumpIfTrue), U8(10), // + B(LdaSmi8), U8(2), // + B(TestEqualStrict), R(0), // + B(JumpIfTrueConstant), U8(0), // + B(JumpConstant), U8(1), // + REPEAT_64(COMMA, // + B(LdaSmi8), U8(2), // + B(Star), R(1)), // + B(Jump), U8(8), // + B(LdaSmi8), U8(3), // + B(Star), R(1), // + B(Jump), U8(2), // + B(LdaUndefined), // + B(Return), // + }, + 2, + {262, 266}}, + {"var a = 1;\n" + "switch(a) {\n" + " case 1: \n" + " switch(a + 1) {\n" + " case 2 : a = 1; break;\n" + " default : a = 2; break;\n" + " } // fall-through\n" + " case 2: a = 3;\n" + "}\n", + 3 * kPointerSize, + 1, + 54, + { + B(LdaSmi8), U8(1), // + B(Star), R(2), // + B(Ldar), R(2), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(TestEqualStrict), R(0), // + B(JumpIfTrue), U8(10), // + B(LdaSmi8), U8(2), // + B(TestEqualStrict), R(0), // + B(JumpIfTrue), U8(30), // + B(Jump), U8(32), // + B(LdaSmi8), U8(1), // + B(Add), R(2), // + B(Star), R(1), // + B(LdaSmi8), U8(2), // + B(TestEqualStrict), R(1), // + B(JumpIfTrue), U8(4), // + B(Jump), U8(8), // + B(LdaSmi8), U8(1), // + B(Star), R(2), // + B(Jump), U8(8), // + B(LdaSmi8), U8(2), // + B(Star), R(2), // + B(Jump), U8(2), // + B(LdaSmi8), U8(3), // + B(Star), R(2), // + B(LdaUndefined), // + B(Return), // + }}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(BasicBlockToBoolean) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + // Check that we don't omit ToBoolean calls if they are at the start of basic + // blocks. + ExpectedSnippet<int> snippets[] = { + {"var a = 1; if (a || a < 0) { return 1; }", + 1 * kPointerSize, + 1, + 18, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(Ldar), R(0), // + B(JumpIfToBooleanTrue), U8(5), // + B(LdaZero), // + B(TestLessThan), R(0), // + B(JumpIfToBooleanFalse), U8(5), // + B(LdaSmi8), U8(1), // + B(Return), // + B(LdaUndefined), // + B(Return), // + }}, + {"var a = 1; if (a && a < 0) { return 1; }", + 1 * kPointerSize, + 1, + 18, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(Ldar), R(0), // + B(JumpIfToBooleanFalse), U8(5), // + B(LdaZero), // + B(TestLessThan), R(0), // + B(JumpIfToBooleanFalse), U8(5), // + B(LdaSmi8), U8(1), // + B(Return), // + B(LdaUndefined), // + B(Return), // + }}, + {"var a = 1; a = (a || a < 0) ? 2 : 3;", + 1 * kPointerSize, + 1, + 23, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(Ldar), R(0), // + B(JumpIfToBooleanTrue), U8(5), // + B(LdaZero), // + B(TestLessThan), R(0), // + B(JumpIfToBooleanFalse), U8(6), // + B(LdaSmi8), U8(2), // + B(Jump), U8(4), // + B(LdaSmi8), U8(3), // + B(Star), R(0), // + B(LdaUndefined), // + B(Return), // + }}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(DeadCodeRemoval) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + ExpectedSnippet<int> snippets[] = { + {"return; var a = 1; a();", + 1 * kPointerSize, + 1, + 2, + { + B(LdaUndefined), // + B(Return), // + }}, + {"if (false) { return; }; var a = 1;", + 1 * kPointerSize, + 1, + 6, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(LdaUndefined), // + B(Return), // + }}, + {"if (true) { return 1; } else { return 2; };", + 0, + 1, + 3, + { + B(LdaSmi8), U8(1), // + B(Return), // + }}, + {"var a = 1; if (a) { return 1; }; return 2;", + 1 * kPointerSize, + 1, + 14, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(Ldar), R(0), // + B(JumpIfToBooleanFalse), U8(5), // + B(LdaSmi8), U8(1), // + B(Return), // + B(LdaSmi8), U8(2), // + B(Return), // + }}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(ThisFunction) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + int closure = Register::function_closure().index(); + + ExpectedSnippet<int> snippets[] = { + {"var f;\n f = function f() { }", + 1 * kPointerSize, + 1, + 9, + { + B(LdaTheHole), // + B(Star), R(0), // + B(Ldar), R(closure), // + B(Star), R(0), // + B(LdaUndefined), // + B(Return), // + }}, + {"var f;\n f = function f() { return f; }", + 1 * kPointerSize, + 1, + 10, + { + B(LdaTheHole), // + B(Star), R(0), // + B(Ldar), R(closure), // + B(Star), R(0), // + B(Ldar), R(0), // + B(Return), // + }}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunction(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(NewTarget) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + ExpectedSnippet<int> snippets[] = { + {"return new.target;", + 1 * kPointerSize, + 1, + 10, + { + B(CallRuntime), U16(Runtime::kGetOriginalConstructor), R(0), // + U8(0), // + B(Star), R(0), // + B(Ldar), R(0), // + B(Return), // + }}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle<BytecodeArray> bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + } // namespace interpreter } // namespace internal -} // namespance v8 +} // namespace v8 diff --git a/deps/v8/test/cctest/interpreter/test-interpreter.cc b/deps/v8/test/cctest/interpreter/test-interpreter.cc index e238318291..d274fa73cb 100644 --- a/deps/v8/test/cctest/interpreter/test-interpreter.cc +++ b/deps/v8/test/cctest/interpreter/test-interpreter.cc @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(rmcilroy): Remove this define after this flag is turned on globally +#define V8_IMMINENT_DEPRECATION_WARNINGS + #include "src/v8.h" #include "src/execution.h" @@ -9,6 +12,7 @@ #include "src/interpreter/bytecode-array-builder.h" #include "src/interpreter/interpreter.h" #include "test/cctest/cctest.h" +#include "test/cctest/test-feedback-vector.h" namespace v8 { namespace internal { @@ -57,18 +61,20 @@ class InterpreterTester { public: InterpreterTester(Isolate* isolate, const char* source, MaybeHandle<BytecodeArray> bytecode, - MaybeHandle<TypeFeedbackVector> feedback_vector) + MaybeHandle<TypeFeedbackVector> feedback_vector, + const char* filter) : isolate_(isolate), source_(source), bytecode_(bytecode), feedback_vector_(feedback_vector) { i::FLAG_vector_stores = true; i::FLAG_ignition = true; + i::FLAG_ignition_fake_try_catch = true; i::FLAG_always_opt = false; // 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); + SNPrintF(ignition_filter, "--ignition-filter=%s", filter); FlagList::SetFlagsFromString(ignition_filter.start(), ignition_filter.length()); // Ensure handler table is generated. @@ -77,13 +83,16 @@ class InterpreterTester { InterpreterTester(Isolate* isolate, Handle<BytecodeArray> bytecode, MaybeHandle<TypeFeedbackVector> feedback_vector = - MaybeHandle<TypeFeedbackVector>()) - : InterpreterTester(isolate, nullptr, bytecode, feedback_vector) {} + MaybeHandle<TypeFeedbackVector>(), + const char* filter = kFunctionName) + : InterpreterTester(isolate, nullptr, bytecode, feedback_vector, filter) { + } - InterpreterTester(Isolate* isolate, const char* source) + InterpreterTester(Isolate* isolate, const char* source, + const char* filter = kFunctionName) : InterpreterTester(isolate, source, MaybeHandle<BytecodeArray>(), - MaybeHandle<TypeFeedbackVector>()) {} + MaybeHandle<TypeFeedbackVector>(), filter) {} virtual ~InterpreterTester() {} @@ -101,6 +110,10 @@ class InterpreterTester { return isolate->factory()->string_table()->LookupString(isolate, result); } + static std::string SourceForBody(const char* body) { + return "function " + function_name() + "() {\n" + std::string(body) + "\n}"; + } + static std::string function_name() { return std::string(kFunctionName); } @@ -116,9 +129,13 @@ class InterpreterTester { Handle<JSFunction> function; if (source_) { CompileRun(source_); + v8::Local<v8::Context> context = + v8::Isolate::GetCurrent()->GetCurrentContext(); Local<Function> api_function = - Local<Function>::Cast(CcTest::global()->Get(v8_str(kFunctionName))); - function = v8::Utils::OpenHandle(*api_function); + Local<Function>::Cast(CcTest::global() + ->Get(context, v8_str(kFunctionName)) + .ToLocalChecked()); + function = Handle<JSFunction>::cast(v8::Utils::OpenHandle(*api_function)); } else { int arg_count = sizeof...(A); std::string source("(function " + function_name() + "("); @@ -126,8 +143,8 @@ class InterpreterTester { source += i == 0 ? "a" : ", a"; } source += "){})"; - function = v8::Utils::OpenHandle( - *v8::Handle<v8::Function>::Cast(CompileRun(source.c_str()))); + function = Handle<JSFunction>::cast(v8::Utils::OpenHandle( + *v8::Local<v8::Function>::Cast(CompileRun(source.c_str())))); function->ReplaceCode( *isolate_->builtins()->InterpreterEntryTrampoline()); } @@ -145,18 +162,6 @@ class InterpreterTester { DISALLOW_COPY_AND_ASSIGN(InterpreterTester); }; -} // namespace interpreter -} // namespace internal -} // namespace v8 - -using v8::internal::BytecodeArray; -using v8::internal::Handle; -using v8::internal::LanguageMode; -using v8::internal::Object; -using v8::internal::Runtime; -using v8::internal::Smi; -using v8::internal::Token; -using namespace v8::internal::interpreter; TEST(InterpreterReturn) { HandleAndZoneScope handles; @@ -165,6 +170,7 @@ TEST(InterpreterReturn) { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(0); + builder.set_context_count(0); builder.set_parameter_count(1); builder.Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -183,6 +189,7 @@ TEST(InterpreterLoadUndefined) { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(0); + builder.set_context_count(0); builder.set_parameter_count(1); builder.LoadUndefined().Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -200,6 +207,7 @@ TEST(InterpreterLoadNull) { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(0); + builder.set_context_count(0); builder.set_parameter_count(1); builder.LoadNull().Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -218,6 +226,7 @@ TEST(InterpreterLoadTheHole) { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(0); + builder.set_context_count(0); builder.set_parameter_count(1); builder.LoadTheHole().Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -235,6 +244,7 @@ TEST(InterpreterLoadTrue) { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(0); + builder.set_context_count(0); builder.set_parameter_count(1); builder.LoadTrue().Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -252,6 +262,7 @@ TEST(InterpreterLoadFalse) { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(0); + builder.set_context_count(0); builder.set_parameter_count(1); builder.LoadFalse().Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -271,6 +282,7 @@ TEST(InterpreterLoadLiteral) { for (int i = -128; i < 128; i++) { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(0); + builder.set_context_count(0); builder.set_parameter_count(1); builder.LoadLiteral(Smi::FromInt(i)).Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -285,6 +297,7 @@ TEST(InterpreterLoadLiteral) { { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(0); + builder.set_context_count(0); builder.set_parameter_count(1); builder.LoadLiteral(Smi::FromInt(0x12345678)).Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -299,6 +312,7 @@ TEST(InterpreterLoadLiteral) { { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(0); + builder.set_context_count(0); builder.set_parameter_count(1); builder.LoadLiteral(factory->NewHeapNumber(-2.1e19)).Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -313,6 +327,7 @@ TEST(InterpreterLoadLiteral) { { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(0); + builder.set_context_count(0); builder.set_parameter_count(1); Handle<i::String> string = factory->NewStringFromAsciiChecked("String"); builder.LoadLiteral(string).Return(); @@ -332,6 +347,7 @@ TEST(InterpreterLoadStoreRegisters) { for (int i = 0; i <= Register::kMaxRegisterIndex; i++) { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(i + 1); + builder.set_context_count(0); builder.set_parameter_count(1); Register reg(i); builder.LoadTrue() @@ -349,9 +365,15 @@ TEST(InterpreterLoadStoreRegisters) { } +static const Token::Value kShiftOperators[] = { + Token::Value::SHL, Token::Value::SAR, Token::Value::SHR}; + + static const Token::Value kArithmeticOperators[] = { - Token::Value::ADD, Token::Value::SUB, Token::Value::MUL, Token::Value::DIV, - Token::Value::MOD}; + Token::Value::BIT_OR, Token::Value::BIT_XOR, Token::Value::BIT_AND, + Token::Value::SHL, Token::Value::SAR, Token::Value::SHR, + Token::Value::ADD, Token::Value::SUB, Token::Value::MUL, + Token::Value::DIV, Token::Value::MOD}; static double BinaryOpC(Token::Value op, double lhs, double rhs) { @@ -366,6 +388,33 @@ static double BinaryOpC(Token::Value op, double lhs, double rhs) { return lhs / rhs; case Token::Value::MOD: return std::fmod(lhs, rhs); + case Token::Value::BIT_OR: + return (v8::internal::DoubleToInt32(lhs) | + v8::internal::DoubleToInt32(rhs)); + case Token::Value::BIT_XOR: + return (v8::internal::DoubleToInt32(lhs) ^ + v8::internal::DoubleToInt32(rhs)); + case Token::Value::BIT_AND: + return (v8::internal::DoubleToInt32(lhs) & + v8::internal::DoubleToInt32(rhs)); + case Token::Value::SHL: { + int32_t val = v8::internal::DoubleToInt32(lhs); + uint32_t count = v8::internal::DoubleToUint32(rhs) & 0x1F; + int32_t result = val << count; + return result; + } + case Token::Value::SAR: { + int32_t val = v8::internal::DoubleToInt32(lhs); + uint32_t count = v8::internal::DoubleToUint32(rhs) & 0x1F; + int32_t result = val >> count; + return result; + } + case Token::Value::SHR: { + uint32_t val = v8::internal::DoubleToUint32(lhs); + uint32_t count = v8::internal::DoubleToUint32(rhs) & 0x1F; + uint32_t result = val >> count; + return result; + } default: UNREACHABLE(); return std::numeric_limits<double>::min(); @@ -373,6 +422,41 @@ static double BinaryOpC(Token::Value op, double lhs, double rhs) { } +TEST(InterpreterShiftOpsSmi) { + int lhs_inputs[] = {0, -17, -182, 1073741823, -1}; + int rhs_inputs[] = {5, 2, 1, -1, -2, 0, 31, 32, -32, 64, 37}; + for (size_t l = 0; l < arraysize(lhs_inputs); l++) { + for (size_t r = 0; r < arraysize(rhs_inputs); r++) { + for (size_t o = 0; o < arraysize(kShiftOperators); o++) { + HandleAndZoneScope handles; + i::Factory* factory = handles.main_isolate()->factory(); + BytecodeArrayBuilder builder(handles.main_isolate(), + handles.main_zone()); + builder.set_locals_count(1); + builder.set_context_count(0); + builder.set_parameter_count(1); + Register reg(0); + int lhs = lhs_inputs[l]; + int rhs = rhs_inputs[r]; + builder.LoadLiteral(Smi::FromInt(lhs)) + .StoreAccumulatorInRegister(reg) + .LoadLiteral(Smi::FromInt(rhs)) + .BinaryOperation(kShiftOperators[o], reg, Strength::WEAK) + .Return(); + Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); + + InterpreterTester tester(handles.main_isolate(), bytecode_array); + auto callable = tester.GetCallable<>(); + Handle<Object> return_value = callable().ToHandleChecked(); + Handle<Object> expected_value = + factory->NewNumber(BinaryOpC(kShiftOperators[o], lhs, rhs)); + CHECK(return_value->SameValue(*expected_value)); + } + } + } +} + + TEST(InterpreterBinaryOpsSmi) { int lhs_inputs[] = {3266, 1024, 0, -17, -18000}; int rhs_inputs[] = {3266, 5, 4, 3, 2, 1, -1, -2}; @@ -384,14 +468,15 @@ TEST(InterpreterBinaryOpsSmi) { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(1); + builder.set_context_count(0); builder.set_parameter_count(1); Register reg(0); int lhs = lhs_inputs[l]; - int rhs = rhs_inputs[l]; + int rhs = rhs_inputs[r]; builder.LoadLiteral(Smi::FromInt(lhs)) .StoreAccumulatorInRegister(reg) .LoadLiteral(Smi::FromInt(rhs)) - .BinaryOperation(kArithmeticOperators[o], reg) + .BinaryOperation(kArithmeticOperators[o], reg, Strength::WEAK) .Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -419,14 +504,15 @@ TEST(InterpreterBinaryOpsHeapNumber) { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(1); + builder.set_context_count(0); builder.set_parameter_count(1); Register reg(0); double lhs = lhs_inputs[l]; - double rhs = rhs_inputs[l]; + double rhs = rhs_inputs[r]; builder.LoadLiteral(factory->NewNumber(lhs)) .StoreAccumulatorInRegister(reg) .LoadLiteral(factory->NewNumber(rhs)) - .BinaryOperation(kArithmeticOperators[o], reg) + .BinaryOperation(kArithmeticOperators[o], reg, Strength::WEAK) .Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -477,12 +563,13 @@ TEST(InterpreterStringAdd) { for (size_t i = 0; i < arraysize(test_cases); i++) { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(1); + builder.set_context_count(0); builder.set_parameter_count(1); Register reg(0); builder.LoadLiteral(test_cases[i].lhs) .StoreAccumulatorInRegister(reg) .LoadLiteral(test_cases[i].rhs) - .BinaryOperation(Token::Value::ADD, reg) + .BinaryOperation(Token::Value::ADD, reg, Strength::WEAK) .Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -498,6 +585,7 @@ TEST(InterpreterParameter1) { HandleAndZoneScope handles; BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(0); + builder.set_context_count(0); builder.set_parameter_count(1); builder.LoadAccumulatorWithRegister(builder.Parameter(0)).Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -521,15 +609,16 @@ TEST(InterpreterParameter8) { HandleAndZoneScope handles; BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(0); + builder.set_context_count(0); builder.set_parameter_count(8); builder.LoadAccumulatorWithRegister(builder.Parameter(0)) - .BinaryOperation(Token::Value::ADD, builder.Parameter(1)) - .BinaryOperation(Token::Value::ADD, builder.Parameter(2)) - .BinaryOperation(Token::Value::ADD, builder.Parameter(3)) - .BinaryOperation(Token::Value::ADD, builder.Parameter(4)) - .BinaryOperation(Token::Value::ADD, builder.Parameter(5)) - .BinaryOperation(Token::Value::ADD, builder.Parameter(6)) - .BinaryOperation(Token::Value::ADD, builder.Parameter(7)) + .BinaryOperation(Token::Value::ADD, builder.Parameter(1), Strength::WEAK) + .BinaryOperation(Token::Value::ADD, builder.Parameter(2), Strength::WEAK) + .BinaryOperation(Token::Value::ADD, builder.Parameter(3), Strength::WEAK) + .BinaryOperation(Token::Value::ADD, builder.Parameter(4), Strength::WEAK) + .BinaryOperation(Token::Value::ADD, builder.Parameter(5), Strength::WEAK) + .BinaryOperation(Token::Value::ADD, builder.Parameter(6), Strength::WEAK) + .BinaryOperation(Token::Value::ADD, builder.Parameter(7), Strength::WEAK) .Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -553,6 +642,28 @@ TEST(InterpreterParameter8) { } +TEST(InterpreterParameter1Assign) { + HandleAndZoneScope handles; + BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); + builder.set_locals_count(0); + builder.set_context_count(0); + builder.set_parameter_count(1); + builder.LoadLiteral(Smi::FromInt(5)) + .StoreAccumulatorInRegister(builder.Parameter(0)) + .LoadAccumulatorWithRegister(builder.Parameter(0)) + .Return(); + Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); + + InterpreterTester tester(handles.main_isolate(), bytecode_array); + auto callable = tester.GetCallable<Handle<Object>>(); + + Handle<Object> return_val = + callable(Handle<Smi>(Smi::FromInt(3), handles.main_isolate())) + .ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(5)); +} + + TEST(InterpreterLoadGlobal) { HandleAndZoneScope handles; @@ -570,6 +681,28 @@ TEST(InterpreterLoadGlobal) { } +TEST(InterpreterStoreGlobal) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + // Test storing to a global. + std::string source( + "var global = 321;\n" + "function " + InterpreterTester::function_name() + "() {\n" + " global = 999;\n" + "}"); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + callable().ToHandleChecked(); + Handle<i::String> name = factory->InternalizeUtf8String("global"); + Handle<i::Object> global_obj = + Object::GetProperty(isolate->global_object(), name).ToHandleChecked(); + CHECK_EQ(Smi::cast(*global_obj), Smi::FromInt(999)); +} + + TEST(InterpreterCallGlobal) { HandleAndZoneScope handles; @@ -587,25 +720,67 @@ TEST(InterpreterCallGlobal) { } +TEST(InterpreterLoadUnallocated) { + HandleAndZoneScope handles; + + // Test loading an unallocated global. + std::string source( + "unallocated = 123;\n" + "function " + InterpreterTester::function_name() + "() {\n" + " return unallocated;\n" + "}"); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<Object> return_val = callable().ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123)); +} + + +TEST(InterpreterStoreUnallocated) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + // Test storing to an unallocated global. + std::string source( + "unallocated = 321;\n" + "function " + InterpreterTester::function_name() + "() {\n" + " unallocated = 999;\n" + "}"); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + callable().ToHandleChecked(); + Handle<i::String> name = factory->InternalizeUtf8String("unallocated"); + Handle<i::Object> global_obj = + Object::GetProperty(isolate->global_object(), name).ToHandleChecked(); + CHECK_EQ(Smi::cast(*global_obj), Smi::FromInt(999)); +} + + TEST(InterpreterLoadNamedProperty) { HandleAndZoneScope handles; i::Isolate* isolate = handles.main_isolate(); i::Factory* factory = isolate->factory(); + i::Zone zone; + + i::FeedbackVectorSpec feedback_spec(&zone); + i::FeedbackVectorSlot slot = feedback_spec.AddLoadICSlot(); - i::FeedbackVectorSlotKind ic_kinds[] = {i::FeedbackVectorSlotKind::LOAD_IC}; - i::StaticFeedbackVectorSpec feedback_spec(0, 1, ic_kinds); Handle<i::TypeFeedbackVector> vector = - factory->NewTypeFeedbackVector(&feedback_spec); + i::NewTypeFeedbackVector(isolate, &feedback_spec); Handle<i::String> name = factory->NewStringFromAsciiChecked("val"); name = factory->string_table()->LookupString(isolate, name); BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(0); + builder.set_context_count(0); builder.set_parameter_count(1); - builder.LoadLiteral(name) - .LoadNamedProperty(builder.Parameter(0), vector->first_ic_slot_index(), - i::SLOPPY) + size_t name_index = builder.GetConstantPoolEntry(name); + builder.LoadNamedProperty(builder.Parameter(0), name_index, + vector->GetIndex(slot), i::SLOPPY) .Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -645,22 +820,24 @@ TEST(InterpreterLoadKeyedProperty) { HandleAndZoneScope handles; i::Isolate* isolate = handles.main_isolate(); i::Factory* factory = isolate->factory(); + i::Zone zone; + + i::FeedbackVectorSpec feedback_spec(&zone); + i::FeedbackVectorSlot slot = feedback_spec.AddKeyedLoadICSlot(); - i::FeedbackVectorSlotKind ic_kinds[] = { - i::FeedbackVectorSlotKind::KEYED_LOAD_IC}; - i::StaticFeedbackVectorSpec feedback_spec(0, 1, ic_kinds); Handle<i::TypeFeedbackVector> vector = - factory->NewTypeFeedbackVector(&feedback_spec); + i::NewTypeFeedbackVector(isolate, &feedback_spec); Handle<i::String> key = factory->NewStringFromAsciiChecked("key"); key = factory->string_table()->LookupString(isolate, key); BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(1); + builder.set_context_count(0); builder.set_parameter_count(1); builder.LoadLiteral(key) - .LoadKeyedProperty(builder.Parameter(0), vector->first_ic_slot_index(), - i::SLOPPY) + .LoadKeyedProperty(builder.Parameter(0), vector->GetIndex(slot), + i::STRICT) .Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -688,23 +865,25 @@ TEST(InterpreterStoreNamedProperty) { HandleAndZoneScope handles; i::Isolate* isolate = handles.main_isolate(); i::Factory* factory = isolate->factory(); + i::Zone zone; + + i::FeedbackVectorSpec feedback_spec(&zone); + i::FeedbackVectorSlot slot = feedback_spec.AddStoreICSlot(); - i::FeedbackVectorSlotKind ic_kinds[] = {i::FeedbackVectorSlotKind::STORE_IC}; - i::StaticFeedbackVectorSpec feedback_spec(0, 1, ic_kinds); Handle<i::TypeFeedbackVector> vector = - factory->NewTypeFeedbackVector(&feedback_spec); + i::NewTypeFeedbackVector(isolate, &feedback_spec); Handle<i::String> name = factory->NewStringFromAsciiChecked("val"); name = factory->string_table()->LookupString(isolate, name); BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); - builder.set_locals_count(1); + builder.set_locals_count(0); + builder.set_context_count(0); builder.set_parameter_count(1); - builder.LoadLiteral(name) - .StoreAccumulatorInRegister(Register(0)) - .LoadLiteral(Smi::FromInt(999)) - .StoreNamedProperty(builder.Parameter(0), Register(0), - vector->first_ic_slot_index(), i::SLOPPY) + size_t name_index = builder.GetConstantPoolEntry(name); + builder.LoadLiteral(Smi::FromInt(999)) + .StoreNamedProperty(builder.Parameter(0), name_index, + vector->GetIndex(slot), i::STRICT) .Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -748,24 +927,26 @@ TEST(InterpreterStoreKeyedProperty) { HandleAndZoneScope handles; i::Isolate* isolate = handles.main_isolate(); i::Factory* factory = isolate->factory(); + i::Zone zone; + + i::FeedbackVectorSpec feedback_spec(&zone); + i::FeedbackVectorSlot slot = feedback_spec.AddKeyedStoreICSlot(); - i::FeedbackVectorSlotKind ic_kinds[] = { - i::FeedbackVectorSlotKind::KEYED_STORE_IC}; - i::StaticFeedbackVectorSpec feedback_spec(0, 1, ic_kinds); Handle<i::TypeFeedbackVector> vector = - factory->NewTypeFeedbackVector(&feedback_spec); + i::NewTypeFeedbackVector(isolate, &feedback_spec); Handle<i::String> name = factory->NewStringFromAsciiChecked("val"); name = factory->string_table()->LookupString(isolate, name); BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(1); + builder.set_context_count(0); builder.set_parameter_count(1); builder.LoadLiteral(name) .StoreAccumulatorInRegister(Register(0)) .LoadLiteral(Smi::FromInt(999)) .StoreKeyedProperty(builder.Parameter(0), Register(0), - vector->first_ic_slot_index(), i::SLOPPY) + vector->GetIndex(slot), i::SLOPPY) .Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -796,11 +977,14 @@ TEST(InterpreterCall) { HandleAndZoneScope handles; i::Isolate* isolate = handles.main_isolate(); i::Factory* factory = isolate->factory(); + i::Zone zone; + + i::FeedbackVectorSpec feedback_spec(&zone); + i::FeedbackVectorSlot slot = feedback_spec.AddLoadICSlot(); - i::FeedbackVectorSlotKind ic_kinds[] = {i::FeedbackVectorSlotKind::LOAD_IC}; - i::StaticFeedbackVectorSpec feedback_spec(0, 1, ic_kinds); Handle<i::TypeFeedbackVector> vector = - factory->NewTypeFeedbackVector(&feedback_spec); + i::NewTypeFeedbackVector(isolate, &feedback_spec); + int slot_index = vector->GetIndex(slot); Handle<i::String> name = factory->NewStringFromAsciiChecked("func"); name = factory->string_table()->LookupString(isolate, name); @@ -809,10 +993,11 @@ TEST(InterpreterCall) { { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(1); + builder.set_context_count(0); builder.set_parameter_count(1); - builder.LoadLiteral(name) - .LoadNamedProperty(builder.Parameter(0), vector->first_ic_slot_index(), - i::SLOPPY) + size_t name_index = builder.GetConstantPoolEntry(name); + builder.LoadNamedProperty(builder.Parameter(0), name_index, slot_index, + i::SLOPPY) .StoreAccumulatorInRegister(Register(0)) .Call(Register(0), builder.Parameter(0), 0) .Return(); @@ -831,10 +1016,11 @@ TEST(InterpreterCall) { { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(1); + builder.set_context_count(0); builder.set_parameter_count(1); - builder.LoadLiteral(name) - .LoadNamedProperty(builder.Parameter(0), vector->first_ic_slot_index(), - i::SLOPPY) + size_t name_index = builder.GetConstantPoolEntry(name); + builder.LoadNamedProperty(builder.Parameter(0), name_index, slot_index, + i::SLOPPY) .StoreAccumulatorInRegister(Register(0)) .Call(Register(0), builder.Parameter(0), 0) .Return(); @@ -856,10 +1042,11 @@ TEST(InterpreterCall) { { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(4); + builder.set_context_count(0); builder.set_parameter_count(1); - builder.LoadLiteral(name) - .LoadNamedProperty(builder.Parameter(0), vector->first_ic_slot_index(), - i::SLOPPY) + size_t name_index = builder.GetConstantPoolEntry(name); + builder.LoadNamedProperty(builder.Parameter(0), name_index, slot_index, + i::SLOPPY) .StoreAccumulatorInRegister(Register(0)) .LoadAccumulatorWithRegister(builder.Parameter(0)) .StoreAccumulatorInRegister(Register(1)) @@ -886,10 +1073,11 @@ TEST(InterpreterCall) { { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(12); + builder.set_context_count(0); builder.set_parameter_count(1); - builder.LoadLiteral(name) - .LoadNamedProperty(builder.Parameter(0), vector->first_ic_slot_index(), - i::SLOPPY) + size_t name_index = builder.GetConstantPoolEntry(name); + builder.LoadNamedProperty(builder.Parameter(0), name_index, slot_index, + i::SLOPPY) .StoreAccumulatorInRegister(Register(0)) .LoadAccumulatorWithRegister(builder.Parameter(0)) .StoreAccumulatorInRegister(Register(1)) @@ -950,7 +1138,7 @@ static BytecodeArrayBuilder& IncrementRegister(BytecodeArrayBuilder& builder, Register scratch) { return builder.StoreAccumulatorInRegister(scratch) .LoadLiteral(Smi::FromInt(value)) - .BinaryOperation(Token::Value::ADD, reg) + .BinaryOperation(Token::Value::ADD, reg, Strength::WEAK) .StoreAccumulatorInRegister(reg) .LoadAccumulatorWithRegister(scratch); } @@ -960,6 +1148,7 @@ TEST(InterpreterJumps) { HandleAndZoneScope handles; BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(2); + builder.set_context_count(0); builder.set_parameter_count(0); Register reg(0), scratch(1); BytecodeLabel label[3]; @@ -988,6 +1177,43 @@ TEST(InterpreterConditionalJumps) { HandleAndZoneScope handles; BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); builder.set_locals_count(2); + builder.set_context_count(0); + builder.set_parameter_count(0); + Register reg(0), scratch(1); + BytecodeLabel label[2]; + BytecodeLabel done, done1; + + builder.LoadLiteral(Smi::FromInt(0)) + .StoreAccumulatorInRegister(reg) + .LoadFalse() + .JumpIfFalse(&label[0]); + IncrementRegister(builder, reg, 1024, scratch) + .Bind(&label[0]) + .LoadTrue() + .JumpIfFalse(&done); + IncrementRegister(builder, reg, 1, scratch).LoadTrue().JumpIfTrue(&label[1]); + IncrementRegister(builder, reg, 2048, scratch).Bind(&label[1]); + IncrementRegister(builder, reg, 2, scratch).LoadFalse().JumpIfTrue(&done1); + IncrementRegister(builder, reg, 4, scratch) + .LoadAccumulatorWithRegister(reg) + .Bind(&done) + .Bind(&done1) + .Return(); + + Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); + InterpreterTester tester(handles.main_isolate(), bytecode_array); + auto callable = tester.GetCallable<>(); + Handle<Object> return_value = callable().ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_value)->value(), 7); +} + + +TEST(InterpreterConditionalJumps2) { + // TODO(oth): Add tests for all conditional jumps near and far. + HandleAndZoneScope handles; + BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); + builder.set_locals_count(2); + builder.set_context_count(0); builder.set_parameter_count(0); Register reg(0), scratch(1); BytecodeLabel label[2]; @@ -1076,11 +1302,12 @@ TEST(InterpreterSmiComparisons) { handles.main_zone()); Register r0(0); builder.set_locals_count(1); + builder.set_context_count(0); builder.set_parameter_count(0); builder.LoadLiteral(Smi::FromInt(inputs[i])) .StoreAccumulatorInRegister(r0) .LoadLiteral(Smi::FromInt(inputs[j])) - .CompareOperation(comparison, r0, LanguageMode::SLOPPY) + .CompareOperation(comparison, r0, Strength::WEAK) .Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -1114,11 +1341,12 @@ TEST(InterpreterHeapNumberComparisons) { handles.main_zone()); Register r0(0); builder.set_locals_count(1); + builder.set_context_count(0); builder.set_parameter_count(0); builder.LoadLiteral(factory->NewHeapNumber(inputs[i])) .StoreAccumulatorInRegister(r0) .LoadLiteral(factory->NewHeapNumber(inputs[j])) - .CompareOperation(comparison, r0, LanguageMode::SLOPPY) + .CompareOperation(comparison, r0, Strength::WEAK) .Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -1149,11 +1377,12 @@ TEST(InterpreterStringComparisons) { handles.main_zone()); Register r0(0); builder.set_locals_count(1); + builder.set_context_count(0); builder.set_parameter_count(0); builder.LoadLiteral(factory->NewStringFromAsciiChecked(lhs)) .StoreAccumulatorInRegister(r0) .LoadLiteral(factory->NewStringFromAsciiChecked(rhs)) - .CompareOperation(comparison, r0, LanguageMode::SLOPPY) + .CompareOperation(comparison, r0, Strength::WEAK) .Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -1195,20 +1424,21 @@ TEST(InterpreterMixedComparisons) { handles.main_zone()); Register r0(0); builder.set_locals_count(1); + builder.set_context_count(0); builder.set_parameter_count(0); if (pass == 0) { // Comparison with HeapNumber on the lhs and String on the rhs builder.LoadLiteral(factory->NewNumber(lhs)) .StoreAccumulatorInRegister(r0) .LoadLiteral(factory->NewStringFromAsciiChecked(rhs_cstr)) - .CompareOperation(comparison, r0, LanguageMode::SLOPPY) + .CompareOperation(comparison, r0, Strength::WEAK) .Return(); } else { // Comparison with HeapNumber on the rhs and String on the lhs builder.LoadLiteral(factory->NewStringFromAsciiChecked(lhs_cstr)) .StoreAccumulatorInRegister(r0) .LoadLiteral(factory->NewNumber(rhs)) - .CompareOperation(comparison, r0, LanguageMode::SLOPPY) + .CompareOperation(comparison, r0, Strength::WEAK) .Return(); } @@ -1239,11 +1469,12 @@ TEST(InterpreterInstanceOf) { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); Register r0(0); builder.set_locals_count(1); + builder.set_context_count(0); builder.set_parameter_count(0); builder.LoadLiteral(cases[i]); builder.StoreAccumulatorInRegister(r0) .LoadLiteral(func) - .CompareOperation(Token::Value::INSTANCEOF, r0, LanguageMode::SLOPPY) + .CompareOperation(Token::Value::INSTANCEOF, r0, Strength::WEAK) .Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -1269,11 +1500,12 @@ TEST(InterpreterTestIn) { BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); Register r0(0); builder.set_locals_count(1); + builder.set_context_count(0); builder.set_parameter_count(0); builder.LoadLiteral(factory->NewStringFromAsciiChecked(properties[i])) .StoreAccumulatorInRegister(r0) .LoadLiteral(Handle<Object>::cast(array)) - .CompareOperation(Token::Value::IN, r0, LanguageMode::SLOPPY) + .CompareOperation(Token::Value::IN, r0, Strength::WEAK) .Return(); Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); @@ -1284,3 +1516,1441 @@ TEST(InterpreterTestIn) { CHECK_EQ(return_value->BooleanValue(), expected_value); } } + + +TEST(InterpreterUnaryNot) { + HandleAndZoneScope handles; + for (size_t i = 1; i < 10; i++) { + bool expected_value = ((i & 1) == 1); + BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); + Register r0(0); + builder.set_locals_count(0); + builder.set_context_count(0); + builder.set_parameter_count(0); + builder.LoadFalse(); + for (size_t j = 0; j < i; j++) { + builder.LogicalNot(); + } + builder.Return(); + Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); + InterpreterTester tester(handles.main_isolate(), bytecode_array); + auto callable = tester.GetCallable<>(); + Handle<Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->IsBoolean()); + CHECK_EQ(return_value->BooleanValue(), expected_value); + } +} + + +static void LoadAny(BytecodeArrayBuilder* builder, + v8::internal::Factory* factory, Handle<Object> obj) { + if (obj->IsOddball()) { + if (obj->SameValue(*factory->true_value())) { + builder->LoadTrue(); + } else if (obj->SameValue(*factory->false_value())) { + builder->LoadFalse(); + } else if (obj->SameValue(*factory->the_hole_value())) { + builder->LoadTheHole(); + } else if (obj->SameValue(*factory->null_value())) { + builder->LoadNull(); + } else if (obj->SameValue(*factory->undefined_value())) { + builder->LoadUndefined(); + } else { + UNREACHABLE(); + } + } else if (obj->IsSmi()) { + builder->LoadLiteral(*Handle<Smi>::cast(obj)); + } else { + builder->LoadLiteral(obj); + } +} + + +TEST(InterpreterToBoolean) { + HandleAndZoneScope handles; + i::Factory* factory = handles.main_isolate()->factory(); + + std::pair<Handle<Object>, bool> object_type_tuples[] = { + std::make_pair(factory->undefined_value(), false), + std::make_pair(factory->null_value(), false), + std::make_pair(factory->false_value(), false), + std::make_pair(factory->true_value(), true), + std::make_pair(factory->NewNumber(9.1), true), + std::make_pair(factory->NewNumberFromInt(0), false), + std::make_pair( + Handle<Object>::cast(factory->NewStringFromStaticChars("hello")), + true), + std::make_pair( + Handle<Object>::cast(factory->NewStringFromStaticChars("")), false), + }; + + for (size_t i = 0; i < arraysize(object_type_tuples); i++) { + BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); + Register r0(0); + builder.set_locals_count(0); + builder.set_context_count(0); + builder.set_parameter_count(0); + LoadAny(&builder, factory, object_type_tuples[i].first); + builder.CastAccumulatorToBoolean(); + builder.Return(); + Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); + InterpreterTester tester(handles.main_isolate(), bytecode_array); + auto callable = tester.GetCallable<>(); + Handle<Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->IsBoolean()); + CHECK_EQ(return_value->BooleanValue(), object_type_tuples[i].second); + } +} + + +TEST(InterpreterUnaryNotNonBoolean) { + HandleAndZoneScope handles; + i::Factory* factory = handles.main_isolate()->factory(); + + std::pair<Handle<Object>, bool> object_type_tuples[] = { + std::make_pair(factory->undefined_value(), true), + std::make_pair(factory->null_value(), true), + std::make_pair(factory->false_value(), true), + std::make_pair(factory->true_value(), false), + std::make_pair(factory->NewNumber(9.1), false), + std::make_pair(factory->NewNumberFromInt(0), true), + std::make_pair( + Handle<Object>::cast(factory->NewStringFromStaticChars("hello")), + false), + std::make_pair( + Handle<Object>::cast(factory->NewStringFromStaticChars("")), true), + }; + + for (size_t i = 0; i < arraysize(object_type_tuples); i++) { + BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); + Register r0(0); + builder.set_locals_count(0); + builder.set_context_count(0); + builder.set_parameter_count(0); + LoadAny(&builder, factory, object_type_tuples[i].first); + builder.LogicalNot(); + builder.Return(); + Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); + InterpreterTester tester(handles.main_isolate(), bytecode_array); + auto callable = tester.GetCallable<>(); + Handle<Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->IsBoolean()); + CHECK_EQ(return_value->BooleanValue(), object_type_tuples[i].second); + } +} + + +TEST(InterpreterTypeof) { + HandleAndZoneScope handles; + + std::pair<const char*, const char*> typeof_vals[] = { + std::make_pair("return typeof undefined;", "undefined"), + std::make_pair("return typeof null;", "object"), + std::make_pair("return typeof true;", "boolean"), + std::make_pair("return typeof false;", "boolean"), + std::make_pair("return typeof 9.1;", "number"), + std::make_pair("return typeof 7771;", "number"), + std::make_pair("return typeof 'hello';", "string"), + std::make_pair("return typeof global_unallocated;", "undefined"), + }; + + for (size_t i = 0; i < arraysize(typeof_vals); i++) { + std::string source(InterpreterTester::SourceForBody(typeof_vals[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + + auto callable = tester.GetCallable<>(); + Handle<v8::internal::String> return_value = + Handle<v8::internal::String>::cast(callable().ToHandleChecked()); + auto actual = return_value->ToCString(); + CHECK_EQ(strcmp(&actual[0], typeof_vals[i].second), 0); + } +} + + +TEST(InterpreterCallRuntime) { + HandleAndZoneScope handles; + + BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); + builder.set_locals_count(2); + builder.set_context_count(0); + builder.set_parameter_count(1); + builder.LoadLiteral(Smi::FromInt(15)) + .StoreAccumulatorInRegister(Register(0)) + .LoadLiteral(Smi::FromInt(40)) + .StoreAccumulatorInRegister(Register(1)) + .CallRuntime(Runtime::kAdd, Register(0), 2) + .Return(); + Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(); + + InterpreterTester tester(handles.main_isolate(), bytecode_array); + auto callable = tester.GetCallable<>(); + + Handle<Object> return_val = callable().ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(55)); +} + + +TEST(InterpreterFunctionLiteral) { + HandleAndZoneScope handles; + + // Test calling a function literal. + std::string source( + "function " + InterpreterTester::function_name() + "(a) {\n" + " return (function(x){ return x + 2; })(a);\n" + "}"); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<Handle<Object>>(); + + Handle<i::Object> return_val = callable( + Handle<Smi>(Smi::FromInt(3), handles.main_isolate())).ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(5)); +} + + +TEST(InterpreterRegExpLiterals) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + std::pair<const char*, Handle<Object>> literals[] = { + std::make_pair("return /abd/.exec('cccabbdd');\n", + factory->null_value()), + std::make_pair("return /ab+d/.exec('cccabbdd')[0];\n", + factory->NewStringFromStaticChars("abbd")), + std::make_pair("return /AbC/i.exec('ssaBC')[0];\n", + factory->NewStringFromStaticChars("aBC")), + std::make_pair("return 'ssaBC'.match(/AbC/i)[0];\n", + factory->NewStringFromStaticChars("aBC")), + std::make_pair("return 'ssaBCtAbC'.match(/(AbC)/gi)[1];\n", + factory->NewStringFromStaticChars("AbC")), + }; + + for (size_t i = 0; i < arraysize(literals); i++) { + std::string source(InterpreterTester::SourceForBody(literals[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*literals[i].second)); + } +} + + +TEST(InterpreterArrayLiterals) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + std::pair<const char*, Handle<Object>> literals[] = { + std::make_pair("return [][0];\n", + factory->undefined_value()), + std::make_pair("return [1, 3, 2][1];\n", + handle(Smi::FromInt(3), isolate)), + std::make_pair("return ['a', 'b', 'c'][2];\n", + factory->NewStringFromStaticChars("c")), + std::make_pair("var a = 100; return [a, a + 1, a + 2, a + 3][2];\n", + handle(Smi::FromInt(102), isolate)), + std::make_pair("return [[1, 2, 3], ['a', 'b', 'c']][1][0];\n", + factory->NewStringFromStaticChars("a")), + std::make_pair("var t = 't'; return [[t, t + 'est'], [1 + t]][0][1];\n", + factory->NewStringFromStaticChars("test")) + }; + + for (size_t i = 0; i < arraysize(literals); i++) { + std::string source(InterpreterTester::SourceForBody(literals[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*literals[i].second)); + } +} + + +TEST(InterpreterObjectLiterals) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + std::pair<const char*, Handle<Object>> literals[] = { + std::make_pair("return { }.name;", + factory->undefined_value()), + std::make_pair("return { name: 'string', val: 9.2 }.name;", + factory->NewStringFromStaticChars("string")), + std::make_pair("var a = 15; return { name: 'string', val: a }.val;", + handle(Smi::FromInt(15), isolate)), + std::make_pair("var a = 5; return { val: a, val: a + 1 }.val;", + handle(Smi::FromInt(6), isolate)), + std::make_pair("return { func: function() { return 'test' } }.func();", + factory->NewStringFromStaticChars("test")), + std::make_pair("return { func(a) { return a + 'st'; } }.func('te');", + factory->NewStringFromStaticChars("test")), + std::make_pair("return { get a() { return 22; } }.a;", + handle(Smi::FromInt(22), isolate)), + std::make_pair("var a = { get b() { return this.x + 't'; },\n" + " set b(val) { this.x = val + 's' } };\n" + "a.b = 'te';\n" + "return a.b;", + factory->NewStringFromStaticChars("test")), + std::make_pair("var a = 123; return { 1: a }[1];", + handle(Smi::FromInt(123), isolate)), + std::make_pair("return Object.getPrototypeOf({ __proto__: null });", + factory->null_value()), + std::make_pair("var a = 'test'; return { [a]: 1 }.test;", + handle(Smi::FromInt(1), isolate)), + std::make_pair("var a = 'test'; return { b: a, [a]: a + 'ing' }['test']", + factory->NewStringFromStaticChars("testing")), + std::make_pair("var a = 'proto_str';\n" + "var b = { [a]: 1, __proto__: { var : a } };\n" + "return Object.getPrototypeOf(b).var", + factory->NewStringFromStaticChars("proto_str")), + std::make_pair("var n = 'name';\n" + "return { [n]: 'val', get a() { return 987 } }['a'];", + handle(Smi::FromInt(987), isolate)), + }; + + for (size_t i = 0; i < arraysize(literals); i++) { + std::string source(InterpreterTester::SourceForBody(literals[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*literals[i].second)); + } +} + + +TEST(InterpreterConstruct) { + HandleAndZoneScope handles; + + std::string source( + "function counter() { this.count = 0; }\n" + "function " + + InterpreterTester::function_name() + + "() {\n" + " var c = new counter();\n" + " return c.count;\n" + "}"); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<Object> return_val = callable().ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(0)); +} + + +TEST(InterpreterConstructWithArgument) { + HandleAndZoneScope handles; + + std::string source( + "function counter(arg0) { this.count = 17; this.x = arg0; }\n" + "function " + + InterpreterTester::function_name() + + "() {\n" + " var c = new counter(3);\n" + " return c.x;\n" + "}"); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<Object> return_val = callable().ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(3)); +} + + +TEST(InterpreterConstructWithArguments) { + HandleAndZoneScope handles; + + std::string source( + "function counter(arg0, arg1) {\n" + " this.count = 7; this.x = arg0; this.y = arg1;\n" + "}\n" + "function " + + InterpreterTester::function_name() + + "() {\n" + " var c = new counter(3, 5);\n" + " return c.count + c.x + c.y;\n" + "}"); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<Object> return_val = callable().ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(15)); +} + + +TEST(InterpreterContextVariables) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + + std::pair<const char*, Handle<Object>> context_vars[] = { + std::make_pair("var a; (function() { a = 1; })(); return a;", + handle(Smi::FromInt(1), isolate)), + std::make_pair("var a = 10; (function() { a; })(); return a;", + handle(Smi::FromInt(10), isolate)), + std::make_pair("var a = 20; var b = 30;\n" + "return (function() { return a + b; })();", + handle(Smi::FromInt(50), isolate)), + std::make_pair("'use strict'; let a = 1;\n" + "{ let b = 2; return (function() { return a + b; })(); }", + handle(Smi::FromInt(3), isolate)), + std::make_pair("'use strict'; let a = 10;\n" + "{ let b = 20; var c = function() { [a, b] };\n" + " return a + b; }", + handle(Smi::FromInt(30), isolate)), + }; + + for (size_t i = 0; i < arraysize(context_vars); i++) { + std::string source(InterpreterTester::SourceForBody(context_vars[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*context_vars[i].second)); + } +} + + +TEST(InterpreterContextParameters) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + + std::pair<const char*, Handle<Object>> context_params[] = { + std::make_pair("return (function() { return arg1; })();", + handle(Smi::FromInt(1), isolate)), + std::make_pair("(function() { arg1 = 4; })(); return arg1;", + handle(Smi::FromInt(4), isolate)), + std::make_pair("(function() { arg3 = arg2 - arg1; })(); return arg3;", + handle(Smi::FromInt(1), isolate)), + }; + + for (size_t i = 0; i < arraysize(context_params); i++) { + std::string source = "function " + InterpreterTester::function_name() + + "(arg1, arg2, arg3) {" + context_params[i].first + "}"; + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = + tester.GetCallable<Handle<Object>, Handle<Object>, Handle<Object>>(); + + Handle<Object> a1 = handle(Smi::FromInt(1), isolate); + Handle<Object> a2 = handle(Smi::FromInt(2), isolate); + Handle<Object> a3 = handle(Smi::FromInt(3), isolate); + Handle<i::Object> return_value = callable(a1, a2, a3).ToHandleChecked(); + CHECK(return_value->SameValue(*context_params[i].second)); + } +} + + +TEST(InterpreterOuterContextVariables) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + + std::pair<const char*, Handle<Object>> context_vars[] = { + std::make_pair("return outerVar * innerArg;", + handle(Smi::FromInt(200), isolate)), + std::make_pair("outerVar = innerArg; return outerVar", + handle(Smi::FromInt(20), isolate)), + }; + + std::string header( + "function Outer() {" + " var outerVar = 10;" + " function Inner(innerArg) {" + " this.innerFunc = function() { "); + std::string footer( + " }}" + " this.getInnerFunc = function() { return new Inner(20).innerFunc; }" + "}" + "var f = new Outer().getInnerFunc();"); + + for (size_t i = 0; i < arraysize(context_vars); i++) { + std::string source = header + context_vars[i].first + footer; + InterpreterTester tester(handles.main_isolate(), source.c_str(), "*"); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*context_vars[i].second)); + } +} + + +TEST(InterpreterComma) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + std::pair<const char*, Handle<Object>> literals[] = { + std::make_pair("var a; return 0, a;\n", factory->undefined_value()), + std::make_pair("return 'a', 2.2, 3;\n", + handle(Smi::FromInt(3), isolate)), + std::make_pair("return 'a', 'b', 'c';\n", + factory->NewStringFromStaticChars("c")), + std::make_pair("return 3.2, 2.3, 4.5;\n", factory->NewNumber(4.5)), + std::make_pair("var a = 10; return b = a, b = b+1;\n", + handle(Smi::FromInt(11), isolate)), + std::make_pair("var a = 10; return b = a, b = b+1, b + 10;\n", + handle(Smi::FromInt(21), isolate))}; + + for (size_t i = 0; i < arraysize(literals); i++) { + std::string source(InterpreterTester::SourceForBody(literals[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*literals[i].second)); + } +} + + +TEST(InterpreterLogicalOr) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + std::pair<const char*, Handle<Object>> literals[] = { + std::make_pair("var a, b; return a || b;\n", factory->undefined_value()), + std::make_pair("var a, b = 10; return a || b;\n", + handle(Smi::FromInt(10), isolate)), + std::make_pair("var a = '0', b = 10; return a || b;\n", + factory->NewStringFromStaticChars("0")), + std::make_pair("return 0 || 3.2;\n", factory->NewNumber(3.2)), + std::make_pair("return 'a' || 0;\n", + factory->NewStringFromStaticChars("a")), + std::make_pair("var a = '0', b = 10; return (a == 0) || b;\n", + factory->true_value())}; + + for (size_t i = 0; i < arraysize(literals); i++) { + std::string source(InterpreterTester::SourceForBody(literals[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*literals[i].second)); + } +} + + +TEST(InterpreterLogicalAnd) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + std::pair<const char*, Handle<Object>> literals[] = { + std::make_pair("var a, b = 10; return a && b;\n", + factory->undefined_value()), + std::make_pair("var a = 0, b = 10; return a && b / a;\n", + handle(Smi::FromInt(0), isolate)), + std::make_pair("var a = '0', b = 10; return a && b;\n", + handle(Smi::FromInt(10), isolate)), + std::make_pair("return 0.0 && 3.2;\n", handle(Smi::FromInt(0), isolate)), + std::make_pair("return 'a' && 'b';\n", + factory->NewStringFromStaticChars("b")), + std::make_pair("return 'a' && 0 || 'b', 'c';\n", + factory->NewStringFromStaticChars("c")), + std::make_pair("var x = 1, y = 3; return x && 0 + 1 || y;\n", + handle(Smi::FromInt(1), isolate)), + std::make_pair("var x = 1, y = 3; return (x == 1) && (3 == 3) || y;\n", + factory->true_value())}; + + for (size_t i = 0; i < arraysize(literals); i++) { + std::string source(InterpreterTester::SourceForBody(literals[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*literals[i].second)); + } +} + + +TEST(InterpreterTryCatch) { + HandleAndZoneScope handles; + + // TODO(rmcilroy): modify tests when we have real try catch support. + std::string source(InterpreterTester::SourceForBody( + "var a = 1; try { a = a + 1; } catch(e) { a = a + 2; }; return a;")); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<Object> return_val = callable().ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(2)); +} + + +TEST(InterpreterTryFinally) { + HandleAndZoneScope handles; + + // TODO(rmcilroy): modify tests when we have real try finally support. + std::string source(InterpreterTester::SourceForBody( + "var a = 1; try { a = a + 1; } finally { a = a + 2; }; return a;")); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<Object> return_val = callable().ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(4)); +} + + +TEST(InterpreterThrow) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + // TODO(rmcilroy): modify tests when we have real try catch support. + std::pair<const char*, Handle<Object>> throws[] = { + std::make_pair("throw undefined;\n", + factory->undefined_value()), + std::make_pair("throw 1;\n", + handle(Smi::FromInt(1), isolate)), + std::make_pair("throw 'Error';\n", + factory->NewStringFromStaticChars("Error")), + std::make_pair("var a = true; if (a) { throw 'Error'; }\n", + factory->NewStringFromStaticChars("Error")), + std::make_pair("var a = false; if (a) { throw 'Error'; }\n", + factory->undefined_value()), + std::make_pair("throw 'Error1'; throw 'Error2'\n", + factory->NewStringFromStaticChars("Error1")), + }; + + const char* try_wrapper = + "(function() { try { f(); } catch(e) { return e; }})()"; + + for (size_t i = 0; i < arraysize(throws); i++) { + std::string source(InterpreterTester::SourceForBody(throws[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + tester.GetCallable<>(); + Handle<Object> thrown_obj = v8::Utils::OpenHandle(*CompileRun(try_wrapper)); + CHECK(thrown_obj->SameValue(*throws[i].second)); + } +} + + +TEST(InterpreterCountOperators) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + std::pair<const char*, Handle<Object>> count_ops[] = { + std::make_pair("var a = 1; return ++a;", + handle(Smi::FromInt(2), isolate)), + std::make_pair("var a = 1; return a++;", + handle(Smi::FromInt(1), isolate)), + std::make_pair("var a = 5; return --a;", + handle(Smi::FromInt(4), isolate)), + std::make_pair("var a = 5; return a--;", + handle(Smi::FromInt(5), isolate)), + std::make_pair("var a = 5.2; return --a;", + factory->NewHeapNumber(4.2)), + std::make_pair("var a = 'string'; return ++a;", + factory->nan_value()), + std::make_pair("var a = 'string'; return a--;", + factory->nan_value()), + std::make_pair("var a = true; return ++a;", + handle(Smi::FromInt(2), isolate)), + std::make_pair("var a = false; return a--;", + handle(Smi::FromInt(0), isolate)), + std::make_pair("var a = { val: 11 }; return ++a.val;", + handle(Smi::FromInt(12), isolate)), + std::make_pair("var a = { val: 11 }; return a.val--;", + handle(Smi::FromInt(11), isolate)), + std::make_pair("var a = { val: 11 }; return ++a.val;", + handle(Smi::FromInt(12), isolate)), + std::make_pair("var name = 'val'; var a = { val: 22 }; return --a[name];", + handle(Smi::FromInt(21), isolate)), + std::make_pair("var name = 'val'; var a = { val: 22 }; return a[name]++;", + handle(Smi::FromInt(22), isolate)), + std::make_pair("var a = 1; (function() { a = 2 })(); return ++a;", + handle(Smi::FromInt(3), isolate)), + std::make_pair("var a = 1; (function() { a = 2 })(); return a--;", + handle(Smi::FromInt(2), isolate)), + }; + + for (size_t i = 0; i < arraysize(count_ops); i++) { + std::string source(InterpreterTester::SourceForBody(count_ops[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*count_ops[i].second)); + } +} + + +TEST(InterpreterGlobalCountOperators) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + + std::pair<const char*, Handle<Object>> count_ops[] = { + std::make_pair("var global = 100;function f(){ return ++global; }", + handle(Smi::FromInt(101), isolate)), + std::make_pair("var global = 100; function f(){ return --global; }", + handle(Smi::FromInt(99), isolate)), + std::make_pair("var global = 100; function f(){ return global++; }", + handle(Smi::FromInt(100), isolate)), + std::make_pair("unallocated = 200; function f(){ return ++unallocated; }", + handle(Smi::FromInt(201), isolate)), + std::make_pair("unallocated = 200; function f(){ return --unallocated; }", + handle(Smi::FromInt(199), isolate)), + std::make_pair("unallocated = 200; function f(){ return unallocated++; }", + handle(Smi::FromInt(200), isolate)), + }; + + for (size_t i = 0; i < arraysize(count_ops); i++) { + InterpreterTester tester(handles.main_isolate(), count_ops[i].first); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*count_ops[i].second)); + } +} + + +TEST(InterpreterCompoundExpressions) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + std::pair<const char*, Handle<Object>> compound_expr[] = { + std::make_pair("var a = 1; a += 2; return a;", + Handle<Object>(Smi::FromInt(3), isolate)), + std::make_pair("var a = 10; a /= 2; return a;", + Handle<Object>(Smi::FromInt(5), isolate)), + std::make_pair("var a = 'test'; a += 'ing'; return a;", + factory->NewStringFromStaticChars("testing")), + std::make_pair("var a = { val: 2 }; a.val *= 2; return a.val;", + Handle<Object>(Smi::FromInt(4), isolate)), + std::make_pair("var a = 1; (function f() { a = 2; })(); a += 24;" + "return a;", + Handle<Object>(Smi::FromInt(26), isolate)), + }; + + for (size_t i = 0; i < arraysize(compound_expr); i++) { + std::string source( + InterpreterTester::SourceForBody(compound_expr[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*compound_expr[i].second)); + } +} + + +TEST(InterpreterGlobalCompoundExpressions) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + + std::pair<const char*, Handle<Object>> compound_expr[2] = { + std::make_pair("var global = 100;" + "function f() { global += 20; return global; }", + Handle<Object>(Smi::FromInt(120), isolate)), + std::make_pair("unallocated = 100;" + "function f() { unallocated -= 20; return unallocated; }", + Handle<Object>(Smi::FromInt(80), isolate)), + }; + + for (size_t i = 0; i < arraysize(compound_expr); i++) { + InterpreterTester tester(handles.main_isolate(), compound_expr[i].first); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*compound_expr[i].second)); + } +} + + +TEST(InterpreterCreateArguments) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + std::pair<const char*, int> create_args[] = { + std::make_pair("function f() { return arguments[0]; }", 0), + std::make_pair("function f(a) { return arguments[0]; }", 0), + std::make_pair("function f() { return arguments[2]; }", 2), + std::make_pair("function f(a) { return arguments[2]; }", 2), + std::make_pair("function f(a, b, c, d) { return arguments[2]; }", 2), + std::make_pair("function f(a) {" + "'use strict'; return arguments[0]; }", + 0), + std::make_pair("function f(a, b, c, d) {" + "'use strict'; return arguments[2]; }", + 2), + // Check arguments are mapped in sloppy mode and unmapped in strict. + std::make_pair("function f(a, b, c, d) {" + " c = b; return arguments[2]; }", + 1), + std::make_pair("function f(a, b, c, d) {" + " 'use strict'; c = b; return arguments[2]; }", + 2), + }; + + // Test passing no arguments. + for (size_t i = 0; i < arraysize(create_args); i++) { + InterpreterTester tester(handles.main_isolate(), create_args[i].first); + auto callable = tester.GetCallable<>(); + Handle<Object> return_val = callable().ToHandleChecked(); + CHECK(return_val.is_identical_to(factory->undefined_value())); + } + + // Test passing one argument. + for (size_t i = 0; i < arraysize(create_args); i++) { + InterpreterTester tester(handles.main_isolate(), create_args[i].first); + auto callable = tester.GetCallable<Handle<Object>>(); + Handle<Object> return_val = + callable(handle(Smi::FromInt(40), isolate)).ToHandleChecked(); + if (create_args[i].second == 0) { + CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(40)); + } else { + CHECK(return_val.is_identical_to(factory->undefined_value())); + } + } + + // Test passing three argument. + for (size_t i = 0; i < arraysize(create_args); i++) { + Handle<Object> args[3] = { + handle(Smi::FromInt(40), isolate), + handle(Smi::FromInt(60), isolate), + handle(Smi::FromInt(80), isolate), + }; + + InterpreterTester tester(handles.main_isolate(), create_args[i].first); + auto callable = + tester.GetCallable<Handle<Object>, Handle<Object>, Handle<Object>>(); + Handle<Object> return_val = + callable(args[0], args[1], args[2]).ToHandleChecked(); + CHECK(return_val->SameValue(*args[create_args[i].second])); + } +} + + +TEST(InterpreterConditional) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + + std::pair<const char*, Handle<Object>> conditional[] = { + std::make_pair("return true ? 2 : 3;", + handle(Smi::FromInt(2), isolate)), + std::make_pair("return false ? 2 : 3;", + handle(Smi::FromInt(3), isolate)), + std::make_pair("var a = 1; return a ? 20 : 30;", + handle(Smi::FromInt(20), isolate)), + std::make_pair("var a = 1; return a ? 20 : 30;", + handle(Smi::FromInt(20), isolate)), + std::make_pair("var a = 'string'; return a ? 20 : 30;", + handle(Smi::FromInt(20), isolate)), + std::make_pair("var a = undefined; return a ? 20 : 30;", + handle(Smi::FromInt(30), isolate)), + std::make_pair("return 1 ? 2 ? 3 : 4 : 5;", + handle(Smi::FromInt(3), isolate)), + std::make_pair("return 0 ? 2 ? 3 : 4 : 5;", + handle(Smi::FromInt(5), isolate)), + }; + + for (size_t i = 0; i < arraysize(conditional); i++) { + std::string source(InterpreterTester::SourceForBody(conditional[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*conditional[i].second)); + } +} + + +TEST(InterpreterDelete) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + // Tests for delete for local variables that work both in strict + // and sloppy modes + std::pair<const char*, Handle<Object>> test_delete[] = { + std::make_pair( + "var a = { x:10, y:'abc', z:30.2}; delete a.x; return a.x;\n", + factory->undefined_value()), + std::make_pair( + "var b = { x:10, y:'abc', z:30.2}; delete b.x; return b.y;\n", + factory->NewStringFromStaticChars("abc")), + std::make_pair("var c = { x:10, y:'abc', z:30.2}; var d = c; delete d.x; " + "return c.x;\n", + factory->undefined_value()), + std::make_pair("var e = { x:10, y:'abc', z:30.2}; var g = e; delete g.x; " + "return e.y;\n", + factory->NewStringFromStaticChars("abc")), + std::make_pair("var a = { x:10, y:'abc', z:30.2};\n" + "var b = a;" + "delete b.x;" + "return b.x;\n", + factory->undefined_value()), + std::make_pair("var a = {1:10};\n" + "(function f1() {return a;});" + "return delete a[1];", + factory->ToBoolean(true)), + std::make_pair("return delete this;", factory->ToBoolean(true)), + std::make_pair("return delete 'test';", factory->ToBoolean(true))}; + + // Test delete in sloppy mode + for (size_t i = 0; i < arraysize(test_delete); i++) { + std::string source(InterpreterTester::SourceForBody(test_delete[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*test_delete[i].second)); + } + + // Test delete in strict mode + for (size_t i = 0; i < arraysize(test_delete); i++) { + std::string strict_test = + "'use strict'; " + std::string(test_delete[i].first); + std::string source(InterpreterTester::SourceForBody(strict_test.c_str())); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*test_delete[i].second)); + } +} + + +TEST(InterpreterDeleteSloppyUnqualifiedIdentifier) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + // These tests generate a syntax error for strict mode. We don't + // test for it here. + std::pair<const char*, Handle<Object>> test_delete[] = { + std::make_pair("var sloppy_a = { x:10, y:'abc'};\n" + "var sloppy_b = delete sloppy_a;\n" + "if (delete sloppy_a) {\n" + " return undefined;\n" + "} else {\n" + " return sloppy_a.x;\n" + "}\n", + Handle<Object>(Smi::FromInt(10), isolate)), + // TODO(mythria) When try-catch is implemented change the tests to check + // if delete actually deletes + std::make_pair("sloppy_a = { x:10, y:'abc'};\n" + "var sloppy_b = delete sloppy_a;\n" + // "try{return a.x;} catch(e) {return b;}\n" + "return sloppy_b;", + factory->ToBoolean(true)), + std::make_pair("sloppy_a = { x:10, y:'abc'};\n" + "var sloppy_b = delete sloppy_c;\n" + "return sloppy_b;", + factory->ToBoolean(true))}; + + for (size_t i = 0; i < arraysize(test_delete); i++) { + std::string source(InterpreterTester::SourceForBody(test_delete[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*test_delete[i].second)); + } +} + + +TEST(InterpreterGlobalDelete) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + std::pair<const char*, Handle<Object>> test_global_delete[] = { + std::make_pair("var a = { x:10, y:'abc', z:30.2 };\n" + "function f() {\n" + " delete a.x;\n" + " return a.x;\n" + "}\n" + "f();\n", + factory->undefined_value()), + std::make_pair("var b = {1:10, 2:'abc', 3:30.2 };\n" + "function f() {\n" + " delete b[2];\n" + " return b[1];\n" + " }\n" + "f();\n", + Handle<Object>(Smi::FromInt(10), isolate)), + std::make_pair("var c = { x:10, y:'abc', z:30.2 };\n" + "function f() {\n" + " var d = c;\n" + " delete d.y;\n" + " return d.x;\n" + "}\n" + "f();\n", + Handle<Object>(Smi::FromInt(10), isolate)), + std::make_pair("e = { x:10, y:'abc' };\n" + "function f() {\n" + " return delete e;\n" + "}\n" + "f();\n", + factory->ToBoolean(true)), + std::make_pair("var g = { x:10, y:'abc' };\n" + "function f() {\n" + " return delete g;\n" + "}\n" + "f();\n", + factory->ToBoolean(false)), + std::make_pair("function f() {\n" + " var obj = {h:10, f1() {return delete this;}};\n" + " return obj.f1();\n" + "}\n" + "f();", + factory->ToBoolean(true)), + std::make_pair("function f() {\n" + " var obj = {h:10,\n" + " f1() {\n" + " 'use strict';\n" + " return delete this.h;}};\n" + " return obj.f1();\n" + "}\n" + "f();", + factory->ToBoolean(true))}; + + for (size_t i = 0; i < arraysize(test_global_delete); i++) { + InterpreterTester tester(handles.main_isolate(), + test_global_delete[i].first); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*test_global_delete[i].second)); + } +} + + +TEST(InterpreterBasicLoops) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + std::pair<const char*, Handle<Object>> loops[] = { + std::make_pair("var a = 10; var b = 1;\n" + "while (a) {\n" + " b = b * 2;\n" + " a = a - 1;\n" + "};\n" + "return b;\n", + factory->NewHeapNumber(1024)), + std::make_pair("var a = 1; var b = 1;\n" + "do {\n" + " b = b * 2;\n" + " --a;\n" + "} while(a);\n" + "return b;\n", + handle(Smi::FromInt(2), isolate)), + std::make_pair("var b = 1;\n" + "for ( var a = 10; a; a--) {\n" + " b *= 2;\n" + "}\n" + "return b;", + factory->NewHeapNumber(1024)), + std::make_pair("var a = 10; var b = 1;\n" + "while (a > 0) {\n" + " b = b * 2;\n" + " a = a - 1;\n" + "};\n" + "return b;\n", + factory->NewHeapNumber(1024)), + std::make_pair("var a = 1; var b = 1;\n" + "do {\n" + " b = b * 2;\n" + " --a;\n" + "} while(a);\n" + "return b;\n", + handle(Smi::FromInt(2), isolate)), + std::make_pair("var b = 1;\n" + "for ( var a = 10; a > 0; a--) {\n" + " b *= 2;\n" + "}\n" + "return b;", + factory->NewHeapNumber(1024)), + std::make_pair("var a = 10; var b = 1;\n" + "while (false) {\n" + " b = b * 2;\n" + " a = a - 1;\n" + "}\n" + "return b;\n", + Handle<Object>(Smi::FromInt(1), isolate)), + std::make_pair("var a = 10; var b = 1;\n" + "while (true) {\n" + " b = b * 2;\n" + " a = a - 1;\n" + " if (a == 0) break;" + " continue;" + "}\n" + "return b;\n", + factory->NewHeapNumber(1024)), + std::make_pair("var a = 10; var b = 1;\n" + "do {\n" + " b = b * 2;\n" + " a = a - 1;\n" + " if (a == 0) break;" + "} while(true);\n" + "return b;\n", + factory->NewHeapNumber(1024)), + std::make_pair("var a = 10; var b = 1;\n" + "do {\n" + " b = b * 2;\n" + " a = a - 1;\n" + " if (a == 0) break;" + "} while(false);\n" + "return b;\n", + Handle<Object>(Smi::FromInt(2), isolate)), + std::make_pair("var a = 10; var b = 1;\n" + "for ( a = 1, b = 30; false; ) {\n" + " b = b * 2;\n" + "}\n" + "return b;\n", + Handle<Object>(Smi::FromInt(30), isolate))}; + + for (size_t i = 0; i < arraysize(loops); i++) { + std::string source(InterpreterTester::SourceForBody(loops[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*loops[i].second)); + } +} + + +TEST(InterpreterForIn) { + HandleAndZoneScope handles; + + // TODO(oth): Add a test here for delete mid-loop when delete is ready. + std::pair<const char*, int> for_in_samples[] = { + {"function f() {\n" + " var r = -1;\n" + " for (var a in null) { r = a; }\n" + " return r;\n" + "}", + -1}, + {"function f() {\n" + " var r = -1;\n" + " for (var a in undefined) { r = a; }\n" + " return r;\n" + "}", + -1}, + {"function f() {\n" + " var r = 0;\n" + " for (var a in [0,6,7,9]) { r = r + (1 << a); }\n" + " return r;\n" + "}", + 0xf}, + {"function f() {\n" + " var r = 0;\n" + " for (var a in [0,6,7,9]) { r = r + (1 << a); }\n" + " var r = 0;\n" + " for (var a in [0,6,7,9]) { r = r + (1 << a); }\n" + " return r;\n" + "}", + 0xf}, + {"function f() {\n" + " var r = 0;\n" + " for (var a in 'foobar') { r = r + (1 << a); }\n" + " return r;\n" + "}", + 0x3f}, + {"function f() {\n" + " var r = 0;\n" + " for (var a in {1:0, 10:1, 100:2, 1000:3}) {\n" + " r = r + Number(a);\n" + " }\n" + " return r;\n" + "}", + 1111}, + {"function f() {\n" + " var r = 0;\n" + " var data = {1:0, 10:1, 100:2, 1000:3};\n" + " for (var a in data) {\n" + " if (a == 1) delete data[1];\n" + " r = r + Number(a);\n" + " }\n" + " return r;\n" + "}", + 1111}, + {"function f() {\n" + " var r = 0;\n" + " var data = {1:0, 10:1, 100:2, 1000:3};\n" + " for (var a in data) {\n" + " if (a == 10) delete data[100];\n" + " r = r + Number(a);\n" + " }\n" + " return r;\n" + "}", + 1011}, + {"function f() {\n" + " var r = 0;\n" + " var data = {1:0, 10:1, 100:2, 1000:3};\n" + " for (var a in data) {\n" + " if (a == 10) data[10000] = 4;\n" + " r = r + Number(a);\n" + " }\n" + " return r;\n" + "}", + 1111}, + {"function f() {\n" + " var r = 0;\n" + " var input = 'foobar';\n" + " for (var a in input) {\n" + " if (input[a] == 'b') break;\n" + " r = r + (1 << a);\n" + " }\n" + " return r;\n" + "}", + 0x7}, + {"function f() {\n" + "var r = 0;\n" + "var input = 'foobar';\n" + "for (var a in input) {\n" + " if (input[a] == 'b') continue;\n" + " r = r + (1 << a);\n" + "}\n" + "return r;\n" + "}", + 0x37}, + {"function f() {\n" + " var r = 0;\n" + " var data = {1:0, 10:1, 100:2, 1000:3};\n" + " for (var a in data) {\n" + " if (a == 10) {\n" + " data[10000] = 4;\n" + " }\n" + " r = r + Number(a);\n" + " }\n" + " return r;\n" + "}", + 1111}, + {"function f() {\n" + " var r = [ 3 ];\n" + " var data = {1:0, 10:1, 100:2, 1000:3};\n" + " for (r[10] in data) {\n" + " }\n" + " return Number(r[10]);\n" + "}", + 1000}, + {"function f() {\n" + " var r = [ 3 ];\n" + " var data = {1:0, 10:1, 100:2, 1000:3};\n" + " for (r['100'] in data) {\n" + " }\n" + " return Number(r['100']);\n" + "}", + 1000}, + {"function f() {\n" + " var obj = {}\n" + " var descObj = new Boolean(false);\n" + " var accessed = 0;\n" + " descObj.enumerable = true;\n" + " Object.defineProperties(obj, { prop:descObj });\n" + " for (var p in obj) {\n" + " if (p === 'prop') { accessed = 1; }\n" + " }\n" + " return accessed;" + "}", + 1}, + {"function f() {\n" + " var appointment = {};\n" + " Object.defineProperty(appointment, 'startTime', {\n" + " value: 1001,\n" + " writable: false,\n" + " enumerable: false,\n" + " configurable: true\n" + " });\n" + " Object.defineProperty(appointment, 'name', {\n" + " value: 'NAME',\n" + " writable: false,\n" + " enumerable: false,\n" + " configurable: true\n" + " });\n" + " var meeting = Object.create(appointment);\n" + " Object.defineProperty(meeting, 'conferenceCall', {\n" + " value: 'In-person meeting',\n" + " writable: false,\n" + " enumerable: false,\n" + " configurable: true\n" + " });\n" + "\n" + " var teamMeeting = Object.create(meeting);\n" + "\n" + " var flags = 0;\n" + " for (var p in teamMeeting) {\n" + " if (p === 'startTime') {\n" + " flags |= 1;\n" + " }\n" + " if (p === 'name') {\n" + " flags |= 2;\n" + " }\n" + " if (p === 'conferenceCall') {\n" + " flags |= 4;\n" + " }\n" + " }\n" + "\n" + " var hasOwnProperty = !teamMeeting.hasOwnProperty('name') &&\n" + " !teamMeeting.hasOwnProperty('startTime') &&\n" + " !teamMeeting.hasOwnProperty('conferenceCall');\n" + " if (!hasOwnProperty) {\n" + " flags |= 8;\n" + " }\n" + " return flags;\n" + " }", + 0}}; + + for (size_t i = 0; i < arraysize(for_in_samples); i++) { + InterpreterTester tester(handles.main_isolate(), for_in_samples[i].first); + auto callable = tester.GetCallable<>(); + Handle<Object> return_val = callable().ToHandleChecked(); + CHECK_EQ(Handle<Smi>::cast(return_val)->value(), for_in_samples[i].second); + } +} + + +TEST(InterpreterSwitch) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + std::pair<const char*, Handle<Object>> switch_ops[] = { + std::make_pair("var a = 1;\n" + "switch(a) {\n" + " case 1: return 2;\n" + " case 2: return 3;\n" + "}\n", + handle(Smi::FromInt(2), isolate)), + std::make_pair("var a = 1;\n" + "switch(a) {\n" + " case 2: a = 2; break;\n" + " case 1: a = 3; break;\n" + "}\n" + "return a;", + handle(Smi::FromInt(3), isolate)), + std::make_pair("var a = 1;\n" + "switch(a) {\n" + " case 1: a = 2; // fall-through\n" + " case 2: a = 3; break;\n" + "}\n" + "return a;", + handle(Smi::FromInt(3), isolate)), + std::make_pair("var a = 100;\n" + "switch(a) {\n" + " case 1: return 100;\n" + " case 2: return 200;\n" + "}\n" + "return undefined;", + factory->undefined_value()), + std::make_pair("var a = 100;\n" + "switch(a) {\n" + " case 1: return 100;\n" + " case 2: return 200;\n" + " default: return 300;\n" + "}\n" + "return undefined;", + handle(Smi::FromInt(300), isolate)), + std::make_pair("var a = 100;\n" + "switch(typeof(a)) {\n" + " case 'string': return 1;\n" + " case 'number': return 2;\n" + " default: return 3;\n" + "}\n", + handle(Smi::FromInt(2), isolate)), + std::make_pair("var a = 100;\n" + "switch(a) {\n" + " case a += 20: return 1;\n" + " case a -= 10: return 2;\n" + " case a -= 10: return 3;\n" + " default: return 3;\n" + "}\n", + handle(Smi::FromInt(3), isolate)), + std::make_pair("var a = 1;\n" + "switch(a) {\n" + " case 1: \n" + " switch(a + 1) {\n" + " case 2 : a += 1; break;\n" + " default : a += 2; break;\n" + " } // fall-through\n" + " case 2: a += 3;\n" + "}\n" + "return a;", + handle(Smi::FromInt(5), isolate)), + }; + + for (size_t i = 0; i < arraysize(switch_ops); i++) { + std::string source(InterpreterTester::SourceForBody(switch_ops[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*switch_ops[i].second)); + } +} + + +TEST(InterpreterSloppyThis) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + std::pair<const char*, Handle<Object>> sloppy_this[] = { + std::make_pair("var global_val = 100;\n" + "function f() { return this.global_val; }\n", + handle(Smi::FromInt(100), isolate)), + std::make_pair("var global_val = 110;\n" + "function g() { return this.global_val; };" + "function f() { return g(); }\n", + handle(Smi::FromInt(110), isolate)), + std::make_pair("var global_val = 110;\n" + "function g() { return this.global_val };" + "function f() { 'use strict'; return g(); }\n", + handle(Smi::FromInt(110), isolate)), + std::make_pair("function f() { 'use strict'; return this; }\n", + factory->undefined_value()), + std::make_pair("function g() { 'use strict'; return this; };" + "function f() { return g(); }\n", + factory->undefined_value()), + }; + + for (size_t i = 0; i < arraysize(sloppy_this); i++) { + InterpreterTester tester(handles.main_isolate(), sloppy_this[i].first); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*sloppy_this[i].second)); + } +} + + +TEST(InterpreterThisFunction) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + InterpreterTester tester(handles.main_isolate(), + "var f;\n f = function f() { return f.name; }"); + auto callable = tester.GetCallable<>(); + + Handle<i::Object> return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*factory->NewStringFromStaticChars("f"))); +} + + +TEST(InterpreterNewTarget) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + // TODO(rmcilroy): Add tests that we get the original constructor for + // superclass constructors once we have class support. + InterpreterTester tester(handles.main_isolate(), + "function f() { this.a = new.target; }"); + auto callable = tester.GetCallable<>(); + callable().ToHandleChecked(); + + Handle<Object> new_target_name = v8::Utils::OpenHandle( + *CompileRun("(function() { return (new f()).a.name; })();")); + CHECK(new_target_name->SameValue(*factory->NewStringFromStaticChars("f"))); +} + +} // namespace interpreter +} // namespace internal +} // namespace v8 |