27e1ac1a79
Apple's upcoming arm64 devices will prevent rwx access to memory, but in turn provide a new per-thread way to switch between write and execute permissions. This patch puts that system to use for the WebAssembly subsystem. The approach relies on CodeSpaceWriteScope objects for now. That isn't optimal for background threads (which could stay in "write" mode permanently instead of toggling), but its simplicity makes it a good first step. Background: https://developer.apple.com/documentation/apple_silicon/porting_just-in-time_compilers_to_apple_silicon Bug: chromium:1117591 Change-Id: I3b60f0efd34c0fed924dfc71ee2c7805801c5d42 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2378307 Commit-Queue: Jakob Kummerow <jkummerow@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Reviewed-by: Thibaud Michaud <thibaudm@chromium.org> Cr-Commit-Position: refs/heads/master@{#69791}
3923 lines
144 KiB
C++
3923 lines
144 KiB
C++
// Copyright 2015 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <cmath>
|
|
|
|
#include "src/api/api-inl.h"
|
|
#include "src/base/utils/random-number-generator.h"
|
|
#include "src/builtins/builtins-promise-gen.h"
|
|
#include "src/builtins/builtins-promise.h"
|
|
#include "src/builtins/builtins-string-gen.h"
|
|
#include "src/codegen/code-factory.h"
|
|
#include "src/codegen/code-stub-assembler.h"
|
|
#include "src/compiler/node.h"
|
|
#include "src/debug/debug.h"
|
|
#include "src/execution/isolate.h"
|
|
#include "src/heap/heap-inl.h"
|
|
#include "src/numbers/hash-seed-inl.h"
|
|
#include "src/objects/hash-table-inl.h"
|
|
#include "src/objects/heap-number-inl.h"
|
|
#include "src/objects/js-array-buffer-inl.h"
|
|
#include "src/objects/js-array-inl.h"
|
|
#include "src/objects/objects-inl.h"
|
|
#include "src/objects/ordered-hash-table-inl.h"
|
|
#include "src/objects/promise-inl.h"
|
|
#include "src/objects/smi.h"
|
|
#include "src/objects/struct-inl.h"
|
|
#include "src/objects/transitions-inl.h"
|
|
#include "src/strings/char-predicates.h"
|
|
#include "test/cctest/compiler/code-assembler-tester.h"
|
|
#include "test/cctest/compiler/function-tester.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace compiler {
|
|
|
|
namespace {
|
|
|
|
using Label = CodeAssemblerLabel;
|
|
template <class T>
|
|
using TVariable = TypedCodeAssemblerVariable<T>;
|
|
using PromiseResolvingFunctions = TorqueStructPromiseResolvingFunctions;
|
|
|
|
intptr_t sum10(intptr_t a0, intptr_t a1, intptr_t a2, intptr_t a3, intptr_t a4,
|
|
intptr_t a5, intptr_t a6, intptr_t a7, intptr_t a8,
|
|
intptr_t a9) {
|
|
return a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9;
|
|
}
|
|
|
|
static int sum3(int a0, int a1, int a2) { return a0 + a1 + a2; }
|
|
|
|
} // namespace
|
|
|
|
TEST(CallCFunction) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 0;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
{
|
|
const TNode<ExternalReference> fun_constant = m.ExternalConstant(
|
|
ExternalReference::Create(reinterpret_cast<Address>(sum10)));
|
|
|
|
MachineType type_intptr = MachineType::IntPtr();
|
|
|
|
Node* const result =
|
|
m.CallCFunction(fun_constant, type_intptr,
|
|
std::make_pair(type_intptr, m.IntPtrConstant(0)),
|
|
std::make_pair(type_intptr, m.IntPtrConstant(1)),
|
|
std::make_pair(type_intptr, m.IntPtrConstant(2)),
|
|
std::make_pair(type_intptr, m.IntPtrConstant(3)),
|
|
std::make_pair(type_intptr, m.IntPtrConstant(4)),
|
|
std::make_pair(type_intptr, m.IntPtrConstant(5)),
|
|
std::make_pair(type_intptr, m.IntPtrConstant(6)),
|
|
std::make_pair(type_intptr, m.IntPtrConstant(7)),
|
|
std::make_pair(type_intptr, m.IntPtrConstant(8)),
|
|
std::make_pair(type_intptr, m.IntPtrConstant(9)));
|
|
m.Return(m.SmiTag(result));
|
|
}
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<Object> result = ft.Call().ToHandleChecked();
|
|
CHECK_EQ(45, Handle<Smi>::cast(result)->value());
|
|
}
|
|
|
|
TEST(CallCFunctionWithCallerSavedRegisters) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 0;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
{
|
|
const TNode<ExternalReference> fun_constant = m.ExternalConstant(
|
|
ExternalReference::Create(reinterpret_cast<Address>(sum3)));
|
|
|
|
MachineType type_intptr = MachineType::IntPtr();
|
|
|
|
Node* const result = m.CallCFunctionWithCallerSavedRegisters(
|
|
fun_constant, type_intptr, kSaveFPRegs,
|
|
std::make_pair(type_intptr, m.IntPtrConstant(0)),
|
|
std::make_pair(type_intptr, m.IntPtrConstant(1)),
|
|
std::make_pair(type_intptr, m.IntPtrConstant(2)));
|
|
m.Return(m.SmiTag(result));
|
|
}
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<Object> result = ft.Call().ToHandleChecked();
|
|
CHECK_EQ(3, Handle<Smi>::cast(result)->value());
|
|
}
|
|
|
|
TEST(NumberToString) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
Factory* factory = isolate->factory();
|
|
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
{
|
|
TNode<Number> input = m.CAST(m.Parameter(1));
|
|
|
|
Label bailout(&m);
|
|
m.Return(m.NumberToString(input, &bailout));
|
|
|
|
m.BIND(&bailout);
|
|
m.Return(m.UndefinedConstant());
|
|
}
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
// clang-format off
|
|
double inputs[] = {
|
|
1, 2, 42, 153, -1, -100, 0, 51095154, -1241950,
|
|
std::nan("-1"), std::nan("1"), std::nan("2"),
|
|
-std::numeric_limits<double>::infinity(),
|
|
std::numeric_limits<double>::infinity(),
|
|
-0.0, -0.001, -0.5, -0.999, -1.0,
|
|
0.0, 0.001, 0.5, 0.999, 1.0,
|
|
-2147483647.9, -2147483648.0, -2147483648.5, -2147483648.9, // SmiMin.
|
|
2147483646.9, 2147483647.0, 2147483647.5, 2147483647.9, // SmiMax.
|
|
-4294967295.9, -4294967296.0, -4294967296.5, -4294967297.0, // - 2^32.
|
|
4294967295.9, 4294967296.0, 4294967296.5, 4294967297.0, // 2^32.
|
|
};
|
|
// clang-format on
|
|
|
|
const int kFullCacheSize = isolate->heap()->MaxNumberToStringCacheSize();
|
|
const int test_count = arraysize(inputs);
|
|
for (int i = 0; i < test_count; i++) {
|
|
int cache_length_before_addition = factory->number_string_cache()->length();
|
|
Handle<Object> input = factory->NewNumber(inputs[i]);
|
|
Handle<String> expected = factory->NumberToString(input);
|
|
|
|
Handle<Object> result = ft.Call(input).ToHandleChecked();
|
|
if (result->IsUndefined(isolate)) {
|
|
// Query may fail if cache was resized, in which case the entry is not
|
|
// added to the cache.
|
|
CHECK_LT(cache_length_before_addition, kFullCacheSize);
|
|
CHECK_EQ(factory->number_string_cache()->length(), kFullCacheSize);
|
|
expected = factory->NumberToString(input);
|
|
result = ft.Call(input).ToHandleChecked();
|
|
}
|
|
CHECK(!result->IsUndefined(isolate));
|
|
CHECK_EQ(*expected, *result);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
void CheckToUint32Result(uint32_t expected, Handle<Object> result) {
|
|
const int64_t result_int64 = NumberToInt64(*result);
|
|
const uint32_t result_uint32 = NumberToUint32(*result);
|
|
|
|
CHECK_EQ(static_cast<int64_t>(result_uint32), result_int64);
|
|
CHECK_EQ(expected, result_uint32);
|
|
|
|
// Ensure that the result is normalized to a Smi, i.e. a HeapNumber is only
|
|
// returned if the result is not within Smi range.
|
|
const bool expected_fits_into_intptr =
|
|
static_cast<int64_t>(expected) <=
|
|
static_cast<int64_t>(std::numeric_limits<intptr_t>::max());
|
|
if (expected_fits_into_intptr &&
|
|
Smi::IsValid(static_cast<intptr_t>(expected))) {
|
|
CHECK(result->IsSmi());
|
|
} else {
|
|
CHECK(result->IsHeapNumber());
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST(ToUint32) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
Factory* factory = isolate->factory();
|
|
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
const int kContextOffset = 3;
|
|
Node* const context = m.Parameter(kNumParams + kContextOffset);
|
|
Node* const input = m.Parameter(1);
|
|
m.Return(m.ToUint32(context, input));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
// clang-format off
|
|
double inputs[] = {
|
|
std::nan("-1"), std::nan("1"), std::nan("2"),
|
|
-std::numeric_limits<double>::infinity(),
|
|
std::numeric_limits<double>::infinity(),
|
|
-0.0, -0.001, -0.5, -0.999, -1.0,
|
|
0.0, 0.001, 0.5, 0.999, 1.0,
|
|
-2147483647.9, -2147483648.0, -2147483648.5, -2147483648.9, // SmiMin.
|
|
2147483646.9, 2147483647.0, 2147483647.5, 2147483647.9, // SmiMax.
|
|
-4294967295.9, -4294967296.0, -4294967296.5, -4294967297.0, // - 2^32.
|
|
4294967295.9, 4294967296.0, 4294967296.5, 4294967297.0, // 2^32.
|
|
};
|
|
|
|
uint32_t expectations[] = {
|
|
0, 0, 0,
|
|
0,
|
|
0,
|
|
0, 0, 0, 0, 4294967295,
|
|
0, 0, 0, 0, 1,
|
|
2147483649, 2147483648, 2147483648, 2147483648,
|
|
2147483646, 2147483647, 2147483647, 2147483647,
|
|
1, 0, 0, 4294967295,
|
|
4294967295, 0, 0, 1,
|
|
};
|
|
// clang-format on
|
|
|
|
STATIC_ASSERT(arraysize(inputs) == arraysize(expectations));
|
|
|
|
const int test_count = arraysize(inputs);
|
|
for (int i = 0; i < test_count; i++) {
|
|
Handle<Object> input_obj = factory->NewNumber(inputs[i]);
|
|
Handle<HeapNumber> input_num;
|
|
|
|
// Check with Smi input.
|
|
if (input_obj->IsSmi()) {
|
|
Handle<Smi> input_smi = Handle<Smi>::cast(input_obj);
|
|
Handle<Object> result = ft.Call(input_smi).ToHandleChecked();
|
|
CheckToUint32Result(expectations[i], result);
|
|
input_num = factory->NewHeapNumber(inputs[i]);
|
|
} else {
|
|
input_num = Handle<HeapNumber>::cast(input_obj);
|
|
}
|
|
|
|
// Check with HeapNumber input.
|
|
{
|
|
CHECK(input_num->IsHeapNumber());
|
|
Handle<Object> result = ft.Call(input_num).ToHandleChecked();
|
|
CheckToUint32Result(expectations[i], result);
|
|
}
|
|
}
|
|
|
|
// A couple of final cases for ToNumber conversions.
|
|
CheckToUint32Result(0, ft.Call(factory->undefined_value()).ToHandleChecked());
|
|
CheckToUint32Result(0, ft.Call(factory->null_value()).ToHandleChecked());
|
|
CheckToUint32Result(0, ft.Call(factory->false_value()).ToHandleChecked());
|
|
CheckToUint32Result(1, ft.Call(factory->true_value()).ToHandleChecked());
|
|
CheckToUint32Result(
|
|
42,
|
|
ft.Call(factory->NewStringFromAsciiChecked("0x2A")).ToHandleChecked());
|
|
|
|
ft.CheckThrows(factory->match_symbol());
|
|
}
|
|
|
|
namespace {
|
|
void IsValidPositiveSmiCase(Isolate* isolate, intptr_t value) {
|
|
const int kNumParams = 0;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
|
|
CodeStubAssembler m(asm_tester.state());
|
|
m.Return(
|
|
m.SelectBooleanConstant(m.IsValidPositiveSmi(m.IntPtrConstant(value))));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
MaybeHandle<Object> maybe_handle = ft.Call();
|
|
|
|
bool expected = i::PlatformSmiTagging::IsValidSmi(value) && (value >= 0);
|
|
if (expected) {
|
|
CHECK(maybe_handle.ToHandleChecked()->IsTrue(isolate));
|
|
} else {
|
|
CHECK(maybe_handle.ToHandleChecked()->IsFalse(isolate));
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
TEST(IsValidPositiveSmi) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
IsValidPositiveSmiCase(isolate, -1);
|
|
IsValidPositiveSmiCase(isolate, 0);
|
|
IsValidPositiveSmiCase(isolate, 1);
|
|
|
|
IsValidPositiveSmiCase(isolate, 0x3FFFFFFFU);
|
|
IsValidPositiveSmiCase(isolate, 0xC0000000U);
|
|
IsValidPositiveSmiCase(isolate, 0x40000000U);
|
|
IsValidPositiveSmiCase(isolate, 0xBFFFFFFFU);
|
|
|
|
using int32_limits = std::numeric_limits<int32_t>;
|
|
IsValidPositiveSmiCase(isolate, int32_limits::max());
|
|
IsValidPositiveSmiCase(isolate, int32_limits::min());
|
|
#if V8_TARGET_ARCH_64_BIT
|
|
IsValidPositiveSmiCase(isolate,
|
|
static_cast<intptr_t>(int32_limits::max()) + 1);
|
|
IsValidPositiveSmiCase(isolate,
|
|
static_cast<intptr_t>(int32_limits::min()) - 1);
|
|
#endif
|
|
}
|
|
|
|
TEST(ConvertToRelativeIndex) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 3;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
enum Result { kFound, kNotFound };
|
|
{
|
|
TNode<Number> index = m.CAST(m.Parameter(1));
|
|
TNode<Number> length_number = m.CAST(m.Parameter(2));
|
|
TNode<Number> expected_relative_index = m.CAST(m.Parameter(3));
|
|
|
|
TNode<UintPtrT> length = m.ChangeUintPtrNumberToUintPtr(length_number);
|
|
TNode<UintPtrT> expected =
|
|
m.ChangeUintPtrNumberToUintPtr(expected_relative_index);
|
|
|
|
TNode<UintPtrT> result = m.ConvertToRelativeIndex(index, length);
|
|
|
|
m.Return(m.SelectBooleanConstant(m.WordEqual(result, expected)));
|
|
}
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
const double kMaxSmi = static_cast<double>(kSmiMaxValue);
|
|
const double kMaxInt32 =
|
|
static_cast<double>(std::numeric_limits<int32_t>::max());
|
|
const double kMaxUInt32 =
|
|
static_cast<double>(std::numeric_limits<uint32_t>::max());
|
|
const double kMaxUIntPtr =
|
|
static_cast<double>(std::numeric_limits<uintptr_t>::max());
|
|
|
|
struct {
|
|
double index;
|
|
double length;
|
|
double expected_result;
|
|
} test_cases[] = {
|
|
// Simple Smi-range cases.
|
|
{0, 0, 0},
|
|
{0, 42, 0},
|
|
{5, 42, 5},
|
|
{100, 42, 42},
|
|
{-10, 153, 153 - 10},
|
|
{-200, 153, 0},
|
|
// Beyond Smi-range index cases.
|
|
{0, kMaxSmi, 0},
|
|
{-153, kMaxSmi, kMaxSmi - 153},
|
|
{kMaxSmi + 153, kMaxSmi, kMaxSmi},
|
|
{kMaxSmi * 33, kMaxSmi, kMaxSmi},
|
|
{-kMaxSmi, kMaxSmi, 0},
|
|
{-kMaxSmi - 1, kMaxSmi, 0},
|
|
{-kMaxSmi - 153, kMaxSmi, 0},
|
|
{-kMaxSmi * 33, kMaxSmi, 0},
|
|
{-std::numeric_limits<double>::infinity(), 153, 0},
|
|
{std::numeric_limits<double>::infinity(), 424242, 424242},
|
|
// Beyond Smi-range length cases.
|
|
{kMaxSmi + 2, kMaxSmi + 1, kMaxSmi + 1},
|
|
{-kMaxSmi + 2, kMaxSmi + 1, 3},
|
|
{kMaxInt32 + 1, kMaxInt32, kMaxInt32},
|
|
{-kMaxInt32 + 1, kMaxInt32, 1},
|
|
{kMaxUInt32 + 1, kMaxUInt32, kMaxUInt32},
|
|
{-42, kMaxUInt32, kMaxUInt32 - 42},
|
|
{-kMaxUInt32 - 1, kMaxUInt32, 0},
|
|
{-kMaxUInt32, kMaxUInt32, 0},
|
|
{-kMaxUInt32 + 1, kMaxUInt32, 1},
|
|
{-kMaxUInt32 + 5, kMaxUInt32, 5},
|
|
{-kMaxUInt32 + 5, kMaxUInt32 + 1, 6},
|
|
{-kMaxSmi * 33, kMaxSmi * 153, kMaxSmi * (153 - 33)},
|
|
{0, kMaxSafeInteger, 0},
|
|
{kMaxSmi, kMaxSafeInteger, kMaxSmi},
|
|
{kMaxSmi * 153, kMaxSafeInteger, kMaxSmi * 153},
|
|
{-10, kMaxSafeInteger, kMaxSafeInteger - 10},
|
|
{-kMaxSafeInteger, kMaxSafeInteger, 0},
|
|
{-kMaxSafeInteger + 1, kMaxSafeInteger, 1},
|
|
{-kMaxSafeInteger + 42, kMaxSafeInteger, 42},
|
|
{kMaxSafeInteger - 153, kMaxSafeInteger, kMaxSafeInteger - 153},
|
|
{kMaxSafeInteger - 1, kMaxSafeInteger, kMaxSafeInteger - 1},
|
|
{kMaxSafeInteger, kMaxSafeInteger, kMaxSafeInteger},
|
|
{kMaxSafeInteger + 1, kMaxSafeInteger, kMaxSafeInteger},
|
|
{kMaxSafeInteger + 42, kMaxSafeInteger, kMaxSafeInteger},
|
|
{kMaxSafeInteger * 11, kMaxSafeInteger, kMaxSafeInteger},
|
|
};
|
|
|
|
Factory* factory = isolate->factory();
|
|
for (size_t i = 0; i < arraysize(test_cases); i++) {
|
|
if (test_cases[i].length > kMaxUIntPtr) {
|
|
// Test cases where length does not fit into uintptr are not valid, so
|
|
// skip them instead of ifdef'ing the test cases above.
|
|
continue;
|
|
}
|
|
Handle<Object> index = factory->NewNumber(test_cases[i].index);
|
|
Handle<Object> length = factory->NewNumber(test_cases[i].length);
|
|
Handle<Object> expected = factory->NewNumber(test_cases[i].expected_result);
|
|
|
|
ft.CheckTrue(index, length, expected);
|
|
}
|
|
}
|
|
|
|
TEST(FixedArrayAccessSmiIndex) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
CodeAssemblerTester asm_tester(isolate);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
Handle<FixedArray> array = isolate->factory()->NewFixedArray(5);
|
|
array->set(4, Smi::FromInt(733));
|
|
m.Return(m.LoadFixedArrayElement(m.HeapConstant(array),
|
|
m.SmiTag(m.IntPtrConstant(4)), 0));
|
|
FunctionTester ft(asm_tester.GenerateCode());
|
|
MaybeHandle<Object> result = ft.Call();
|
|
CHECK_EQ(733, Handle<Smi>::cast(result.ToHandleChecked())->value());
|
|
}
|
|
|
|
TEST(LoadHeapNumberValue) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
CodeAssemblerTester asm_tester(isolate);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
Handle<HeapNumber> number = isolate->factory()->NewHeapNumber(1234);
|
|
m.Return(m.SmiFromInt32(m.Signed(
|
|
m.ChangeFloat64ToUint32(m.LoadHeapNumberValue(m.HeapConstant(number))))));
|
|
FunctionTester ft(asm_tester.GenerateCode());
|
|
MaybeHandle<Object> result = ft.Call();
|
|
CHECK_EQ(1234, Handle<Smi>::cast(result.ToHandleChecked())->value());
|
|
}
|
|
|
|
TEST(LoadInstanceType) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
CodeAssemblerTester asm_tester(isolate);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
Handle<HeapObject> undefined = isolate->factory()->undefined_value();
|
|
m.Return(m.SmiFromInt32(m.LoadInstanceType(m.HeapConstant(undefined))));
|
|
FunctionTester ft(asm_tester.GenerateCode());
|
|
MaybeHandle<Object> result = ft.Call();
|
|
CHECK_EQ(InstanceType::ODDBALL_TYPE,
|
|
Handle<Smi>::cast(result.ToHandleChecked())->value());
|
|
}
|
|
|
|
TEST(DecodeWordFromWord32) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
CodeAssemblerTester asm_tester(isolate);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
using TestBitField = base::BitField<unsigned, 3, 3>;
|
|
m.Return(m.SmiTag(
|
|
m.Signed(m.DecodeWordFromWord32<TestBitField>(m.Int32Constant(0x2F)))));
|
|
FunctionTester ft(asm_tester.GenerateCode());
|
|
MaybeHandle<Object> result = ft.Call();
|
|
// value = 00101111
|
|
// mask = 00111000
|
|
// result = 101
|
|
CHECK_EQ(5, Handle<Smi>::cast(result.ToHandleChecked())->value());
|
|
}
|
|
|
|
TEST(JSFunction) {
|
|
const int kNumParams = 2; // left, right.
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
m.Return(m.SmiFromInt32(
|
|
m.Int32Add(m.SmiToInt32(m.Parameter(1)), m.SmiToInt32(m.Parameter(2)))));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
MaybeHandle<Object> result = ft.Call(handle(Smi::FromInt(23), isolate),
|
|
handle(Smi::FromInt(34), isolate));
|
|
CHECK_EQ(57, Handle<Smi>::cast(result.ToHandleChecked())->value());
|
|
}
|
|
|
|
TEST(ComputeIntegerHash) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
m.Return(m.SmiFromInt32(m.UncheckedCast<Int32T>(
|
|
m.ComputeSeededHash(m.SmiUntag(m.Parameter(1))))));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
base::RandomNumberGenerator rand_gen(FLAG_random_seed);
|
|
|
|
for (int i = 0; i < 1024; i++) {
|
|
int k = rand_gen.NextInt(Smi::kMaxValue);
|
|
|
|
Handle<Smi> key(Smi::FromInt(k), isolate);
|
|
Handle<Object> result = ft.Call(key).ToHandleChecked();
|
|
|
|
uint32_t hash = ComputeSeededHash(k, HashSeed(isolate));
|
|
Smi expected = Smi::FromInt(hash);
|
|
CHECK_EQ(expected, Smi::cast(*result));
|
|
}
|
|
}
|
|
|
|
TEST(ToString) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
m.Return(m.ToStringImpl(m.CAST(m.Parameter(kNumParams + 3)),
|
|
m.CAST(m.Parameter(1))));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<FixedArray> test_cases = isolate->factory()->NewFixedArray(5);
|
|
Handle<FixedArray> smi_test = isolate->factory()->NewFixedArray(2);
|
|
smi_test->set(0, Smi::FromInt(42));
|
|
Handle<String> str(isolate->factory()->InternalizeUtf8String("42"));
|
|
smi_test->set(1, *str);
|
|
test_cases->set(0, *smi_test);
|
|
|
|
Handle<FixedArray> number_test = isolate->factory()->NewFixedArray(2);
|
|
Handle<HeapNumber> num(isolate->factory()->NewHeapNumber(3.14));
|
|
number_test->set(0, *num);
|
|
str = isolate->factory()->InternalizeUtf8String("3.14");
|
|
number_test->set(1, *str);
|
|
test_cases->set(1, *number_test);
|
|
|
|
Handle<FixedArray> string_test = isolate->factory()->NewFixedArray(2);
|
|
str = isolate->factory()->InternalizeUtf8String("test");
|
|
string_test->set(0, *str);
|
|
string_test->set(1, *str);
|
|
test_cases->set(2, *string_test);
|
|
|
|
Handle<FixedArray> oddball_test = isolate->factory()->NewFixedArray(2);
|
|
oddball_test->set(0, ReadOnlyRoots(isolate).undefined_value());
|
|
str = isolate->factory()->InternalizeUtf8String("undefined");
|
|
oddball_test->set(1, *str);
|
|
test_cases->set(3, *oddball_test);
|
|
|
|
Handle<FixedArray> tostring_test = isolate->factory()->NewFixedArray(2);
|
|
Handle<FixedArray> js_array_storage = isolate->factory()->NewFixedArray(2);
|
|
js_array_storage->set(0, Smi::FromInt(1));
|
|
js_array_storage->set(1, Smi::FromInt(2));
|
|
Handle<JSArray> js_array = isolate->factory()->NewJSArray(2);
|
|
JSArray::SetContent(js_array, js_array_storage);
|
|
tostring_test->set(0, *js_array);
|
|
str = isolate->factory()->InternalizeUtf8String("1,2");
|
|
tostring_test->set(1, *str);
|
|
test_cases->set(4, *tostring_test);
|
|
|
|
for (int i = 0; i < 5; ++i) {
|
|
Handle<FixedArray> test =
|
|
handle(FixedArray::cast(test_cases->get(i)), isolate);
|
|
Handle<Object> obj = handle(test->get(0), isolate);
|
|
Handle<String> expected = handle(String::cast(test->get(1)), isolate);
|
|
Handle<Object> result = ft.Call(obj).ToHandleChecked();
|
|
CHECK(result->IsString());
|
|
CHECK(String::Equals(isolate, Handle<String>::cast(result), expected));
|
|
}
|
|
}
|
|
|
|
TEST(TryToName) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 3;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
enum Result { kKeyIsIndex, kKeyIsUnique, kBailout };
|
|
{
|
|
Node* key = m.Parameter(1);
|
|
TNode<MaybeObject> expected_result =
|
|
m.UncheckedCast<MaybeObject>(m.Parameter(2));
|
|
TNode<Object> expected_arg = m.CAST(m.Parameter(3));
|
|
|
|
Label passed(&m), failed(&m);
|
|
Label if_keyisindex(&m), if_keyisunique(&m), if_bailout(&m);
|
|
{
|
|
TYPED_VARIABLE_DEF(IntPtrT, var_index, &m);
|
|
TYPED_VARIABLE_DEF(Name, var_unique, &m);
|
|
TYPED_VARIABLE_DEF(IntPtrT, var_expected, &m);
|
|
|
|
m.TryToName(key, &if_keyisindex, &var_index, &if_keyisunique, &var_unique,
|
|
&if_bailout);
|
|
|
|
m.BIND(&if_keyisindex);
|
|
m.GotoIfNot(m.TaggedEqual(expected_result,
|
|
m.SmiConstant(Smi::FromInt(kKeyIsIndex))),
|
|
&failed);
|
|
|
|
Label if_expectedissmi(&m), if_expectedisheapnumber(&m), check_result(&m);
|
|
m.Branch(m.TaggedIsSmi(expected_arg), &if_expectedissmi,
|
|
&if_expectedisheapnumber);
|
|
|
|
m.BIND(&if_expectedissmi);
|
|
var_expected = m.SmiUntag(m.CAST(expected_arg));
|
|
m.Goto(&check_result);
|
|
|
|
m.BIND(&if_expectedisheapnumber);
|
|
CSA_ASSERT(&m, m.IsHeapNumber(m.CAST(expected_arg)));
|
|
TNode<Float64T> value = m.LoadHeapNumberValue(m.CAST(expected_arg));
|
|
// We know this to be safe as all expected values are in intptr
|
|
// range.
|
|
var_expected = m.UncheckedCast<IntPtrT>(m.ChangeFloat64ToUintPtr(value));
|
|
m.Goto(&check_result);
|
|
|
|
m.BIND(&check_result);
|
|
m.Branch(m.IntPtrEqual(var_expected.value(), var_index.value()), &passed,
|
|
&failed);
|
|
|
|
m.BIND(&if_keyisunique);
|
|
m.GotoIfNot(m.TaggedEqual(expected_result,
|
|
m.SmiConstant(Smi::FromInt(kKeyIsUnique))),
|
|
&failed);
|
|
m.Branch(m.TaggedEqual(expected_arg, var_unique.value()), &passed,
|
|
&failed);
|
|
}
|
|
|
|
m.BIND(&if_bailout);
|
|
m.Branch(
|
|
m.TaggedEqual(expected_result, m.SmiConstant(Smi::FromInt(kBailout))),
|
|
&passed, &failed);
|
|
|
|
m.BIND(&passed);
|
|
m.Return(m.BooleanConstant(true));
|
|
|
|
m.BIND(&failed);
|
|
m.Return(m.BooleanConstant(false));
|
|
}
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<Object> expect_index(Smi::FromInt(kKeyIsIndex), isolate);
|
|
Handle<Object> expect_unique(Smi::FromInt(kKeyIsUnique), isolate);
|
|
Handle<Object> expect_bailout(Smi::FromInt(kBailout), isolate);
|
|
|
|
{
|
|
// TryToName(<zero smi>) => if_keyisindex: smi value.
|
|
Handle<Object> key(Smi::zero(), isolate);
|
|
ft.CheckTrue(key, expect_index, key);
|
|
}
|
|
|
|
{
|
|
// TryToName(<positive smi>) => if_keyisindex: smi value.
|
|
Handle<Object> key(Smi::FromInt(153), isolate);
|
|
ft.CheckTrue(key, expect_index, key);
|
|
}
|
|
|
|
{
|
|
// TryToName(<negative smi>) => if_keyisindex: smi value.
|
|
// A subsequent bounds check needs to take care of this case.
|
|
Handle<Object> key(Smi::FromInt(-1), isolate);
|
|
ft.CheckTrue(key, expect_index, key);
|
|
}
|
|
|
|
{
|
|
// TryToName(<heap number with int value>) => if_keyisindex: number.
|
|
Handle<Object> key(isolate->factory()->NewHeapNumber(153));
|
|
Handle<Object> index(Smi::FromInt(153), isolate);
|
|
ft.CheckTrue(key, expect_index, index);
|
|
}
|
|
|
|
{
|
|
// TryToName(<true>) => if_keyisunique: "true".
|
|
Handle<Object> key = isolate->factory()->true_value();
|
|
Handle<Object> unique = isolate->factory()->InternalizeUtf8String("true");
|
|
ft.CheckTrue(key, expect_unique, unique);
|
|
}
|
|
|
|
{
|
|
// TryToName(<false>) => if_keyisunique: "false".
|
|
Handle<Object> key = isolate->factory()->false_value();
|
|
Handle<Object> unique = isolate->factory()->InternalizeUtf8String("false");
|
|
ft.CheckTrue(key, expect_unique, unique);
|
|
}
|
|
|
|
{
|
|
// TryToName(<null>) => if_keyisunique: "null".
|
|
Handle<Object> key = isolate->factory()->null_value();
|
|
Handle<Object> unique = isolate->factory()->InternalizeUtf8String("null");
|
|
ft.CheckTrue(key, expect_unique, unique);
|
|
}
|
|
|
|
{
|
|
// TryToName(<undefined>) => if_keyisunique: "undefined".
|
|
Handle<Object> key = isolate->factory()->undefined_value();
|
|
Handle<Object> unique =
|
|
isolate->factory()->InternalizeUtf8String("undefined");
|
|
ft.CheckTrue(key, expect_unique, unique);
|
|
}
|
|
|
|
{
|
|
// TryToName(<symbol>) => if_keyisunique: <symbol>.
|
|
Handle<Object> key = isolate->factory()->NewSymbol();
|
|
ft.CheckTrue(key, expect_unique, key);
|
|
}
|
|
|
|
{
|
|
// TryToName(<internalized string>) => if_keyisunique: <internalized string>
|
|
Handle<Object> key = isolate->factory()->InternalizeUtf8String("test");
|
|
ft.CheckTrue(key, expect_unique, key);
|
|
}
|
|
|
|
{
|
|
// TryToName(<internalized number string>) => if_keyisindex: number.
|
|
Handle<Object> key = isolate->factory()->InternalizeUtf8String("153");
|
|
Handle<Object> index(Smi::FromInt(153), isolate);
|
|
ft.CheckTrue(key, expect_index, index);
|
|
}
|
|
|
|
{
|
|
// TryToName(<internalized uncacheable number string greater than
|
|
// array index but less than MAX_SAFE_INTEGER>) => 32-bit platforms
|
|
// take the if_keyisunique path, 64-bit platforms bail out because they
|
|
// let the runtime handle the string-to-size_t parsing.
|
|
Handle<Object> key =
|
|
isolate->factory()->InternalizeUtf8String("4294967296");
|
|
#if V8_TARGET_ARCH_64_BIT
|
|
ft.CheckTrue(key, expect_bailout);
|
|
#else
|
|
ft.CheckTrue(key, expect_unique, key);
|
|
#endif
|
|
}
|
|
|
|
{
|
|
// TryToName(<internalized uncacheable number string greater than
|
|
// INT_MAX but less than array index>) => bailout.
|
|
Handle<Object> key =
|
|
isolate->factory()->InternalizeUtf8String("4294967294");
|
|
ft.CheckTrue(key, expect_bailout);
|
|
}
|
|
|
|
{
|
|
// TryToName(<internalized uncacheable number string less than
|
|
// INT_MAX>) => bailout
|
|
Handle<Object> key =
|
|
isolate->factory()->InternalizeUtf8String("2147483647");
|
|
ft.CheckTrue(key, expect_bailout);
|
|
}
|
|
|
|
{
|
|
// TryToName(<non-internalized number string>) => if_keyisindex: number.
|
|
Handle<String> key = isolate->factory()->NewStringFromAsciiChecked("153");
|
|
uint32_t dummy;
|
|
CHECK(key->AsArrayIndex(&dummy));
|
|
CHECK(key->HasHashCode());
|
|
CHECK(!key->IsInternalizedString());
|
|
Handle<Object> index(Smi::FromInt(153), isolate);
|
|
ft.CheckTrue(key, expect_index, index);
|
|
}
|
|
|
|
{
|
|
// TryToName(<number string without cached index>) => is_keyisindex: number.
|
|
Handle<String> key = isolate->factory()->NewStringFromAsciiChecked("153");
|
|
CHECK(!key->HasHashCode());
|
|
ft.CheckTrue(key, expect_bailout);
|
|
}
|
|
|
|
{
|
|
// TryToName(<non-internalized string>) => bailout.
|
|
Handle<Object> key = isolate->factory()->NewStringFromAsciiChecked("test");
|
|
ft.CheckTrue(key, expect_bailout);
|
|
}
|
|
|
|
if (FLAG_thin_strings) {
|
|
// TryToName(<thin string>) => internalized version.
|
|
Handle<String> s = isolate->factory()->NewStringFromAsciiChecked("foo");
|
|
Handle<String> internalized = isolate->factory()->InternalizeString(s);
|
|
ft.CheckTrue(s, expect_unique, internalized);
|
|
}
|
|
|
|
if (FLAG_thin_strings) {
|
|
// TryToName(<thin two-byte string>) => internalized version.
|
|
uc16 array1[] = {2001, 2002, 2003};
|
|
Handle<String> s = isolate->factory()
|
|
->NewStringFromTwoByte(ArrayVector(array1))
|
|
.ToHandleChecked();
|
|
Handle<String> internalized = isolate->factory()->InternalizeString(s);
|
|
ft.CheckTrue(s, expect_unique, internalized);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
template <typename Dictionary>
|
|
void TestEntryToIndex() {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
{
|
|
TNode<IntPtrT> entry = m.SmiUntag(m.Parameter(1));
|
|
TNode<IntPtrT> result = m.EntryToIndex<Dictionary>(entry);
|
|
m.Return(m.SmiTag(result));
|
|
}
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
// Test a wide range of entries but staying linear in the first 100 entries.
|
|
for (int entry = 0; entry < Dictionary::kMaxCapacity;
|
|
entry = entry * 1.01 + 1) {
|
|
Handle<Object> result =
|
|
ft.Call(handle(Smi::FromInt(entry), isolate)).ToHandleChecked();
|
|
CHECK_EQ(Dictionary::EntryToIndex(InternalIndex(entry)),
|
|
Smi::ToInt(*result));
|
|
}
|
|
}
|
|
|
|
TEST(NameDictionaryEntryToIndex) { TestEntryToIndex<NameDictionary>(); }
|
|
TEST(GlobalDictionaryEntryToIndex) { TestEntryToIndex<GlobalDictionary>(); }
|
|
|
|
} // namespace
|
|
|
|
namespace {
|
|
|
|
template <typename Dictionary>
|
|
void TestNameDictionaryLookup() {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 4;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
enum Result { kFound, kNotFound };
|
|
{
|
|
TNode<Dictionary> dictionary = m.CAST(m.Parameter(1));
|
|
TNode<Name> unique_name = m.CAST(m.Parameter(2));
|
|
TNode<Smi> expected_result = m.CAST(m.Parameter(3));
|
|
TNode<Object> expected_arg = m.CAST(m.Parameter(4));
|
|
|
|
Label passed(&m), failed(&m);
|
|
Label if_found(&m), if_not_found(&m);
|
|
TVariable<IntPtrT> var_name_index(&m);
|
|
|
|
m.NameDictionaryLookup<Dictionary>(dictionary, unique_name, &if_found,
|
|
&var_name_index, &if_not_found);
|
|
m.BIND(&if_found);
|
|
m.GotoIfNot(
|
|
m.TaggedEqual(expected_result, m.SmiConstant(Smi::FromInt(kFound))),
|
|
&failed);
|
|
m.Branch(
|
|
m.WordEqual(m.SmiUntag(m.CAST(expected_arg)), var_name_index.value()),
|
|
&passed, &failed);
|
|
|
|
m.BIND(&if_not_found);
|
|
m.Branch(
|
|
m.TaggedEqual(expected_result, m.SmiConstant(Smi::FromInt(kNotFound))),
|
|
&passed, &failed);
|
|
|
|
m.BIND(&passed);
|
|
m.Return(m.BooleanConstant(true));
|
|
|
|
m.BIND(&failed);
|
|
m.Return(m.BooleanConstant(false));
|
|
}
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<Object> expect_found(Smi::FromInt(kFound), isolate);
|
|
Handle<Object> expect_not_found(Smi::FromInt(kNotFound), isolate);
|
|
|
|
Handle<Dictionary> dictionary = Dictionary::New(isolate, 40);
|
|
PropertyDetails fake_details = PropertyDetails::Empty();
|
|
|
|
Factory* factory = isolate->factory();
|
|
Handle<Name> keys[] = {
|
|
factory->InternalizeUtf8String("0"),
|
|
factory->InternalizeUtf8String("42"),
|
|
factory->InternalizeUtf8String("-153"),
|
|
factory->InternalizeUtf8String("0.0"),
|
|
factory->InternalizeUtf8String("4.2"),
|
|
factory->InternalizeUtf8String(""),
|
|
factory->InternalizeUtf8String("name"),
|
|
factory->NewSymbol(),
|
|
factory->NewPrivateSymbol(),
|
|
};
|
|
|
|
for (size_t i = 0; i < arraysize(keys); i++) {
|
|
Handle<Object> value = factory->NewPropertyCell(keys[i]);
|
|
dictionary =
|
|
Dictionary::Add(isolate, dictionary, keys[i], value, fake_details);
|
|
}
|
|
|
|
for (size_t i = 0; i < arraysize(keys); i++) {
|
|
InternalIndex entry = dictionary->FindEntry(isolate, keys[i]);
|
|
int name_index =
|
|
Dictionary::EntryToIndex(entry) + Dictionary::kEntryKeyIndex;
|
|
CHECK(entry.is_found());
|
|
|
|
Handle<Object> expected_name_index(Smi::FromInt(name_index), isolate);
|
|
ft.CheckTrue(dictionary, keys[i], expect_found, expected_name_index);
|
|
}
|
|
|
|
Handle<Name> non_existing_keys[] = {
|
|
factory->InternalizeUtf8String("1"),
|
|
factory->InternalizeUtf8String("-42"),
|
|
factory->InternalizeUtf8String("153"),
|
|
factory->InternalizeUtf8String("-1.0"),
|
|
factory->InternalizeUtf8String("1.3"),
|
|
factory->InternalizeUtf8String("a"),
|
|
factory->InternalizeUtf8String("boom"),
|
|
factory->NewSymbol(),
|
|
factory->NewPrivateSymbol(),
|
|
};
|
|
|
|
for (size_t i = 0; i < arraysize(non_existing_keys); i++) {
|
|
InternalIndex entry = dictionary->FindEntry(isolate, non_existing_keys[i]);
|
|
CHECK(entry.is_not_found());
|
|
|
|
ft.CheckTrue(dictionary, non_existing_keys[i], expect_not_found);
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST(NameDictionaryLookup) { TestNameDictionaryLookup<NameDictionary>(); }
|
|
|
|
TEST(GlobalDictionaryLookup) { TestNameDictionaryLookup<GlobalDictionary>(); }
|
|
|
|
TEST(NumberDictionaryLookup) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 4;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
enum Result { kFound, kNotFound };
|
|
{
|
|
TNode<NumberDictionary> dictionary = m.CAST(m.Parameter(1));
|
|
TNode<IntPtrT> key = m.SmiUntag(m.Parameter(2));
|
|
TNode<Smi> expected_result = m.CAST(m.Parameter(3));
|
|
TNode<Object> expected_arg = m.CAST(m.Parameter(4));
|
|
|
|
Label passed(&m), failed(&m);
|
|
Label if_found(&m), if_not_found(&m);
|
|
TVariable<IntPtrT> var_entry(&m);
|
|
|
|
m.NumberDictionaryLookup(dictionary, key, &if_found, &var_entry,
|
|
&if_not_found);
|
|
m.BIND(&if_found);
|
|
m.GotoIfNot(
|
|
m.TaggedEqual(expected_result, m.SmiConstant(Smi::FromInt(kFound))),
|
|
&failed);
|
|
m.Branch(m.WordEqual(m.SmiUntag(m.CAST(expected_arg)), var_entry.value()),
|
|
&passed, &failed);
|
|
|
|
m.BIND(&if_not_found);
|
|
m.Branch(
|
|
m.TaggedEqual(expected_result, m.SmiConstant(Smi::FromInt(kNotFound))),
|
|
&passed, &failed);
|
|
|
|
m.BIND(&passed);
|
|
m.Return(m.BooleanConstant(true));
|
|
|
|
m.BIND(&failed);
|
|
m.Return(m.BooleanConstant(false));
|
|
}
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<Object> expect_found(Smi::FromInt(kFound), isolate);
|
|
Handle<Object> expect_not_found(Smi::FromInt(kNotFound), isolate);
|
|
|
|
const int kKeysCount = 1000;
|
|
Handle<NumberDictionary> dictionary =
|
|
NumberDictionary::New(isolate, kKeysCount);
|
|
uint32_t keys[kKeysCount];
|
|
|
|
Handle<Object> fake_value(Smi::FromInt(42), isolate);
|
|
PropertyDetails fake_details = PropertyDetails::Empty();
|
|
|
|
base::RandomNumberGenerator rand_gen(FLAG_random_seed);
|
|
|
|
for (int i = 0; i < kKeysCount; i++) {
|
|
int random_key = rand_gen.NextInt(Smi::kMaxValue);
|
|
keys[i] = static_cast<uint32_t>(random_key);
|
|
if (dictionary->FindEntry(isolate, keys[i]).is_found()) continue;
|
|
|
|
dictionary = NumberDictionary::Add(isolate, dictionary, keys[i], fake_value,
|
|
fake_details);
|
|
}
|
|
|
|
// Now try querying existing keys.
|
|
for (int i = 0; i < kKeysCount; i++) {
|
|
InternalIndex entry = dictionary->FindEntry(isolate, keys[i]);
|
|
CHECK(entry.is_found());
|
|
|
|
Handle<Object> key(Smi::FromInt(keys[i]), isolate);
|
|
Handle<Object> expected_entry(Smi::FromInt(entry.as_int()), isolate);
|
|
ft.CheckTrue(dictionary, key, expect_found, expected_entry);
|
|
}
|
|
|
|
// Now try querying random keys which do not exist in the dictionary.
|
|
for (int i = 0; i < kKeysCount;) {
|
|
int random_key = rand_gen.NextInt(Smi::kMaxValue);
|
|
InternalIndex entry = dictionary->FindEntry(isolate, random_key);
|
|
if (entry.is_found()) continue;
|
|
i++;
|
|
|
|
Handle<Object> key(Smi::FromInt(random_key), isolate);
|
|
ft.CheckTrue(dictionary, key, expect_not_found);
|
|
}
|
|
}
|
|
|
|
TEST(TransitionLookup) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 4;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
|
|
enum Result { kFound, kNotFound };
|
|
|
|
class TempAssembler : public CodeStubAssembler {
|
|
public:
|
|
explicit TempAssembler(compiler::CodeAssemblerState* state)
|
|
: CodeStubAssembler(state) {}
|
|
|
|
void Generate() {
|
|
TNode<TransitionArray> transitions = CAST(Parameter(1));
|
|
TNode<Name> name = CAST(Parameter(2));
|
|
TNode<Smi> expected_result = CAST(Parameter(3));
|
|
TNode<Object> expected_arg = CAST(Parameter(4));
|
|
|
|
Label passed(this), failed(this);
|
|
Label if_found(this), if_not_found(this);
|
|
TVARIABLE(IntPtrT, var_transition_index);
|
|
|
|
TransitionLookup(name, transitions, &if_found, &var_transition_index,
|
|
&if_not_found);
|
|
|
|
BIND(&if_found);
|
|
GotoIfNot(TaggedEqual(expected_result, SmiConstant(kFound)), &failed);
|
|
Branch(TaggedEqual(expected_arg, SmiTag(var_transition_index.value())),
|
|
&passed, &failed);
|
|
|
|
BIND(&if_not_found);
|
|
Branch(TaggedEqual(expected_result, SmiConstant(kNotFound)), &passed,
|
|
&failed);
|
|
|
|
BIND(&passed);
|
|
Return(BooleanConstant(true));
|
|
|
|
BIND(&failed);
|
|
Return(BooleanConstant(false));
|
|
}
|
|
};
|
|
TempAssembler(asm_tester.state()).Generate();
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<Object> expect_found(Smi::FromInt(kFound), isolate);
|
|
Handle<Object> expect_not_found(Smi::FromInt(kNotFound), isolate);
|
|
|
|
const int ATTRS_COUNT = (READ_ONLY | DONT_ENUM | DONT_DELETE) + 1;
|
|
STATIC_ASSERT(ATTRS_COUNT == 8);
|
|
|
|
const int kKeysCount = 300;
|
|
Handle<Map> root_map = Map::Create(isolate, 0);
|
|
Handle<Name> keys[kKeysCount];
|
|
|
|
base::RandomNumberGenerator rand_gen(FLAG_random_seed);
|
|
|
|
Factory* factory = isolate->factory();
|
|
Handle<FieldType> any = FieldType::Any(isolate);
|
|
|
|
for (int i = 0; i < kKeysCount; i++) {
|
|
Handle<Name> name;
|
|
if (i % 30 == 0) {
|
|
name = factory->NewPrivateSymbol();
|
|
} else if (i % 10 == 0) {
|
|
name = factory->NewSymbol();
|
|
} else {
|
|
int random_key = rand_gen.NextInt(Smi::kMaxValue);
|
|
name = CcTest::MakeName("p", random_key);
|
|
}
|
|
keys[i] = name;
|
|
|
|
bool is_private = name->IsPrivate();
|
|
PropertyAttributes base_attributes = is_private ? DONT_ENUM : NONE;
|
|
|
|
// Ensure that all the combinations of cases are covered:
|
|
// 1) there is a "base" attributes transition
|
|
// 2) there are other non-base attributes transitions
|
|
if ((i & 1) == 0) {
|
|
CHECK(!Map::CopyWithField(isolate, root_map, name, any, base_attributes,
|
|
PropertyConstness::kMutable,
|
|
Representation::Tagged(), INSERT_TRANSITION)
|
|
.is_null());
|
|
}
|
|
|
|
if ((i & 2) == 0) {
|
|
for (int j = 0; j < ATTRS_COUNT; j++) {
|
|
PropertyAttributes attributes = static_cast<PropertyAttributes>(j);
|
|
if (attributes == base_attributes) continue;
|
|
// Don't add private symbols with enumerable attributes.
|
|
if (is_private && ((attributes & DONT_ENUM) == 0)) continue;
|
|
CHECK(!Map::CopyWithField(isolate, root_map, name, any, attributes,
|
|
PropertyConstness::kMutable,
|
|
Representation::Tagged(), INSERT_TRANSITION)
|
|
.is_null());
|
|
}
|
|
}
|
|
}
|
|
|
|
CHECK(root_map->raw_transitions()
|
|
->GetHeapObjectAssumeStrong()
|
|
.IsTransitionArray());
|
|
Handle<TransitionArray> transitions(
|
|
TransitionArray::cast(
|
|
root_map->raw_transitions()->GetHeapObjectAssumeStrong()),
|
|
isolate);
|
|
DCHECK(transitions->IsSortedNoDuplicates());
|
|
|
|
// Ensure we didn't overflow transition array and therefore all the
|
|
// combinations of cases are covered.
|
|
CHECK(TransitionsAccessor(isolate, root_map).CanHaveMoreTransitions());
|
|
|
|
// Now try querying keys.
|
|
bool positive_lookup_tested = false;
|
|
bool negative_lookup_tested = false;
|
|
for (int i = 0; i < kKeysCount; i++) {
|
|
Handle<Name> name = keys[i];
|
|
|
|
int transition_number = transitions->SearchNameForTesting(*name);
|
|
|
|
if (transition_number != TransitionArray::kNotFound) {
|
|
Handle<Smi> expected_value(
|
|
Smi::FromInt(TransitionArray::ToKeyIndex(transition_number)),
|
|
isolate);
|
|
ft.CheckTrue(transitions, name, expect_found, expected_value);
|
|
positive_lookup_tested = true;
|
|
} else {
|
|
ft.CheckTrue(transitions, name, expect_not_found);
|
|
negative_lookup_tested = true;
|
|
}
|
|
}
|
|
CHECK(positive_lookup_tested);
|
|
CHECK(negative_lookup_tested);
|
|
}
|
|
|
|
namespace {
|
|
|
|
void AddProperties(Handle<JSObject> object, Handle<Name> names[],
|
|
size_t count) {
|
|
Isolate* isolate = object->GetIsolate();
|
|
for (size_t i = 0; i < count; i++) {
|
|
Handle<Object> value(Smi::FromInt(static_cast<int>(42 + i)), isolate);
|
|
JSObject::AddProperty(isolate, object, names[i], value, NONE);
|
|
}
|
|
}
|
|
|
|
Handle<AccessorPair> CreateAccessorPair(FunctionTester* ft,
|
|
const char* getter_body,
|
|
const char* setter_body) {
|
|
Handle<AccessorPair> pair = ft->isolate->factory()->NewAccessorPair();
|
|
if (getter_body) {
|
|
pair->set_getter(*ft->NewFunction(getter_body));
|
|
}
|
|
if (setter_body) {
|
|
pair->set_setter(*ft->NewFunction(setter_body));
|
|
}
|
|
return pair;
|
|
}
|
|
|
|
void AddProperties(Handle<JSObject> object, Handle<Name> names[],
|
|
size_t names_count, Handle<Object> values[],
|
|
size_t values_count, int seed = 0) {
|
|
Isolate* isolate = object->GetIsolate();
|
|
for (size_t i = 0; i < names_count; i++) {
|
|
Handle<Object> value = values[(seed + i) % values_count];
|
|
if (value->IsAccessorPair()) {
|
|
Handle<AccessorPair> pair = Handle<AccessorPair>::cast(value);
|
|
Handle<Object> getter(pair->getter(), isolate);
|
|
Handle<Object> setter(pair->setter(), isolate);
|
|
JSObject::DefineAccessor(object, names[i], getter, setter, NONE).Check();
|
|
} else {
|
|
JSObject::AddProperty(isolate, object, names[i], value, NONE);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST(TryHasOwnProperty) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 3;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
enum Result { kFound, kNotFound, kBailout };
|
|
{
|
|
TNode<HeapObject> object = m.CAST(m.Parameter(1));
|
|
TNode<Name> unique_name = m.CAST(m.Parameter(2));
|
|
TNode<MaybeObject> expected_result =
|
|
m.UncheckedCast<MaybeObject>(m.Parameter(3));
|
|
|
|
Label passed(&m), failed(&m);
|
|
Label if_found(&m), if_not_found(&m), if_bailout(&m);
|
|
|
|
TNode<Map> map = m.LoadMap(object);
|
|
TNode<Uint16T> instance_type = m.LoadMapInstanceType(map);
|
|
|
|
m.TryHasOwnProperty(object, map, instance_type, unique_name, &if_found,
|
|
&if_not_found, &if_bailout);
|
|
|
|
m.BIND(&if_found);
|
|
m.Branch(
|
|
m.TaggedEqual(expected_result, m.SmiConstant(Smi::FromInt(kFound))),
|
|
&passed, &failed);
|
|
|
|
m.BIND(&if_not_found);
|
|
m.Branch(
|
|
m.TaggedEqual(expected_result, m.SmiConstant(Smi::FromInt(kNotFound))),
|
|
&passed, &failed);
|
|
|
|
m.BIND(&if_bailout);
|
|
m.Branch(
|
|
m.TaggedEqual(expected_result, m.SmiConstant(Smi::FromInt(kBailout))),
|
|
&passed, &failed);
|
|
|
|
m.BIND(&passed);
|
|
m.Return(m.BooleanConstant(true));
|
|
|
|
m.BIND(&failed);
|
|
m.Return(m.BooleanConstant(false));
|
|
}
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<Object> expect_found(Smi::FromInt(kFound), isolate);
|
|
Handle<Object> expect_not_found(Smi::FromInt(kNotFound), isolate);
|
|
Handle<Object> expect_bailout(Smi::FromInt(kBailout), isolate);
|
|
|
|
Factory* factory = isolate->factory();
|
|
|
|
Handle<Name> deleted_property_name =
|
|
factory->InternalizeUtf8String("deleted");
|
|
|
|
Handle<Name> names[] = {
|
|
factory->InternalizeUtf8String("a"),
|
|
factory->InternalizeUtf8String("bb"),
|
|
factory->InternalizeUtf8String("ccc"),
|
|
factory->InternalizeUtf8String("dddd"),
|
|
factory->InternalizeUtf8String("eeeee"),
|
|
factory->InternalizeUtf8String(""),
|
|
factory->InternalizeUtf8String("name"),
|
|
factory->NewSymbol(),
|
|
factory->NewPrivateSymbol(),
|
|
};
|
|
|
|
std::vector<Handle<JSObject>> objects;
|
|
|
|
{
|
|
// Fast object, no inobject properties.
|
|
int inobject_properties = 0;
|
|
Handle<Map> map = Map::Create(isolate, inobject_properties);
|
|
Handle<JSObject> object = factory->NewJSObjectFromMap(map);
|
|
AddProperties(object, names, arraysize(names));
|
|
CHECK_EQ(JS_OBJECT_TYPE, object->map().instance_type());
|
|
CHECK_EQ(inobject_properties, object->map().GetInObjectProperties());
|
|
CHECK(!object->map().is_dictionary_map());
|
|
objects.push_back(object);
|
|
}
|
|
|
|
{
|
|
// Fast object, all inobject properties.
|
|
int inobject_properties = arraysize(names) * 2;
|
|
Handle<Map> map = Map::Create(isolate, inobject_properties);
|
|
Handle<JSObject> object = factory->NewJSObjectFromMap(map);
|
|
AddProperties(object, names, arraysize(names));
|
|
CHECK_EQ(JS_OBJECT_TYPE, object->map().instance_type());
|
|
CHECK_EQ(inobject_properties, object->map().GetInObjectProperties());
|
|
CHECK(!object->map().is_dictionary_map());
|
|
objects.push_back(object);
|
|
}
|
|
|
|
{
|
|
// Fast object, half inobject properties.
|
|
int inobject_properties = arraysize(names) / 2;
|
|
Handle<Map> map = Map::Create(isolate, inobject_properties);
|
|
Handle<JSObject> object = factory->NewJSObjectFromMap(map);
|
|
AddProperties(object, names, arraysize(names));
|
|
CHECK_EQ(JS_OBJECT_TYPE, object->map().instance_type());
|
|
CHECK_EQ(inobject_properties, object->map().GetInObjectProperties());
|
|
CHECK(!object->map().is_dictionary_map());
|
|
objects.push_back(object);
|
|
}
|
|
|
|
{
|
|
// Dictionary mode object.
|
|
Handle<JSFunction> function =
|
|
factory->NewFunctionForTest(factory->empty_string());
|
|
Handle<JSObject> object = factory->NewJSObject(function);
|
|
AddProperties(object, names, arraysize(names));
|
|
JSObject::NormalizeProperties(isolate, object, CLEAR_INOBJECT_PROPERTIES, 0,
|
|
"test");
|
|
|
|
JSObject::AddProperty(isolate, object, deleted_property_name, object, NONE);
|
|
CHECK(JSObject::DeleteProperty(object, deleted_property_name,
|
|
LanguageMode::kSloppy)
|
|
.FromJust());
|
|
|
|
CHECK_EQ(JS_OBJECT_TYPE, object->map().instance_type());
|
|
CHECK(object->map().is_dictionary_map());
|
|
objects.push_back(object);
|
|
}
|
|
|
|
{
|
|
// Global object.
|
|
Handle<JSFunction> function =
|
|
factory->NewFunctionForTest(factory->empty_string());
|
|
JSFunction::EnsureHasInitialMap(function);
|
|
function->initial_map().set_instance_type(JS_GLOBAL_OBJECT_TYPE);
|
|
function->initial_map().set_is_prototype_map(true);
|
|
function->initial_map().set_is_dictionary_map(true);
|
|
function->initial_map().set_may_have_interesting_symbols(true);
|
|
Handle<JSObject> object = factory->NewJSGlobalObject(function);
|
|
AddProperties(object, names, arraysize(names));
|
|
|
|
JSObject::AddProperty(isolate, object, deleted_property_name, object, NONE);
|
|
CHECK(JSObject::DeleteProperty(object, deleted_property_name,
|
|
LanguageMode::kSloppy)
|
|
.FromJust());
|
|
|
|
CHECK_EQ(JS_GLOBAL_OBJECT_TYPE, object->map().instance_type());
|
|
CHECK(object->map().is_dictionary_map());
|
|
objects.push_back(object);
|
|
}
|
|
|
|
{
|
|
for (Handle<JSObject> object : objects) {
|
|
for (size_t name_index = 0; name_index < arraysize(names); name_index++) {
|
|
Handle<Name> name = names[name_index];
|
|
CHECK(JSReceiver::HasProperty(object, name).FromJust());
|
|
ft.CheckTrue(object, name, expect_found);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
Handle<Name> non_existing_names[] = {
|
|
factory->NewSymbol(),
|
|
factory->InternalizeUtf8String("ne_a"),
|
|
factory->InternalizeUtf8String("ne_bb"),
|
|
factory->NewPrivateSymbol(),
|
|
factory->InternalizeUtf8String("ne_ccc"),
|
|
factory->InternalizeUtf8String("ne_dddd"),
|
|
deleted_property_name,
|
|
};
|
|
for (Handle<JSObject> object : objects) {
|
|
for (size_t key_index = 0; key_index < arraysize(non_existing_names);
|
|
key_index++) {
|
|
Handle<Name> name = non_existing_names[key_index];
|
|
CHECK(!JSReceiver::HasProperty(object, name).FromJust());
|
|
ft.CheckTrue(object, name, expect_not_found);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
Handle<JSFunction> function =
|
|
factory->NewFunctionForTest(factory->empty_string());
|
|
Handle<JSProxy> object = factory->NewJSProxy(function, objects[0]);
|
|
CHECK_EQ(JS_PROXY_TYPE, object->map().instance_type());
|
|
ft.CheckTrue(object, names[0], expect_bailout);
|
|
}
|
|
|
|
{
|
|
Handle<JSObject> object = isolate->global_proxy();
|
|
CHECK_EQ(JS_GLOBAL_PROXY_TYPE, object->map().instance_type());
|
|
ft.CheckTrue(object, names[0], expect_bailout);
|
|
}
|
|
}
|
|
|
|
TEST(TryGetOwnProperty) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
Factory* factory = isolate->factory();
|
|
|
|
const int kNumParams = 2;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
Handle<Symbol> not_found_symbol = factory->NewSymbol();
|
|
Handle<Symbol> bailout_symbol = factory->NewSymbol();
|
|
{
|
|
TNode<JSReceiver> object = m.CAST(m.Parameter(1));
|
|
TNode<Name> unique_name = m.CAST(m.Parameter(2));
|
|
TNode<Context> context = m.CAST(m.Parameter(kNumParams + 3));
|
|
|
|
TVariable<Object> var_value(&m);
|
|
Label if_found(&m), if_not_found(&m), if_bailout(&m);
|
|
|
|
TNode<Map> map = m.LoadMap(object);
|
|
TNode<Uint16T> instance_type = m.LoadMapInstanceType(map);
|
|
|
|
m.TryGetOwnProperty(context, object, object, map, instance_type,
|
|
unique_name, &if_found, &var_value, &if_not_found,
|
|
&if_bailout);
|
|
|
|
m.BIND(&if_found);
|
|
m.Return(m.UncheckedCast<Object>(var_value.value()));
|
|
|
|
m.BIND(&if_not_found);
|
|
m.Return(m.HeapConstant(not_found_symbol));
|
|
|
|
m.BIND(&if_bailout);
|
|
m.Return(m.HeapConstant(bailout_symbol));
|
|
}
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<Name> deleted_property_name =
|
|
factory->InternalizeUtf8String("deleted");
|
|
|
|
Handle<Name> names[] = {
|
|
factory->InternalizeUtf8String("bb"),
|
|
factory->NewSymbol(),
|
|
factory->InternalizeUtf8String("a"),
|
|
factory->InternalizeUtf8String("ccc"),
|
|
factory->InternalizeUtf8String("esajefe"),
|
|
factory->NewPrivateSymbol(),
|
|
factory->InternalizeUtf8String("eeeee"),
|
|
factory->InternalizeUtf8String("p1"),
|
|
factory->InternalizeUtf8String("acshw23e"),
|
|
factory->InternalizeUtf8String(""),
|
|
factory->InternalizeUtf8String("dddd"),
|
|
factory->NewPrivateSymbol(),
|
|
factory->InternalizeUtf8String("name"),
|
|
factory->InternalizeUtf8String("p2"),
|
|
factory->InternalizeUtf8String("p3"),
|
|
factory->InternalizeUtf8String("p4"),
|
|
factory->NewPrivateSymbol(),
|
|
};
|
|
Handle<Object> values[] = {
|
|
factory->NewFunctionForTest(factory->empty_string()),
|
|
factory->NewSymbol(),
|
|
factory->InternalizeUtf8String("a"),
|
|
CreateAccessorPair(&ft, "() => 188;", "() => 199;"),
|
|
factory->NewFunctionForTest(factory->InternalizeUtf8String("bb")),
|
|
factory->InternalizeUtf8String("ccc"),
|
|
CreateAccessorPair(&ft, "() => 88;", nullptr),
|
|
handle(Smi::FromInt(1), isolate),
|
|
factory->InternalizeUtf8String(""),
|
|
CreateAccessorPair(&ft, nullptr, "() => 99;"),
|
|
factory->NewHeapNumber(4.2),
|
|
handle(Smi::FromInt(153), isolate),
|
|
factory->NewJSObject(
|
|
factory->NewFunctionForTest(factory->empty_string())),
|
|
factory->NewPrivateSymbol(),
|
|
};
|
|
STATIC_ASSERT(arraysize(values) < arraysize(names));
|
|
|
|
base::RandomNumberGenerator rand_gen(FLAG_random_seed);
|
|
|
|
std::vector<Handle<JSObject>> objects;
|
|
|
|
{
|
|
// Fast object, no inobject properties.
|
|
int inobject_properties = 0;
|
|
Handle<Map> map = Map::Create(isolate, inobject_properties);
|
|
Handle<JSObject> object = factory->NewJSObjectFromMap(map);
|
|
AddProperties(object, names, arraysize(names), values, arraysize(values),
|
|
rand_gen.NextInt());
|
|
CHECK_EQ(JS_OBJECT_TYPE, object->map().instance_type());
|
|
CHECK_EQ(inobject_properties, object->map().GetInObjectProperties());
|
|
CHECK(!object->map().is_dictionary_map());
|
|
objects.push_back(object);
|
|
}
|
|
|
|
{
|
|
// Fast object, all inobject properties.
|
|
int inobject_properties = arraysize(names) * 2;
|
|
Handle<Map> map = Map::Create(isolate, inobject_properties);
|
|
Handle<JSObject> object = factory->NewJSObjectFromMap(map);
|
|
AddProperties(object, names, arraysize(names), values, arraysize(values),
|
|
rand_gen.NextInt());
|
|
CHECK_EQ(JS_OBJECT_TYPE, object->map().instance_type());
|
|
CHECK_EQ(inobject_properties, object->map().GetInObjectProperties());
|
|
CHECK(!object->map().is_dictionary_map());
|
|
objects.push_back(object);
|
|
}
|
|
|
|
{
|
|
// Fast object, half inobject properties.
|
|
int inobject_properties = arraysize(names) / 2;
|
|
Handle<Map> map = Map::Create(isolate, inobject_properties);
|
|
Handle<JSObject> object = factory->NewJSObjectFromMap(map);
|
|
AddProperties(object, names, arraysize(names), values, arraysize(values),
|
|
rand_gen.NextInt());
|
|
CHECK_EQ(JS_OBJECT_TYPE, object->map().instance_type());
|
|
CHECK_EQ(inobject_properties, object->map().GetInObjectProperties());
|
|
CHECK(!object->map().is_dictionary_map());
|
|
objects.push_back(object);
|
|
}
|
|
|
|
{
|
|
// Dictionary mode object.
|
|
Handle<JSFunction> function =
|
|
factory->NewFunctionForTest(factory->empty_string());
|
|
Handle<JSObject> object = factory->NewJSObject(function);
|
|
AddProperties(object, names, arraysize(names), values, arraysize(values),
|
|
rand_gen.NextInt());
|
|
JSObject::NormalizeProperties(isolate, object, CLEAR_INOBJECT_PROPERTIES, 0,
|
|
"test");
|
|
|
|
JSObject::AddProperty(isolate, object, deleted_property_name, object, NONE);
|
|
CHECK(JSObject::DeleteProperty(object, deleted_property_name,
|
|
LanguageMode::kSloppy)
|
|
.FromJust());
|
|
|
|
CHECK_EQ(JS_OBJECT_TYPE, object->map().instance_type());
|
|
CHECK(object->map().is_dictionary_map());
|
|
objects.push_back(object);
|
|
}
|
|
|
|
{
|
|
// Global object.
|
|
Handle<JSGlobalObject> object = isolate->global_object();
|
|
AddProperties(object, names, arraysize(names), values, arraysize(values),
|
|
rand_gen.NextInt());
|
|
|
|
JSObject::AddProperty(isolate, object, deleted_property_name, object, NONE);
|
|
CHECK(JSObject::DeleteProperty(object, deleted_property_name,
|
|
LanguageMode::kSloppy)
|
|
.FromJust());
|
|
|
|
CHECK_EQ(JS_GLOBAL_OBJECT_TYPE, object->map().instance_type());
|
|
CHECK(object->map().is_dictionary_map());
|
|
objects.push_back(object);
|
|
}
|
|
|
|
// TODO(ishell): test proxy and interceptors when they are supported.
|
|
|
|
{
|
|
for (Handle<JSObject> object : objects) {
|
|
for (size_t name_index = 0; name_index < arraysize(names); name_index++) {
|
|
Handle<Name> name = names[name_index];
|
|
Handle<Object> expected_value =
|
|
JSReceiver::GetProperty(isolate, object, name).ToHandleChecked();
|
|
Handle<Object> value = ft.Call(object, name).ToHandleChecked();
|
|
CHECK(expected_value->SameValue(*value));
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
Handle<Name> non_existing_names[] = {
|
|
factory->NewSymbol(),
|
|
factory->InternalizeUtf8String("ne_a"),
|
|
factory->InternalizeUtf8String("ne_bb"),
|
|
factory->NewPrivateSymbol(),
|
|
factory->InternalizeUtf8String("ne_ccc"),
|
|
factory->InternalizeUtf8String("ne_dddd"),
|
|
deleted_property_name,
|
|
};
|
|
for (Handle<JSObject> object : objects) {
|
|
for (size_t key_index = 0; key_index < arraysize(non_existing_names);
|
|
key_index++) {
|
|
Handle<Name> name = non_existing_names[key_index];
|
|
Handle<Object> expected_value =
|
|
JSReceiver::GetProperty(isolate, object, name).ToHandleChecked();
|
|
CHECK(expected_value->IsUndefined(isolate));
|
|
Handle<Object> value = ft.Call(object, name).ToHandleChecked();
|
|
CHECK_EQ(*not_found_symbol, *value);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
Handle<JSFunction> function =
|
|
factory->NewFunctionForTest(factory->empty_string());
|
|
Handle<JSProxy> object = factory->NewJSProxy(function, objects[0]);
|
|
CHECK_EQ(JS_PROXY_TYPE, object->map().instance_type());
|
|
Handle<Object> value = ft.Call(object, names[0]).ToHandleChecked();
|
|
// Proxies are not supported yet.
|
|
CHECK_EQ(*bailout_symbol, *value);
|
|
}
|
|
|
|
{
|
|
Handle<JSObject> object = isolate->global_proxy();
|
|
CHECK_EQ(JS_GLOBAL_PROXY_TYPE, object->map().instance_type());
|
|
// Global proxies are not supported yet.
|
|
Handle<Object> value = ft.Call(object, names[0]).ToHandleChecked();
|
|
CHECK_EQ(*bailout_symbol, *value);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
void AddElement(Handle<JSObject> object, uint32_t index, Handle<Object> value,
|
|
PropertyAttributes attributes = NONE) {
|
|
JSObject::AddDataElement(object, index, value, attributes);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST(TryLookupElement) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 3;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
enum Result { kFound, kAbsent, kNotFound, kBailout };
|
|
{
|
|
TNode<HeapObject> object = m.CAST(m.Parameter(1));
|
|
TNode<IntPtrT> index = m.SmiUntag(m.Parameter(2));
|
|
TNode<MaybeObject> expected_result =
|
|
m.UncheckedCast<MaybeObject>(m.Parameter(3));
|
|
|
|
Label passed(&m), failed(&m);
|
|
Label if_found(&m), if_not_found(&m), if_bailout(&m), if_absent(&m);
|
|
|
|
TNode<Map> map = m.LoadMap(object);
|
|
TNode<Uint16T> instance_type = m.LoadMapInstanceType(map);
|
|
|
|
m.TryLookupElement(object, map, instance_type, index, &if_found, &if_absent,
|
|
&if_not_found, &if_bailout);
|
|
|
|
m.BIND(&if_found);
|
|
m.Branch(
|
|
m.TaggedEqual(expected_result, m.SmiConstant(Smi::FromInt(kFound))),
|
|
&passed, &failed);
|
|
|
|
m.BIND(&if_absent);
|
|
m.Branch(
|
|
m.TaggedEqual(expected_result, m.SmiConstant(Smi::FromInt(kAbsent))),
|
|
&passed, &failed);
|
|
|
|
m.BIND(&if_not_found);
|
|
m.Branch(
|
|
m.TaggedEqual(expected_result, m.SmiConstant(Smi::FromInt(kNotFound))),
|
|
&passed, &failed);
|
|
|
|
m.BIND(&if_bailout);
|
|
m.Branch(
|
|
m.TaggedEqual(expected_result, m.SmiConstant(Smi::FromInt(kBailout))),
|
|
&passed, &failed);
|
|
|
|
m.BIND(&passed);
|
|
m.Return(m.BooleanConstant(true));
|
|
|
|
m.BIND(&failed);
|
|
m.Return(m.BooleanConstant(false));
|
|
}
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Factory* factory = isolate->factory();
|
|
Handle<Object> smi0(Smi::zero(), isolate);
|
|
Handle<Object> smi1(Smi::FromInt(1), isolate);
|
|
Handle<Object> smi7(Smi::FromInt(7), isolate);
|
|
Handle<Object> smi13(Smi::FromInt(13), isolate);
|
|
Handle<Object> smi42(Smi::FromInt(42), isolate);
|
|
|
|
Handle<Object> expect_found(Smi::FromInt(kFound), isolate);
|
|
Handle<Object> expect_absent(Smi::FromInt(kAbsent), isolate);
|
|
Handle<Object> expect_not_found(Smi::FromInt(kNotFound), isolate);
|
|
Handle<Object> expect_bailout(Smi::FromInt(kBailout), isolate);
|
|
|
|
#define CHECK_FOUND(object, index) \
|
|
CHECK(JSReceiver::HasElement(object, index).FromJust()); \
|
|
ft.CheckTrue(object, smi##index, expect_found);
|
|
|
|
#define CHECK_NOT_FOUND(object, index) \
|
|
CHECK(!JSReceiver::HasElement(object, index).FromJust()); \
|
|
ft.CheckTrue(object, smi##index, expect_not_found);
|
|
|
|
#define CHECK_ABSENT(object, index) \
|
|
{ \
|
|
Handle<Smi> smi(Smi::FromInt(index), isolate); \
|
|
LookupIterator::Key key(isolate, smi); \
|
|
LookupIterator it(isolate, object, key); \
|
|
CHECK(!JSReceiver::HasProperty(&it).FromJust()); \
|
|
ft.CheckTrue(object, smi, expect_absent); \
|
|
}
|
|
|
|
{
|
|
Handle<JSArray> object = factory->NewJSArray(0, PACKED_SMI_ELEMENTS);
|
|
AddElement(object, 0, smi0);
|
|
AddElement(object, 1, smi0);
|
|
CHECK_EQ(PACKED_SMI_ELEMENTS, object->map().elements_kind());
|
|
|
|
CHECK_FOUND(object, 0);
|
|
CHECK_FOUND(object, 1);
|
|
CHECK_NOT_FOUND(object, 7);
|
|
CHECK_NOT_FOUND(object, 13);
|
|
CHECK_NOT_FOUND(object, 42);
|
|
}
|
|
|
|
{
|
|
Handle<JSArray> object = factory->NewJSArray(0, HOLEY_SMI_ELEMENTS);
|
|
AddElement(object, 0, smi0);
|
|
AddElement(object, 13, smi0);
|
|
CHECK_EQ(HOLEY_SMI_ELEMENTS, object->map().elements_kind());
|
|
|
|
CHECK_FOUND(object, 0);
|
|
CHECK_NOT_FOUND(object, 1);
|
|
CHECK_NOT_FOUND(object, 7);
|
|
CHECK_FOUND(object, 13);
|
|
CHECK_NOT_FOUND(object, 42);
|
|
}
|
|
|
|
{
|
|
Handle<JSArray> object = factory->NewJSArray(0, PACKED_ELEMENTS);
|
|
AddElement(object, 0, smi0);
|
|
AddElement(object, 1, smi0);
|
|
CHECK_EQ(PACKED_ELEMENTS, object->map().elements_kind());
|
|
|
|
CHECK_FOUND(object, 0);
|
|
CHECK_FOUND(object, 1);
|
|
CHECK_NOT_FOUND(object, 7);
|
|
CHECK_NOT_FOUND(object, 13);
|
|
CHECK_NOT_FOUND(object, 42);
|
|
}
|
|
|
|
{
|
|
Handle<JSArray> object = factory->NewJSArray(0, HOLEY_ELEMENTS);
|
|
AddElement(object, 0, smi0);
|
|
AddElement(object, 13, smi0);
|
|
CHECK_EQ(HOLEY_ELEMENTS, object->map().elements_kind());
|
|
|
|
CHECK_FOUND(object, 0);
|
|
CHECK_NOT_FOUND(object, 1);
|
|
CHECK_NOT_FOUND(object, 7);
|
|
CHECK_FOUND(object, 13);
|
|
CHECK_NOT_FOUND(object, 42);
|
|
}
|
|
|
|
{
|
|
v8::Local<v8::ArrayBuffer> buffer =
|
|
v8::ArrayBuffer::New(reinterpret_cast<v8::Isolate*>(isolate), 8);
|
|
Handle<JSTypedArray> object = factory->NewJSTypedArray(
|
|
kExternalInt32Array, v8::Utils::OpenHandle(*buffer), 0, 2);
|
|
|
|
CHECK_EQ(INT32_ELEMENTS, object->map().elements_kind());
|
|
|
|
CHECK_FOUND(object, 0);
|
|
CHECK_FOUND(object, 1);
|
|
CHECK_ABSENT(object, -10);
|
|
CHECK_ABSENT(object, 13);
|
|
CHECK_ABSENT(object, 42);
|
|
|
|
{
|
|
std::shared_ptr<v8::BackingStore> backing_store =
|
|
buffer->GetBackingStore();
|
|
buffer->Detach();
|
|
}
|
|
CHECK_ABSENT(object, 0);
|
|
CHECK_ABSENT(object, 1);
|
|
CHECK_ABSENT(object, -10);
|
|
CHECK_ABSENT(object, 13);
|
|
CHECK_ABSENT(object, 42);
|
|
}
|
|
|
|
{
|
|
Handle<JSFunction> constructor = isolate->string_function();
|
|
Handle<JSObject> object = factory->NewJSObject(constructor);
|
|
Handle<String> str = factory->InternalizeUtf8String("ab");
|
|
Handle<JSPrimitiveWrapper>::cast(object)->set_value(*str);
|
|
AddElement(object, 13, smi0);
|
|
CHECK_EQ(FAST_STRING_WRAPPER_ELEMENTS, object->map().elements_kind());
|
|
|
|
CHECK_FOUND(object, 0);
|
|
CHECK_FOUND(object, 1);
|
|
CHECK_NOT_FOUND(object, 7);
|
|
CHECK_FOUND(object, 13);
|
|
CHECK_NOT_FOUND(object, 42);
|
|
}
|
|
|
|
{
|
|
Handle<JSFunction> constructor = isolate->string_function();
|
|
Handle<JSObject> object = factory->NewJSObject(constructor);
|
|
Handle<String> str = factory->InternalizeUtf8String("ab");
|
|
Handle<JSPrimitiveWrapper>::cast(object)->set_value(*str);
|
|
AddElement(object, 13, smi0);
|
|
JSObject::NormalizeElements(object);
|
|
CHECK_EQ(SLOW_STRING_WRAPPER_ELEMENTS, object->map().elements_kind());
|
|
|
|
CHECK_FOUND(object, 0);
|
|
CHECK_FOUND(object, 1);
|
|
CHECK_NOT_FOUND(object, 7);
|
|
CHECK_FOUND(object, 13);
|
|
CHECK_NOT_FOUND(object, 42);
|
|
}
|
|
|
|
// TODO(ishell): uncomment once NO_ELEMENTS kind is supported.
|
|
// {
|
|
// Handle<Map> map = Map::Create(isolate, 0);
|
|
// map->set_elements_kind(NO_ELEMENTS);
|
|
// Handle<JSObject> object = factory->NewJSObjectFromMap(map);
|
|
// CHECK_EQ(NO_ELEMENTS, object->map()->elements_kind());
|
|
//
|
|
// CHECK_NOT_FOUND(object, 0);
|
|
// CHECK_NOT_FOUND(object, 1);
|
|
// CHECK_NOT_FOUND(object, 7);
|
|
// CHECK_NOT_FOUND(object, 13);
|
|
// CHECK_NOT_FOUND(object, 42);
|
|
// }
|
|
|
|
#undef CHECK_FOUND
|
|
#undef CHECK_NOT_FOUND
|
|
#undef CHECK_ABSENT
|
|
|
|
{
|
|
Handle<JSArray> handler = factory->NewJSArray(0);
|
|
Handle<JSFunction> function =
|
|
factory->NewFunctionForTest(factory->empty_string());
|
|
Handle<JSProxy> object = factory->NewJSProxy(function, handler);
|
|
CHECK_EQ(JS_PROXY_TYPE, object->map().instance_type());
|
|
ft.CheckTrue(object, smi0, expect_bailout);
|
|
}
|
|
|
|
{
|
|
Handle<JSObject> object = isolate->global_object();
|
|
CHECK_EQ(JS_GLOBAL_OBJECT_TYPE, object->map().instance_type());
|
|
ft.CheckTrue(object, smi0, expect_bailout);
|
|
}
|
|
|
|
{
|
|
Handle<JSObject> object = isolate->global_proxy();
|
|
CHECK_EQ(JS_GLOBAL_PROXY_TYPE, object->map().instance_type());
|
|
ft.CheckTrue(object, smi0, expect_bailout);
|
|
}
|
|
}
|
|
|
|
TEST(AllocateJSObjectFromMap) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
Factory* factory = isolate->factory();
|
|
|
|
const int kNumParams = 3;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
{
|
|
TNode<Map> map = m.CAST(m.Parameter(1));
|
|
TNode<HeapObject> properties = m.CAST(m.Parameter(2));
|
|
TNode<FixedArray> elements = m.CAST(m.Parameter(3));
|
|
|
|
TNode<JSObject> result =
|
|
m.AllocateJSObjectFromMap(map, properties, elements);
|
|
|
|
CodeStubAssembler::Label done(&m);
|
|
m.GotoIfNot(m.IsJSArrayMap(map), &done);
|
|
|
|
// JS array verification requires the length field to be set.
|
|
m.StoreObjectFieldNoWriteBarrier(result, JSArray::kLengthOffset,
|
|
m.SmiConstant(0));
|
|
m.Goto(&done);
|
|
|
|
m.Bind(&done);
|
|
m.Return(result);
|
|
}
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<Map> maps[] = {
|
|
handle(isolate->object_function()->initial_map(), isolate),
|
|
handle(isolate->array_function()->initial_map(), isolate),
|
|
};
|
|
|
|
{
|
|
Handle<FixedArray> empty_fixed_array = factory->empty_fixed_array();
|
|
Handle<PropertyArray> empty_property_array =
|
|
factory->empty_property_array();
|
|
for (size_t i = 0; i < arraysize(maps); i++) {
|
|
Handle<Map> map = maps[i];
|
|
Handle<JSObject> result = Handle<JSObject>::cast(
|
|
ft.Call(map, empty_fixed_array, empty_fixed_array).ToHandleChecked());
|
|
CHECK_EQ(result->map(), *map);
|
|
CHECK_EQ(result->property_array(), *empty_property_array);
|
|
CHECK_EQ(result->elements(), *empty_fixed_array);
|
|
CHECK(result->HasFastProperties());
|
|
#ifdef VERIFY_HEAP
|
|
isolate->heap()->Verify();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
{
|
|
// TODO(cbruni): handle in-object properties
|
|
Handle<JSObject> object = Handle<JSObject>::cast(
|
|
v8::Utils::OpenHandle(*CompileRun("var object = {a:1,b:2, 1:1, 2:2}; "
|
|
"object")));
|
|
JSObject::NormalizeProperties(isolate, object, KEEP_INOBJECT_PROPERTIES, 0,
|
|
"Normalize");
|
|
Handle<JSObject> result = Handle<JSObject>::cast(
|
|
ft.Call(handle(object->map(), isolate),
|
|
handle(object->property_dictionary(), isolate),
|
|
handle(object->elements(), isolate))
|
|
.ToHandleChecked());
|
|
CHECK_EQ(result->map(), object->map());
|
|
CHECK_EQ(result->property_dictionary(), object->property_dictionary());
|
|
CHECK(!result->HasFastProperties());
|
|
#ifdef VERIFY_HEAP
|
|
isolate->heap()->Verify();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
TEST(AllocateNameDictionary) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
{
|
|
Node* capacity = m.Parameter(1);
|
|
TNode<NameDictionary> result =
|
|
m.AllocateNameDictionary(m.SmiUntag(capacity));
|
|
m.Return(result);
|
|
}
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
{
|
|
for (int i = 0; i < 256; i = i * 1.1 + 1) {
|
|
Handle<HeapObject> result = Handle<HeapObject>::cast(
|
|
ft.Call(handle(Smi::FromInt(i), isolate)).ToHandleChecked());
|
|
Handle<NameDictionary> dict = NameDictionary::New(isolate, i);
|
|
// Both dictionaries should be memory equal.
|
|
int size = dict->Size();
|
|
CHECK_EQ(0, memcmp(reinterpret_cast<void*>(dict->address()),
|
|
reinterpret_cast<void*>(result->address()), size));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(PopAndReturnConstant) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 4;
|
|
const int kNumProgrammaticParams = 2;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams - kNumProgrammaticParams);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
// Call a function that return |kNumProgramaticParams| parameters in addition
|
|
// to those specified by the static descriptor. |kNumProgramaticParams| is
|
|
// specified as a constant.
|
|
m.PopAndReturn(m.Int32Constant(kNumProgrammaticParams),
|
|
m.SmiConstant(Smi::FromInt(1234)));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result;
|
|
for (int test_count = 0; test_count < 100; ++test_count) {
|
|
result = ft.Call(isolate->factory()->undefined_value(),
|
|
Handle<Smi>(Smi::FromInt(1234), isolate),
|
|
isolate->factory()->undefined_value(),
|
|
isolate->factory()->undefined_value())
|
|
.ToHandleChecked();
|
|
CHECK_EQ(1234, Handle<Smi>::cast(result)->value());
|
|
}
|
|
}
|
|
|
|
TEST(PopAndReturnVariable) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 4;
|
|
const int kNumProgrammaticParams = 2;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams - kNumProgrammaticParams);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
// Call a function that return |kNumProgramaticParams| parameters in addition
|
|
// to those specified by the static descriptor. |kNumProgramaticParams| is
|
|
// passed in as a parameter to the function so that it can't be recongized as
|
|
// a constant.
|
|
m.PopAndReturn(m.SmiUntag(m.Parameter(1)), m.SmiConstant(Smi::FromInt(1234)));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result;
|
|
for (int test_count = 0; test_count < 100; ++test_count) {
|
|
result = ft.Call(isolate->factory()->undefined_value(),
|
|
Handle<Smi>(Smi::FromInt(1234), isolate),
|
|
isolate->factory()->undefined_value(),
|
|
Handle<Smi>(Smi::FromInt(kNumProgrammaticParams), isolate))
|
|
.ToHandleChecked();
|
|
CHECK_EQ(1234, Handle<Smi>::cast(result)->value());
|
|
}
|
|
}
|
|
|
|
TEST(OneToTwoByteStringCopy) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 2;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
StringBuiltinsAssembler m(asm_tester.state());
|
|
|
|
m.CopyStringCharacters<String>(m.CAST(m.Parameter(1)), m.CAST(m.Parameter(2)),
|
|
m.IntPtrConstant(0), m.IntPtrConstant(0),
|
|
m.IntPtrConstant(5), String::ONE_BYTE_ENCODING,
|
|
String::TWO_BYTE_ENCODING);
|
|
m.Return(m.SmiConstant(Smi::FromInt(0)));
|
|
|
|
Handle<String> string1 = isolate->factory()->InternalizeUtf8String("abcde");
|
|
uc16 array[] = {1000, 1001, 1002, 1003, 1004};
|
|
Handle<String> string2 = isolate->factory()
|
|
->NewStringFromTwoByte(ArrayVector(array))
|
|
.ToHandleChecked();
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
ft.Call(string1, string2);
|
|
DisallowHeapAllocation no_gc;
|
|
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars(no_gc)[0],
|
|
Handle<SeqTwoByteString>::cast(string2)->GetChars(no_gc)[0]);
|
|
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars(no_gc)[1],
|
|
Handle<SeqTwoByteString>::cast(string2)->GetChars(no_gc)[1]);
|
|
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars(no_gc)[2],
|
|
Handle<SeqTwoByteString>::cast(string2)->GetChars(no_gc)[2]);
|
|
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars(no_gc)[3],
|
|
Handle<SeqTwoByteString>::cast(string2)->GetChars(no_gc)[3]);
|
|
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars(no_gc)[4],
|
|
Handle<SeqTwoByteString>::cast(string2)->GetChars(no_gc)[4]);
|
|
}
|
|
|
|
TEST(OneToOneByteStringCopy) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 2;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
StringBuiltinsAssembler m(asm_tester.state());
|
|
|
|
m.CopyStringCharacters<String>(m.CAST(m.Parameter(1)), m.CAST(m.Parameter(2)),
|
|
m.IntPtrConstant(0), m.IntPtrConstant(0),
|
|
m.IntPtrConstant(5), String::ONE_BYTE_ENCODING,
|
|
String::ONE_BYTE_ENCODING);
|
|
m.Return(m.SmiConstant(Smi::FromInt(0)));
|
|
|
|
Handle<String> string1 = isolate->factory()->InternalizeUtf8String("abcde");
|
|
uint8_t array[] = {100, 101, 102, 103, 104};
|
|
Handle<String> string2 = isolate->factory()
|
|
->NewStringFromOneByte(ArrayVector(array))
|
|
.ToHandleChecked();
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
ft.Call(string1, string2);
|
|
DisallowHeapAllocation no_gc;
|
|
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars(no_gc)[0],
|
|
Handle<SeqOneByteString>::cast(string2)->GetChars(no_gc)[0]);
|
|
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars(no_gc)[1],
|
|
Handle<SeqOneByteString>::cast(string2)->GetChars(no_gc)[1]);
|
|
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars(no_gc)[2],
|
|
Handle<SeqOneByteString>::cast(string2)->GetChars(no_gc)[2]);
|
|
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars(no_gc)[3],
|
|
Handle<SeqOneByteString>::cast(string2)->GetChars(no_gc)[3]);
|
|
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars(no_gc)[4],
|
|
Handle<SeqOneByteString>::cast(string2)->GetChars(no_gc)[4]);
|
|
}
|
|
|
|
TEST(OneToOneByteStringCopyNonZeroStart) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 2;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
StringBuiltinsAssembler m(asm_tester.state());
|
|
|
|
m.CopyStringCharacters<String>(m.CAST(m.Parameter(1)), m.CAST(m.Parameter(2)),
|
|
m.IntPtrConstant(0), m.IntPtrConstant(3),
|
|
m.IntPtrConstant(2), String::ONE_BYTE_ENCODING,
|
|
String::ONE_BYTE_ENCODING);
|
|
m.Return(m.SmiConstant(Smi::FromInt(0)));
|
|
|
|
Handle<String> string1 = isolate->factory()->InternalizeUtf8String("abcde");
|
|
uint8_t array[] = {100, 101, 102, 103, 104};
|
|
Handle<String> string2 = isolate->factory()
|
|
->NewStringFromOneByte(ArrayVector(array))
|
|
.ToHandleChecked();
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
ft.Call(string1, string2);
|
|
DisallowHeapAllocation no_gc;
|
|
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars(no_gc)[0],
|
|
Handle<SeqOneByteString>::cast(string2)->GetChars(no_gc)[3]);
|
|
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars(no_gc)[1],
|
|
Handle<SeqOneByteString>::cast(string2)->GetChars(no_gc)[4]);
|
|
CHECK_EQ(100, Handle<SeqOneByteString>::cast(string2)->GetChars(no_gc)[0]);
|
|
CHECK_EQ(101, Handle<SeqOneByteString>::cast(string2)->GetChars(no_gc)[1]);
|
|
CHECK_EQ(102, Handle<SeqOneByteString>::cast(string2)->GetChars(no_gc)[2]);
|
|
}
|
|
|
|
TEST(TwoToTwoByteStringCopy) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 2;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
StringBuiltinsAssembler m(asm_tester.state());
|
|
|
|
m.CopyStringCharacters<String>(m.CAST(m.Parameter(1)), m.CAST(m.Parameter(2)),
|
|
m.IntPtrConstant(0), m.IntPtrConstant(0),
|
|
m.IntPtrConstant(5), String::TWO_BYTE_ENCODING,
|
|
String::TWO_BYTE_ENCODING);
|
|
m.Return(m.SmiConstant(Smi::FromInt(0)));
|
|
|
|
uc16 array1[] = {2000, 2001, 2002, 2003, 2004};
|
|
Handle<String> string1 = isolate->factory()
|
|
->NewStringFromTwoByte(ArrayVector(array1))
|
|
.ToHandleChecked();
|
|
uc16 array2[] = {1000, 1001, 1002, 1003, 1004};
|
|
Handle<String> string2 = isolate->factory()
|
|
->NewStringFromTwoByte(ArrayVector(array2))
|
|
.ToHandleChecked();
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
ft.Call(string1, string2);
|
|
DisallowHeapAllocation no_gc;
|
|
CHECK_EQ(Handle<SeqTwoByteString>::cast(string1)->GetChars(no_gc)[0],
|
|
Handle<SeqTwoByteString>::cast(string2)->GetChars(no_gc)[0]);
|
|
CHECK_EQ(Handle<SeqTwoByteString>::cast(string1)->GetChars(no_gc)[1],
|
|
Handle<SeqTwoByteString>::cast(string2)->GetChars(no_gc)[1]);
|
|
CHECK_EQ(Handle<SeqTwoByteString>::cast(string1)->GetChars(no_gc)[2],
|
|
Handle<SeqTwoByteString>::cast(string2)->GetChars(no_gc)[2]);
|
|
CHECK_EQ(Handle<SeqTwoByteString>::cast(string1)->GetChars(no_gc)[3],
|
|
Handle<SeqTwoByteString>::cast(string2)->GetChars(no_gc)[3]);
|
|
CHECK_EQ(Handle<SeqTwoByteString>::cast(string1)->GetChars(no_gc)[4],
|
|
Handle<SeqTwoByteString>::cast(string2)->GetChars(no_gc)[4]);
|
|
}
|
|
|
|
TEST(Arguments) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 3;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
CodeStubArguments arguments(&m, m.IntPtrConstant(3));
|
|
|
|
CSA_ASSERT(&m, m.TaggedEqual(arguments.AtIndex(0), m.SmiConstant(12)));
|
|
CSA_ASSERT(&m, m.TaggedEqual(arguments.AtIndex(1), m.SmiConstant(13)));
|
|
CSA_ASSERT(&m, m.TaggedEqual(arguments.AtIndex(2), m.SmiConstant(14)));
|
|
|
|
arguments.PopAndReturn(arguments.GetReceiver());
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result = ft.Call(Handle<Smi>(Smi::FromInt(12), isolate),
|
|
Handle<Smi>(Smi::FromInt(13), isolate),
|
|
Handle<Smi>(Smi::FromInt(14), isolate))
|
|
.ToHandleChecked();
|
|
// When calling with undefined object as the receiver, the CallFunction
|
|
// builtin swaps it to the global proxy object.
|
|
CHECK_EQ(*isolate->global_proxy(), *result);
|
|
}
|
|
|
|
TEST(ArgumentsForEach) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 3;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
CodeStubArguments arguments(&m, m.IntPtrConstant(3));
|
|
|
|
TVariable<Smi> sum(&m);
|
|
CodeAssemblerVariableList list({&sum}, m.zone());
|
|
|
|
sum = m.SmiConstant(0);
|
|
|
|
arguments.ForEach(list, [&](TNode<Object> arg) {
|
|
sum = m.SmiAdd(sum.value(), m.CAST(arg));
|
|
});
|
|
|
|
arguments.PopAndReturn(sum.value());
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result = ft.Call(Handle<Smi>(Smi::FromInt(12), isolate),
|
|
Handle<Smi>(Smi::FromInt(13), isolate),
|
|
Handle<Smi>(Smi::FromInt(14), isolate))
|
|
.ToHandleChecked();
|
|
CHECK_EQ(Smi::FromInt(12 + 13 + 14), *result);
|
|
}
|
|
|
|
TEST(IsDebugActive) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 0;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
Label if_active(&m), if_not_active(&m);
|
|
|
|
m.Branch(m.IsDebugActive(), &if_active, &if_not_active);
|
|
m.BIND(&if_active);
|
|
m.Return(m.TrueConstant());
|
|
m.BIND(&if_not_active);
|
|
m.Return(m.FalseConstant());
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
CHECK(!isolate->debug()->is_active());
|
|
Handle<Object> result =
|
|
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
|
|
CHECK_EQ(ReadOnlyRoots(isolate).false_value(), *result);
|
|
|
|
bool* debug_is_active = reinterpret_cast<bool*>(
|
|
ExternalReference::debug_is_active_address(isolate).address());
|
|
|
|
// Cheat to enable debug (TODO: do this properly).
|
|
*debug_is_active = true;
|
|
|
|
result = ft.Call().ToHandleChecked();
|
|
CHECK_EQ(ReadOnlyRoots(isolate).true_value(), *result);
|
|
|
|
// Reset debug mode.
|
|
*debug_is_active = false;
|
|
}
|
|
|
|
class AppendJSArrayCodeStubAssembler : public CodeStubAssembler {
|
|
public:
|
|
AppendJSArrayCodeStubAssembler(compiler::CodeAssemblerState* state,
|
|
ElementsKind kind)
|
|
: CodeStubAssembler(state), kind_(kind) {}
|
|
|
|
void TestAppendJSArrayImpl(Isolate* isolate, CodeAssemblerTester* csa_tester,
|
|
Handle<Object> o1, Handle<Object> o2,
|
|
Handle<Object> o3, Handle<Object> o4,
|
|
int initial_size, int result_size) {
|
|
Handle<JSArray> array = isolate->factory()->NewJSArray(
|
|
kind_, 2, initial_size, INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE);
|
|
Object::SetElement(isolate, array, 0, Handle<Smi>(Smi::FromInt(1), isolate),
|
|
kDontThrow)
|
|
.Check();
|
|
Object::SetElement(isolate, array, 1, Handle<Smi>(Smi::FromInt(2), isolate),
|
|
kDontThrow)
|
|
.Check();
|
|
CodeStubArguments args(this, IntPtrConstant(kNumParams));
|
|
TVariable<IntPtrT> arg_index(this);
|
|
Label bailout(this);
|
|
arg_index = IntPtrConstant(0);
|
|
TNode<Smi> length = BuildAppendJSArray(kind_, HeapConstant(array), &args,
|
|
&arg_index, &bailout);
|
|
Return(length);
|
|
|
|
BIND(&bailout);
|
|
Return(SmiTag(IntPtrAdd(arg_index.value(), IntPtrConstant(2))));
|
|
|
|
FunctionTester ft(csa_tester->GenerateCode(), kNumParams);
|
|
|
|
Handle<Object> result = ft.Call(o1, o2, o3, o4).ToHandleChecked();
|
|
|
|
CHECK_EQ(kind_, array->GetElementsKind());
|
|
CHECK_EQ(result_size, Handle<Smi>::cast(result)->value());
|
|
CHECK_EQ(result_size, Smi::ToInt(array->length()));
|
|
Handle<Object> obj =
|
|
JSObject::GetElement(isolate, array, 2).ToHandleChecked();
|
|
Handle<HeapObject> undefined_value =
|
|
Handle<HeapObject>(ReadOnlyRoots(isolate).undefined_value(), isolate);
|
|
CHECK_EQ(result_size < 3 ? *undefined_value : *o1, *obj);
|
|
obj = JSObject::GetElement(isolate, array, 3).ToHandleChecked();
|
|
CHECK_EQ(result_size < 4 ? *undefined_value : *o2, *obj);
|
|
obj = JSObject::GetElement(isolate, array, 4).ToHandleChecked();
|
|
CHECK_EQ(result_size < 5 ? *undefined_value : *o3, *obj);
|
|
obj = JSObject::GetElement(isolate, array, 5).ToHandleChecked();
|
|
CHECK_EQ(result_size < 6 ? *undefined_value : *o4, *obj);
|
|
}
|
|
|
|
static void TestAppendJSArray(Isolate* isolate, ElementsKind kind, Object o1,
|
|
Object o2, Object o3, Object o4,
|
|
int initial_size, int result_size) {
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
AppendJSArrayCodeStubAssembler m(asm_tester.state(), kind);
|
|
m.TestAppendJSArrayImpl(
|
|
isolate, &asm_tester, Handle<Object>(o1, isolate),
|
|
Handle<Object>(o2, isolate), Handle<Object>(o3, isolate),
|
|
Handle<Object>(o4, isolate), initial_size, result_size);
|
|
}
|
|
|
|
private:
|
|
static const int kNumParams = 4;
|
|
ElementsKind kind_;
|
|
};
|
|
|
|
TEST(BuildAppendJSArrayFastElement) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
AppendJSArrayCodeStubAssembler::TestAppendJSArray(
|
|
isolate, PACKED_ELEMENTS, Smi::FromInt(3), Smi::FromInt(4),
|
|
Smi::FromInt(5), Smi::FromInt(6), 6, 6);
|
|
}
|
|
|
|
TEST(BuildAppendJSArrayFastElementGrow) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
AppendJSArrayCodeStubAssembler::TestAppendJSArray(
|
|
isolate, PACKED_ELEMENTS, Smi::FromInt(3), Smi::FromInt(4),
|
|
Smi::FromInt(5), Smi::FromInt(6), 2, 6);
|
|
}
|
|
|
|
TEST(BuildAppendJSArrayFastSmiElement) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
AppendJSArrayCodeStubAssembler::TestAppendJSArray(
|
|
isolate, PACKED_SMI_ELEMENTS, Smi::FromInt(3), Smi::FromInt(4),
|
|
Smi::FromInt(5), Smi::FromInt(6), 6, 6);
|
|
}
|
|
|
|
TEST(BuildAppendJSArrayFastSmiElementGrow) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
AppendJSArrayCodeStubAssembler::TestAppendJSArray(
|
|
isolate, PACKED_SMI_ELEMENTS, Smi::FromInt(3), Smi::FromInt(4),
|
|
Smi::FromInt(5), Smi::FromInt(6), 2, 6);
|
|
}
|
|
|
|
TEST(BuildAppendJSArrayFastSmiElementObject) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
AppendJSArrayCodeStubAssembler::TestAppendJSArray(
|
|
isolate, PACKED_SMI_ELEMENTS, Smi::FromInt(3), Smi::FromInt(4),
|
|
ReadOnlyRoots(isolate).undefined_value(), Smi::FromInt(6), 6, 4);
|
|
}
|
|
|
|
TEST(BuildAppendJSArrayFastSmiElementObjectGrow) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
AppendJSArrayCodeStubAssembler::TestAppendJSArray(
|
|
isolate, PACKED_SMI_ELEMENTS, Smi::FromInt(3), Smi::FromInt(4),
|
|
ReadOnlyRoots(isolate).undefined_value(), Smi::FromInt(6), 2, 4);
|
|
}
|
|
|
|
TEST(BuildAppendJSArrayFastDoubleElements) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
AppendJSArrayCodeStubAssembler::TestAppendJSArray(
|
|
isolate, PACKED_DOUBLE_ELEMENTS, Smi::FromInt(3), Smi::FromInt(4),
|
|
Smi::FromInt(5), Smi::FromInt(6), 6, 6);
|
|
}
|
|
|
|
TEST(BuildAppendJSArrayFastDoubleElementsGrow) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
AppendJSArrayCodeStubAssembler::TestAppendJSArray(
|
|
isolate, PACKED_DOUBLE_ELEMENTS, Smi::FromInt(3), Smi::FromInt(4),
|
|
Smi::FromInt(5), Smi::FromInt(6), 2, 6);
|
|
}
|
|
|
|
TEST(BuildAppendJSArrayFastDoubleElementsObject) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
AppendJSArrayCodeStubAssembler::TestAppendJSArray(
|
|
isolate, PACKED_DOUBLE_ELEMENTS, Smi::FromInt(3), Smi::FromInt(4),
|
|
ReadOnlyRoots(isolate).undefined_value(), Smi::FromInt(6), 6, 4);
|
|
}
|
|
|
|
namespace {
|
|
|
|
template <typename Stub, typename... Args>
|
|
void Recompile(Args... args) {
|
|
Stub stub(args...);
|
|
stub.DeleteStubFromCacheForTesting();
|
|
stub.GetCode();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void CustomPromiseHook(v8::PromiseHookType type, v8::Local<v8::Promise> promise,
|
|
v8::Local<v8::Value> parentPromise) {}
|
|
|
|
TEST(IsPromiseHookEnabled) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
m.Return(
|
|
m.SelectBooleanConstant(m.IsPromiseHookEnabledOrHasAsyncEventDelegate()));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result =
|
|
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
|
|
CHECK_EQ(ReadOnlyRoots(isolate).false_value(), *result);
|
|
|
|
isolate->SetPromiseHook(CustomPromiseHook);
|
|
result = ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
|
|
CHECK_EQ(ReadOnlyRoots(isolate).true_value(), *result);
|
|
|
|
isolate->SetPromiseHook(nullptr);
|
|
result = ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
|
|
CHECK_EQ(ReadOnlyRoots(isolate).false_value(), *result);
|
|
}
|
|
|
|
TEST(NewJSPromise) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
PromiseBuiltinsAssembler m(asm_tester.state());
|
|
|
|
Node* const context = m.Parameter(kNumParams + 2);
|
|
const TNode<JSPromise> promise = m.NewJSPromise(m.CAST(context));
|
|
m.Return(promise);
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result =
|
|
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
|
|
CHECK(result->IsJSPromise());
|
|
}
|
|
|
|
TEST(NewJSPromise2) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
PromiseBuiltinsAssembler m(asm_tester.state());
|
|
|
|
Node* const context = m.Parameter(kNumParams + 2);
|
|
const TNode<JSPromise> promise =
|
|
m.NewJSPromise(m.CAST(context), v8::Promise::kRejected, m.SmiConstant(1));
|
|
m.Return(promise);
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result =
|
|
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
|
|
CHECK(result->IsJSPromise());
|
|
Handle<JSPromise> js_promise = Handle<JSPromise>::cast(result);
|
|
CHECK_EQ(v8::Promise::kRejected, js_promise->status());
|
|
CHECK_EQ(Smi::FromInt(1), js_promise->result());
|
|
CHECK(!js_promise->has_handler());
|
|
}
|
|
|
|
TEST(IsSymbol) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
TNode<HeapObject> const symbol = m.CAST(m.Parameter(1));
|
|
m.Return(m.SelectBooleanConstant(m.IsSymbol(symbol)));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result =
|
|
ft.Call(isolate->factory()->NewSymbol()).ToHandleChecked();
|
|
CHECK_EQ(ReadOnlyRoots(isolate).true_value(), *result);
|
|
|
|
result = ft.Call(isolate->factory()->empty_string()).ToHandleChecked();
|
|
CHECK_EQ(ReadOnlyRoots(isolate).false_value(), *result);
|
|
}
|
|
|
|
TEST(IsPrivateSymbol) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
TNode<HeapObject> const symbol = m.CAST(m.Parameter(1));
|
|
m.Return(m.SelectBooleanConstant(m.IsPrivateSymbol(symbol)));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result =
|
|
ft.Call(isolate->factory()->NewSymbol()).ToHandleChecked();
|
|
CHECK_EQ(ReadOnlyRoots(isolate).false_value(), *result);
|
|
|
|
result = ft.Call(isolate->factory()->empty_string()).ToHandleChecked();
|
|
CHECK_EQ(ReadOnlyRoots(isolate).false_value(), *result);
|
|
|
|
result = ft.Call(isolate->factory()->NewPrivateSymbol()).ToHandleChecked();
|
|
CHECK_EQ(ReadOnlyRoots(isolate).true_value(), *result);
|
|
}
|
|
|
|
TEST(PromiseHasHandler) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
PromiseBuiltinsAssembler m(asm_tester.state());
|
|
|
|
Node* const context = m.Parameter(kNumParams + 2);
|
|
const TNode<JSPromise> promise =
|
|
m.NewJSPromise(m.CAST(context), m.UndefinedConstant());
|
|
m.Return(m.SelectBooleanConstant(m.PromiseHasHandler(promise)));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result =
|
|
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
|
|
CHECK_EQ(ReadOnlyRoots(isolate).false_value(), *result);
|
|
}
|
|
|
|
TEST(CreatePromiseResolvingFunctionsContext) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 0;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
PromiseBuiltinsAssembler m(asm_tester.state());
|
|
|
|
const TNode<Context> context = m.CAST(m.Parameter(kNumParams + 3));
|
|
const TNode<NativeContext> native_context = m.LoadNativeContext(context);
|
|
const TNode<JSPromise> promise =
|
|
m.NewJSPromise(context, m.UndefinedConstant());
|
|
const TNode<Context> promise_context =
|
|
m.CreatePromiseResolvingFunctionsContext(
|
|
context, promise, m.BooleanConstant(false), native_context);
|
|
m.Return(promise_context);
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result = ft.Call().ToHandleChecked();
|
|
CHECK(result->IsContext());
|
|
Handle<Context> context_js = Handle<Context>::cast(result);
|
|
CHECK_EQ(isolate->root(RootIndex::kEmptyScopeInfo), context_js->scope_info());
|
|
CHECK_EQ(*isolate->native_context(), context_js->native_context());
|
|
CHECK(context_js->get(PromiseBuiltins::kPromiseSlot).IsJSPromise());
|
|
CHECK_EQ(ReadOnlyRoots(isolate).false_value(),
|
|
context_js->get(PromiseBuiltins::kDebugEventSlot));
|
|
}
|
|
|
|
TEST(CreatePromiseResolvingFunctions) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
PromiseBuiltinsAssembler m(asm_tester.state());
|
|
|
|
Node* const context = m.Parameter(kNumParams + 2);
|
|
const TNode<NativeContext> native_context = m.LoadNativeContext(context);
|
|
const TNode<JSPromise> promise =
|
|
m.NewJSPromise(m.CAST(context), m.UndefinedConstant());
|
|
PromiseResolvingFunctions funcs = m.CreatePromiseResolvingFunctions(
|
|
m.CAST(context), promise, m.BooleanConstant(false), native_context);
|
|
Node *resolve = funcs.resolve, *reject = funcs.reject;
|
|
TNode<IntPtrT> const kSize = m.IntPtrConstant(2);
|
|
TNode<FixedArray> const arr =
|
|
m.Cast(m.AllocateFixedArray(PACKED_ELEMENTS, kSize));
|
|
m.StoreFixedArrayElement(arr, 0, resolve);
|
|
m.StoreFixedArrayElement(arr, 1, reject);
|
|
m.Return(arr);
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result_obj =
|
|
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
|
|
CHECK(result_obj->IsFixedArray());
|
|
Handle<FixedArray> result_arr = Handle<FixedArray>::cast(result_obj);
|
|
CHECK(result_arr->get(0).IsJSFunction());
|
|
CHECK(result_arr->get(1).IsJSFunction());
|
|
}
|
|
|
|
TEST(NewElementsCapacity) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
CodeAssemblerTester asm_tester(isolate, 2);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
m.Return(
|
|
m.SmiTag(m.CalculateNewElementsCapacity(m.SmiUntag(m.Parameter(1)))));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), 1);
|
|
Handle<Smi> test_value = Handle<Smi>(Smi::FromInt(1), isolate);
|
|
Handle<Smi> result_obj = ft.CallChecked<Smi>(test_value);
|
|
CHECK_EQ(
|
|
result_obj->value(),
|
|
static_cast<int>(JSObject::NewElementsCapacity(test_value->value())));
|
|
test_value = Handle<Smi>(Smi::FromInt(1), isolate);
|
|
result_obj = ft.CallChecked<Smi>(test_value);
|
|
CHECK_EQ(
|
|
result_obj->value(),
|
|
static_cast<int>(JSObject::NewElementsCapacity(test_value->value())));
|
|
test_value = Handle<Smi>(Smi::FromInt(2), isolate);
|
|
result_obj = ft.CallChecked<Smi>(test_value);
|
|
CHECK_EQ(
|
|
result_obj->value(),
|
|
static_cast<int>(JSObject::NewElementsCapacity(test_value->value())));
|
|
test_value = Handle<Smi>(Smi::FromInt(1025), isolate);
|
|
result_obj = ft.CallChecked<Smi>(test_value);
|
|
CHECK_EQ(
|
|
result_obj->value(),
|
|
static_cast<int>(JSObject::NewElementsCapacity(test_value->value())));
|
|
}
|
|
|
|
TEST(NewElementsCapacitySmi) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
CodeAssemblerTester asm_tester(isolate, 2);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
m.Return(
|
|
m.CalculateNewElementsCapacity(m.UncheckedCast<Smi>(m.Parameter(1))));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), 1);
|
|
Handle<Smi> test_value = Handle<Smi>(Smi::FromInt(0), isolate);
|
|
Handle<Smi> result_obj = ft.CallChecked<Smi>(test_value);
|
|
CHECK_EQ(
|
|
result_obj->value(),
|
|
static_cast<int>(JSObject::NewElementsCapacity(test_value->value())));
|
|
test_value = Handle<Smi>(Smi::FromInt(1), isolate);
|
|
result_obj = ft.CallChecked<Smi>(test_value);
|
|
CHECK_EQ(
|
|
result_obj->value(),
|
|
static_cast<int>(JSObject::NewElementsCapacity(test_value->value())));
|
|
test_value = Handle<Smi>(Smi::FromInt(2), isolate);
|
|
result_obj = ft.CallChecked<Smi>(test_value);
|
|
CHECK_EQ(
|
|
result_obj->value(),
|
|
static_cast<int>(JSObject::NewElementsCapacity(test_value->value())));
|
|
test_value = Handle<Smi>(Smi::FromInt(1025), isolate);
|
|
result_obj = ft.CallChecked<Smi>(test_value);
|
|
CHECK_EQ(
|
|
result_obj->value(),
|
|
static_cast<int>(JSObject::NewElementsCapacity(test_value->value())));
|
|
}
|
|
|
|
TEST(AllocateFunctionWithMapAndContext) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
PromiseBuiltinsAssembler m(asm_tester.state());
|
|
|
|
const TNode<Context> context = m.CAST(m.Parameter(kNumParams + 2));
|
|
const TNode<NativeContext> native_context = m.LoadNativeContext(context);
|
|
const TNode<JSPromise> promise =
|
|
m.NewJSPromise(context, m.UndefinedConstant());
|
|
TNode<Context> promise_context = m.CreatePromiseResolvingFunctionsContext(
|
|
context, promise, m.BooleanConstant(false), native_context);
|
|
TNode<Object> resolve_info =
|
|
m.PromiseCapabilityDefaultResolveSharedFunConstant();
|
|
const TNode<Object> map = m.LoadContextElement(
|
|
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
|
|
const TNode<JSFunction> resolve = m.AllocateFunctionWithMapAndContext(
|
|
m.CAST(map), m.CAST(resolve_info), promise_context);
|
|
m.Return(resolve);
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result_obj =
|
|
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
|
|
CHECK(result_obj->IsJSFunction());
|
|
Handle<JSFunction> fun = Handle<JSFunction>::cast(result_obj);
|
|
CHECK_EQ(ReadOnlyRoots(isolate).empty_property_array(),
|
|
fun->property_array());
|
|
CHECK_EQ(ReadOnlyRoots(isolate).empty_fixed_array(), fun->elements());
|
|
CHECK_EQ(isolate->heap()->many_closures_cell(), fun->raw_feedback_cell());
|
|
CHECK(!fun->has_prototype_slot());
|
|
CHECK_EQ(*isolate->factory()->promise_capability_default_resolve_shared_fun(),
|
|
fun->shared());
|
|
CHECK_EQ(isolate->factory()
|
|
->promise_capability_default_resolve_shared_fun()
|
|
->GetCode(),
|
|
fun->code());
|
|
}
|
|
|
|
TEST(CreatePromiseGetCapabilitiesExecutorContext) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
PromiseBuiltinsAssembler m(asm_tester.state());
|
|
|
|
TNode<Context> context = m.CAST(m.Parameter(kNumParams + 2));
|
|
TNode<NativeContext> native_context = m.LoadNativeContext(context);
|
|
|
|
TNode<PromiseCapability> capability = m.CreatePromiseCapability(
|
|
m.UndefinedConstant(), m.UndefinedConstant(), m.UndefinedConstant());
|
|
TNode<Context> executor_context =
|
|
m.CreatePromiseCapabilitiesExecutorContext(native_context, capability);
|
|
m.Return(executor_context);
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result_obj =
|
|
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
|
|
CHECK(result_obj->IsContext());
|
|
Handle<Context> context_js = Handle<Context>::cast(result_obj);
|
|
CHECK_EQ(PromiseBuiltins::kCapabilitiesContextLength, context_js->length());
|
|
CHECK_EQ(isolate->root(RootIndex::kEmptyScopeInfo), context_js->scope_info());
|
|
CHECK_EQ(*isolate->native_context(), context_js->native_context());
|
|
CHECK(
|
|
context_js->get(PromiseBuiltins::kCapabilitySlot).IsPromiseCapability());
|
|
}
|
|
|
|
TEST(NewPromiseCapability) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
{ // Builtin Promise
|
|
const int kNumParams = 0;
|
|
CodeAssemblerTester asm_tester(isolate,
|
|
kNumParams + 1); // Include receiver.
|
|
PromiseBuiltinsAssembler m(asm_tester.state());
|
|
|
|
Node* const context = m.Parameter(kNumParams + 3);
|
|
const TNode<NativeContext> native_context = m.LoadNativeContext(context);
|
|
const TNode<Object> promise_constructor =
|
|
m.LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
|
|
|
|
const TNode<Oddball> debug_event = m.TrueConstant();
|
|
const TNode<Object> capability =
|
|
m.CallBuiltin(Builtins::kNewPromiseCapability, context,
|
|
promise_constructor, debug_event);
|
|
m.Return(capability);
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<Object> result_obj = ft.Call().ToHandleChecked();
|
|
CHECK(result_obj->IsPromiseCapability());
|
|
Handle<PromiseCapability> result =
|
|
Handle<PromiseCapability>::cast(result_obj);
|
|
|
|
CHECK(result->promise().IsJSPromise());
|
|
CHECK(result->resolve().IsJSFunction());
|
|
CHECK(result->reject().IsJSFunction());
|
|
CHECK_EQ(
|
|
*isolate->factory()->promise_capability_default_reject_shared_fun(),
|
|
JSFunction::cast(result->reject()).shared());
|
|
CHECK_EQ(
|
|
*isolate->factory()->promise_capability_default_resolve_shared_fun(),
|
|
JSFunction::cast(result->resolve()).shared());
|
|
|
|
Handle<JSFunction> callbacks[] = {
|
|
handle(JSFunction::cast(result->resolve()), isolate),
|
|
handle(JSFunction::cast(result->reject()), isolate)};
|
|
|
|
for (auto&& callback : callbacks) {
|
|
Handle<Context> context(Context::cast(callback->context()), isolate);
|
|
CHECK_EQ(isolate->root(RootIndex::kEmptyScopeInfo),
|
|
context->scope_info());
|
|
CHECK_EQ(*isolate->native_context(), context->native_context());
|
|
CHECK_EQ(PromiseBuiltins::kPromiseContextLength, context->length());
|
|
CHECK_EQ(context->get(PromiseBuiltins::kPromiseSlot), result->promise());
|
|
}
|
|
}
|
|
|
|
{ // Custom Promise
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate,
|
|
kNumParams + 1); // Include receiver.
|
|
PromiseBuiltinsAssembler m(asm_tester.state());
|
|
|
|
Node* const context = m.Parameter(kNumParams + 3);
|
|
|
|
Node* const constructor = m.Parameter(1);
|
|
const TNode<Oddball> debug_event = m.TrueConstant();
|
|
const TNode<Object> capability = m.CallBuiltin(
|
|
Builtins::kNewPromiseCapability, context, constructor, debug_event);
|
|
m.Return(capability);
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<JSFunction> constructor_fn =
|
|
Handle<JSFunction>::cast(v8::Utils::OpenHandle(*CompileRun(
|
|
"(function FakePromise(executor) {"
|
|
" var self = this;"
|
|
" function resolve(value) { self.resolvedValue = value; }"
|
|
" function reject(reason) { self.rejectedReason = reason; }"
|
|
" executor(resolve, reject);"
|
|
"})")));
|
|
|
|
Handle<Object> result_obj = ft.Call(constructor_fn).ToHandleChecked();
|
|
CHECK(result_obj->IsPromiseCapability());
|
|
Handle<PromiseCapability> result =
|
|
Handle<PromiseCapability>::cast(result_obj);
|
|
|
|
CHECK(result->promise().IsJSObject());
|
|
Handle<JSObject> promise(JSObject::cast(result->promise()), isolate);
|
|
CHECK_EQ(constructor_fn->prototype_or_initial_map(), promise->map());
|
|
CHECK(result->resolve().IsJSFunction());
|
|
CHECK(result->reject().IsJSFunction());
|
|
|
|
Handle<String> resolved_str =
|
|
isolate->factory()->NewStringFromAsciiChecked("resolvedStr");
|
|
Handle<String> rejected_str =
|
|
isolate->factory()->NewStringFromAsciiChecked("rejectedStr");
|
|
|
|
Handle<Object> argv1[] = {resolved_str};
|
|
Handle<Object> ret =
|
|
Execution::Call(isolate, handle(result->resolve(), isolate),
|
|
isolate->factory()->undefined_value(), 1, argv1)
|
|
.ToHandleChecked();
|
|
|
|
Handle<Object> prop1 =
|
|
JSReceiver::GetProperty(isolate, promise, "resolvedValue")
|
|
.ToHandleChecked();
|
|
CHECK_EQ(*resolved_str, *prop1);
|
|
|
|
Handle<Object> argv2[] = {rejected_str};
|
|
ret = Execution::Call(isolate, handle(result->reject(), isolate),
|
|
isolate->factory()->undefined_value(), 1, argv2)
|
|
.ToHandleChecked();
|
|
Handle<Object> prop2 =
|
|
JSReceiver::GetProperty(isolate, promise, "rejectedReason")
|
|
.ToHandleChecked();
|
|
CHECK_EQ(*rejected_str, *prop2);
|
|
}
|
|
}
|
|
|
|
TEST(DirectMemoryTest8BitWord32Immediate) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 0;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
int8_t buffer[] = {1, 2, 4, 8, 17, 33, 65, 127};
|
|
const int element_count = 8;
|
|
Label bad(&m);
|
|
|
|
TNode<RawPtrT> buffer_node = m.PointerConstant(buffer);
|
|
for (size_t i = 0; i < element_count; ++i) {
|
|
for (size_t j = 0; j < element_count; ++j) {
|
|
TNode<Uint8T> loaded =
|
|
m.LoadBufferData<Uint8T>(buffer_node, static_cast<int>(i));
|
|
TNode<Word32T> masked = m.Word32And(loaded, m.Int32Constant(buffer[j]));
|
|
if ((buffer[j] & buffer[i]) != 0) {
|
|
m.GotoIf(m.Word32Equal(masked, m.Int32Constant(0)), &bad);
|
|
} else {
|
|
m.GotoIf(m.Word32NotEqual(masked, m.Int32Constant(0)), &bad);
|
|
}
|
|
}
|
|
}
|
|
|
|
m.Return(m.SmiConstant(1));
|
|
|
|
m.BIND(&bad);
|
|
m.Return(m.SmiConstant(0));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
CHECK_EQ(1, ft.CallChecked<Smi>()->value());
|
|
}
|
|
|
|
TEST(DirectMemoryTest16BitWord32Immediate) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 0;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
int16_t buffer[] = {156, 2234, 4544, 8444, 1723, 3888, 658, 1278};
|
|
const int element_count = 8;
|
|
Label bad(&m);
|
|
|
|
TNode<RawPtrT> buffer_node = m.PointerConstant(buffer);
|
|
for (size_t i = 0; i < element_count; ++i) {
|
|
for (size_t j = 0; j < element_count; ++j) {
|
|
TNode<Uint16T> loaded = m.LoadBufferData<Uint16T>(
|
|
buffer_node, static_cast<int>(i * sizeof(int16_t)));
|
|
TNode<Word32T> masked = m.Word32And(loaded, m.Int32Constant(buffer[j]));
|
|
if ((buffer[j] & buffer[i]) != 0) {
|
|
m.GotoIf(m.Word32Equal(masked, m.Int32Constant(0)), &bad);
|
|
} else {
|
|
m.GotoIf(m.Word32NotEqual(masked, m.Int32Constant(0)), &bad);
|
|
}
|
|
}
|
|
}
|
|
|
|
m.Return(m.SmiConstant(1));
|
|
|
|
m.BIND(&bad);
|
|
m.Return(m.SmiConstant(0));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
CHECK_EQ(1, ft.CallChecked<Smi>()->value());
|
|
}
|
|
|
|
TEST(DirectMemoryTest8BitWord32) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 0;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
int8_t buffer[] = {1, 2, 4, 8, 17, 33, 65, 127, 67, 38};
|
|
const int element_count = 10;
|
|
Label bad(&m);
|
|
TNode<Uint32T> constants[element_count];
|
|
|
|
TNode<RawPtrT> buffer_node = m.PointerConstant(buffer);
|
|
for (size_t i = 0; i < element_count; ++i) {
|
|
constants[i] = m.LoadBufferData<Uint8T>(buffer_node, static_cast<int>(i));
|
|
}
|
|
|
|
for (size_t i = 0; i < element_count; ++i) {
|
|
for (size_t j = 0; j < element_count; ++j) {
|
|
TNode<Uint8T> loaded =
|
|
m.LoadBufferData<Uint8T>(buffer_node, static_cast<int>(i));
|
|
TNode<Word32T> masked = m.Word32And(loaded, constants[j]);
|
|
if ((buffer[j] & buffer[i]) != 0) {
|
|
m.GotoIf(m.Word32Equal(masked, m.Int32Constant(0)), &bad);
|
|
} else {
|
|
m.GotoIf(m.Word32NotEqual(masked, m.Int32Constant(0)), &bad);
|
|
}
|
|
|
|
masked = m.Word32And(constants[i], constants[j]);
|
|
if ((buffer[j] & buffer[i]) != 0) {
|
|
m.GotoIf(m.Word32Equal(masked, m.Int32Constant(0)), &bad);
|
|
} else {
|
|
m.GotoIf(m.Word32NotEqual(masked, m.Int32Constant(0)), &bad);
|
|
}
|
|
}
|
|
}
|
|
|
|
m.Return(m.SmiConstant(1));
|
|
|
|
m.BIND(&bad);
|
|
m.Return(m.SmiConstant(0));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
CHECK_EQ(1, ft.CallChecked<Smi>()->value());
|
|
}
|
|
|
|
TEST(DirectMemoryTest16BitWord32) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 0;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
int16_t buffer[] = {1, 2, 4, 8, 12345, 33, 65, 255, 67, 3823};
|
|
const int element_count = 10;
|
|
Label bad(&m);
|
|
TNode<Uint32T> constants[element_count];
|
|
|
|
TNode<RawPtrT> buffer_node1 = m.PointerConstant(buffer);
|
|
for (size_t i = 0; i < element_count; ++i) {
|
|
constants[i] = m.LoadBufferData<Uint16T>(
|
|
buffer_node1, static_cast<int>(i * sizeof(int16_t)));
|
|
}
|
|
TNode<RawPtrT> buffer_node2 = m.PointerConstant(buffer);
|
|
|
|
for (size_t i = 0; i < element_count; ++i) {
|
|
for (size_t j = 0; j < element_count; ++j) {
|
|
Node* loaded = m.LoadBufferData<Uint16T>(
|
|
buffer_node1, static_cast<int>(i * sizeof(int16_t)));
|
|
TNode<Word32T> masked = m.Word32And(loaded, constants[j]);
|
|
if ((buffer[j] & buffer[i]) != 0) {
|
|
m.GotoIf(m.Word32Equal(masked, m.Int32Constant(0)), &bad);
|
|
} else {
|
|
m.GotoIf(m.Word32NotEqual(masked, m.Int32Constant(0)), &bad);
|
|
}
|
|
|
|
// Force a memory access relative to a high-number register.
|
|
loaded = m.LoadBufferData<Uint16T>(buffer_node2,
|
|
static_cast<int>(i * sizeof(int16_t)));
|
|
masked = m.Word32And(loaded, constants[j]);
|
|
if ((buffer[j] & buffer[i]) != 0) {
|
|
m.GotoIf(m.Word32Equal(masked, m.Int32Constant(0)), &bad);
|
|
} else {
|
|
m.GotoIf(m.Word32NotEqual(masked, m.Int32Constant(0)), &bad);
|
|
}
|
|
|
|
masked = m.Word32And(constants[i], constants[j]);
|
|
if ((buffer[j] & buffer[i]) != 0) {
|
|
m.GotoIf(m.Word32Equal(masked, m.Int32Constant(0)), &bad);
|
|
} else {
|
|
m.GotoIf(m.Word32NotEqual(masked, m.Int32Constant(0)), &bad);
|
|
}
|
|
}
|
|
}
|
|
|
|
m.Return(m.SmiConstant(1));
|
|
|
|
m.BIND(&bad);
|
|
m.Return(m.SmiConstant(0));
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
CHECK_EQ(1, ft.CallChecked<Smi>()->value());
|
|
}
|
|
|
|
TEST(LoadJSArrayElementsMap) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester.state());
|
|
Node* context = m.Parameter(kNumParams + 3);
|
|
TNode<NativeContext> native_context = m.LoadNativeContext(context);
|
|
TNode<Int32T> kind = m.SmiToInt32(m.Parameter(1));
|
|
m.Return(m.LoadJSArrayElementsMap(kind, native_context));
|
|
}
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
for (int kind = 0; kind <= HOLEY_DOUBLE_ELEMENTS; kind++) {
|
|
Handle<Map> csa_result =
|
|
ft.CallChecked<Map>(handle(Smi::FromInt(kind), isolate));
|
|
ElementsKind elements_kind = static_cast<ElementsKind>(kind);
|
|
Handle<Map> result(
|
|
isolate->native_context()->GetInitialJSArrayMap(elements_kind),
|
|
isolate);
|
|
CHECK_EQ(*csa_result, *result);
|
|
}
|
|
}
|
|
|
|
TEST(GotoIfNotWhiteSpaceOrLineTerminator) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
StringTrimAssembler m(asm_tester.state());
|
|
|
|
{ // Returns true if whitespace, false otherwise.
|
|
Label if_not_whitespace(&m);
|
|
|
|
m.GotoIfNotWhiteSpaceOrLineTerminator(m.SmiToInt32(m.Parameter(1)),
|
|
&if_not_whitespace);
|
|
m.Return(m.TrueConstant());
|
|
|
|
m.BIND(&if_not_whitespace);
|
|
m.Return(m.FalseConstant());
|
|
}
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<Object> true_value = ft.true_value();
|
|
Handle<Object> false_value = ft.false_value();
|
|
|
|
for (uc16 c = 0; c < 0xFFFF; c++) {
|
|
Handle<Object> expected_value =
|
|
IsWhiteSpaceOrLineTerminator(c) ? true_value : false_value;
|
|
ft.CheckCall(expected_value, handle(Smi::FromInt(c), isolate));
|
|
}
|
|
}
|
|
|
|
TEST(BranchIfNumberRelationalComparison) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
Factory* f = isolate->factory();
|
|
const int kNumParams = 2;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester.state());
|
|
Label return_true(&m), return_false(&m);
|
|
m.BranchIfNumberRelationalComparison(Operation::kGreaterThanOrEqual,
|
|
m.Parameter(1), m.Parameter(2),
|
|
&return_true, &return_false);
|
|
m.BIND(&return_true);
|
|
m.Return(m.BooleanConstant(true));
|
|
m.BIND(&return_false);
|
|
m.Return(m.BooleanConstant(false));
|
|
}
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
ft.CheckTrue(f->NewNumber(0), f->NewNumber(0));
|
|
ft.CheckTrue(f->NewNumber(1), f->NewNumber(0));
|
|
ft.CheckTrue(f->NewNumber(1), f->NewNumber(1));
|
|
ft.CheckFalse(f->NewNumber(0), f->NewNumber(1));
|
|
ft.CheckFalse(f->NewNumber(-1), f->NewNumber(0));
|
|
ft.CheckTrue(f->NewNumber(-1), f->NewNumber(-1));
|
|
|
|
ft.CheckTrue(f->NewNumber(-1), f->NewNumber(-1.5));
|
|
ft.CheckFalse(f->NewNumber(-1.5), f->NewNumber(-1));
|
|
ft.CheckTrue(f->NewNumber(-1.5), f->NewNumber(-1.5));
|
|
}
|
|
|
|
TEST(IsNumberArrayIndex) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester.state());
|
|
TNode<Number> number = m.CAST(m.Parameter(1));
|
|
m.Return(
|
|
m.SmiFromInt32(m.UncheckedCast<Int32T>(m.IsNumberArrayIndex(number))));
|
|
}
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
double indices[] = {Smi::kMinValue,
|
|
-11,
|
|
-1,
|
|
0,
|
|
1,
|
|
2,
|
|
Smi::kMaxValue,
|
|
-11.0,
|
|
-11.1,
|
|
-2.0,
|
|
-1.0,
|
|
-0.0,
|
|
0.0,
|
|
0.00001,
|
|
0.1,
|
|
1,
|
|
2,
|
|
Smi::kMinValue - 1.0,
|
|
Smi::kMinValue + 1.0,
|
|
Smi::kMinValue + 1.2,
|
|
kMaxInt + 1.2,
|
|
kMaxInt - 10.0,
|
|
kMaxInt - 1.0,
|
|
kMaxInt,
|
|
kMaxInt + 1.0,
|
|
kMaxInt + 10.0};
|
|
|
|
for (size_t i = 0; i < arraysize(indices); i++) {
|
|
Handle<Object> index = isolate->factory()->NewNumber(indices[i]);
|
|
uint32_t array_index;
|
|
CHECK_EQ(index->ToArrayIndex(&array_index),
|
|
(ft.CallChecked<Smi>(index)->value() == 1));
|
|
}
|
|
}
|
|
|
|
TEST(NumberMinMax) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 2;
|
|
CodeAssemblerTester asm_tester_min(isolate,
|
|
kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester_min.state());
|
|
m.Return(m.NumberMin(m.Parameter(1), m.Parameter(2)));
|
|
}
|
|
FunctionTester ft_min(asm_tester_min.GenerateCode(), kNumParams);
|
|
|
|
CodeAssemblerTester asm_tester_max(isolate,
|
|
kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester_max.state());
|
|
m.Return(m.NumberMax(m.Parameter(1), m.Parameter(2)));
|
|
}
|
|
FunctionTester ft_max(asm_tester_max.GenerateCode(), kNumParams);
|
|
|
|
// Test smi values.
|
|
Handle<Smi> smi_1(Smi::FromInt(1), isolate);
|
|
Handle<Smi> smi_2(Smi::FromInt(2), isolate);
|
|
Handle<Smi> smi_5(Smi::FromInt(5), isolate);
|
|
CHECK_EQ(ft_min.CallChecked<Smi>(smi_1, smi_2)->value(), 1);
|
|
CHECK_EQ(ft_min.CallChecked<Smi>(smi_2, smi_1)->value(), 1);
|
|
CHECK_EQ(ft_max.CallChecked<Smi>(smi_1, smi_2)->value(), 2);
|
|
CHECK_EQ(ft_max.CallChecked<Smi>(smi_2, smi_1)->value(), 2);
|
|
|
|
// Test double values.
|
|
Handle<Object> double_a = isolate->factory()->NewNumber(2.5);
|
|
Handle<Object> double_b = isolate->factory()->NewNumber(3.5);
|
|
Handle<Object> nan =
|
|
isolate->factory()->NewNumber(std::numeric_limits<double>::quiet_NaN());
|
|
Handle<Object> infinity = isolate->factory()->NewNumber(V8_INFINITY);
|
|
|
|
CHECK_EQ(ft_min.CallChecked<HeapNumber>(double_a, double_b)->value(), 2.5);
|
|
CHECK_EQ(ft_min.CallChecked<HeapNumber>(double_b, double_a)->value(), 2.5);
|
|
CHECK_EQ(ft_min.CallChecked<HeapNumber>(infinity, double_a)->value(), 2.5);
|
|
CHECK_EQ(ft_min.CallChecked<HeapNumber>(double_a, infinity)->value(), 2.5);
|
|
CHECK(std::isnan(ft_min.CallChecked<HeapNumber>(nan, double_a)->value()));
|
|
CHECK(std::isnan(ft_min.CallChecked<HeapNumber>(double_a, nan)->value()));
|
|
|
|
CHECK_EQ(ft_max.CallChecked<HeapNumber>(double_a, double_b)->value(), 3.5);
|
|
CHECK_EQ(ft_max.CallChecked<HeapNumber>(double_b, double_a)->value(), 3.5);
|
|
CHECK_EQ(ft_max.CallChecked<HeapNumber>(infinity, double_a)->value(),
|
|
V8_INFINITY);
|
|
CHECK_EQ(ft_max.CallChecked<HeapNumber>(double_a, infinity)->value(),
|
|
V8_INFINITY);
|
|
CHECK(std::isnan(ft_max.CallChecked<HeapNumber>(nan, double_a)->value()));
|
|
CHECK(std::isnan(ft_max.CallChecked<HeapNumber>(double_a, nan)->value()));
|
|
|
|
// Mixed smi/double values.
|
|
CHECK_EQ(ft_max.CallChecked<HeapNumber>(smi_1, double_b)->value(), 3.5);
|
|
CHECK_EQ(ft_max.CallChecked<HeapNumber>(double_b, smi_1)->value(), 3.5);
|
|
CHECK_EQ(ft_min.CallChecked<HeapNumber>(smi_5, double_b)->value(), 3.5);
|
|
CHECK_EQ(ft_min.CallChecked<HeapNumber>(double_b, smi_5)->value(), 3.5);
|
|
}
|
|
|
|
TEST(NumberAddSub) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 2;
|
|
CodeAssemblerTester asm_tester_add(isolate,
|
|
kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester_add.state());
|
|
m.Return(m.NumberAdd(m.Parameter(1), m.Parameter(2)));
|
|
}
|
|
FunctionTester ft_add(asm_tester_add.GenerateCode(), kNumParams);
|
|
|
|
CodeAssemblerTester asm_tester_sub(isolate,
|
|
kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester_sub.state());
|
|
m.Return(m.NumberSub(m.Parameter(1), m.Parameter(2)));
|
|
}
|
|
FunctionTester ft_sub(asm_tester_sub.GenerateCode(), kNumParams);
|
|
|
|
// Test smi values.
|
|
Handle<Smi> smi_1(Smi::FromInt(1), isolate);
|
|
Handle<Smi> smi_2(Smi::FromInt(2), isolate);
|
|
CHECK_EQ(ft_add.CallChecked<Smi>(smi_1, smi_2)->value(), 3);
|
|
CHECK_EQ(ft_sub.CallChecked<Smi>(smi_2, smi_1)->value(), 1);
|
|
|
|
// Test double values.
|
|
Handle<Object> double_a = isolate->factory()->NewNumber(2.5);
|
|
Handle<Object> double_b = isolate->factory()->NewNumber(3.0);
|
|
CHECK_EQ(ft_add.CallChecked<HeapNumber>(double_a, double_b)->value(), 5.5);
|
|
CHECK_EQ(ft_sub.CallChecked<HeapNumber>(double_a, double_b)->value(), -.5);
|
|
|
|
// Test overflow.
|
|
Handle<Smi> smi_max(Smi::FromInt(Smi::kMaxValue), isolate);
|
|
Handle<Smi> smi_min(Smi::FromInt(Smi::kMinValue), isolate);
|
|
CHECK_EQ(ft_add.CallChecked<HeapNumber>(smi_max, smi_1)->value(),
|
|
static_cast<double>(Smi::kMaxValue) + 1);
|
|
CHECK_EQ(ft_sub.CallChecked<HeapNumber>(smi_min, smi_1)->value(),
|
|
static_cast<double>(Smi::kMinValue) - 1);
|
|
|
|
// Test mixed smi/double values.
|
|
CHECK_EQ(ft_add.CallChecked<HeapNumber>(smi_1, double_a)->value(), 3.5);
|
|
CHECK_EQ(ft_add.CallChecked<HeapNumber>(double_a, smi_1)->value(), 3.5);
|
|
CHECK_EQ(ft_sub.CallChecked<HeapNumber>(smi_1, double_a)->value(), -1.5);
|
|
CHECK_EQ(ft_sub.CallChecked<HeapNumber>(double_a, smi_1)->value(), 1.5);
|
|
}
|
|
|
|
TEST(CloneEmptyFixedArray) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester.state());
|
|
m.Return(m.CloneFixedArray(m.CAST(m.Parameter(1))));
|
|
}
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<FixedArray> source(isolate->factory()->empty_fixed_array());
|
|
Handle<Object> result_raw = ft.Call(source).ToHandleChecked();
|
|
FixedArray result(FixedArray::cast(*result_raw));
|
|
CHECK_EQ(0, result.length());
|
|
CHECK_EQ(*(isolate->factory()->empty_fixed_array()), result);
|
|
}
|
|
|
|
TEST(CloneFixedArray) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester.state());
|
|
m.Return(m.CloneFixedArray(m.CAST(m.Parameter(1))));
|
|
}
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<FixedArray> source(isolate->factory()->NewFixedArrayWithHoles(5));
|
|
source->set(1, Smi::FromInt(1234));
|
|
Handle<Object> result_raw = ft.Call(source).ToHandleChecked();
|
|
FixedArray result(FixedArray::cast(*result_raw));
|
|
CHECK_EQ(5, result.length());
|
|
CHECK(result.get(0).IsTheHole(isolate));
|
|
CHECK_EQ(Smi::cast(result.get(1)).value(), 1234);
|
|
CHECK(result.get(2).IsTheHole(isolate));
|
|
CHECK(result.get(3).IsTheHole(isolate));
|
|
CHECK(result.get(4).IsTheHole(isolate));
|
|
}
|
|
|
|
TEST(CloneFixedArrayCOW) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester.state());
|
|
m.Return(m.CloneFixedArray(m.CAST(m.Parameter(1))));
|
|
}
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<FixedArray> source(isolate->factory()->NewFixedArrayWithHoles(5));
|
|
source->set(1, Smi::FromInt(1234));
|
|
source->set_map(ReadOnlyRoots(isolate).fixed_cow_array_map());
|
|
Handle<Object> result_raw = ft.Call(source).ToHandleChecked();
|
|
FixedArray result(FixedArray::cast(*result_raw));
|
|
CHECK_EQ(*source, result);
|
|
}
|
|
|
|
TEST(ExtractFixedArrayCOWForceCopy) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester.state());
|
|
CodeStubAssembler::ExtractFixedArrayFlags flags;
|
|
flags |= CodeStubAssembler::ExtractFixedArrayFlag::kAllFixedArrays;
|
|
base::Optional<TNode<Smi>> constant(m.SmiConstant(0));
|
|
m.Return(m.ExtractFixedArray(m.CAST(m.Parameter(1)), constant,
|
|
base::Optional<TNode<Smi>>(base::nullopt),
|
|
base::Optional<TNode<Smi>>(base::nullopt),
|
|
flags));
|
|
}
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<FixedArray> source(isolate->factory()->NewFixedArrayWithHoles(5));
|
|
source->set(1, Smi::FromInt(1234));
|
|
source->set_map(ReadOnlyRoots(isolate).fixed_cow_array_map());
|
|
Handle<Object> result_raw = ft.Call(source).ToHandleChecked();
|
|
FixedArray result(FixedArray::cast(*result_raw));
|
|
CHECK_NE(*source, result);
|
|
CHECK_EQ(5, result.length());
|
|
CHECK(result.get(0).IsTheHole(isolate));
|
|
CHECK_EQ(Smi::cast(result.get(1)).value(), 1234);
|
|
CHECK(result.get(2).IsTheHole(isolate));
|
|
CHECK(result.get(3).IsTheHole(isolate));
|
|
CHECK(result.get(4).IsTheHole(isolate));
|
|
}
|
|
|
|
TEST(ExtractFixedArraySimple) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 3;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester.state());
|
|
CodeStubAssembler::ExtractFixedArrayFlags flags;
|
|
flags |= CodeStubAssembler::ExtractFixedArrayFlag::kAllFixedArrays;
|
|
flags |= CodeStubAssembler::ExtractFixedArrayFlag::kDontCopyCOW;
|
|
base::Optional<TNode<IntPtrT>> p1_untagged(m.SmiUntag(m.Parameter(2)));
|
|
base::Optional<TNode<IntPtrT>> p2_untagged(m.SmiUntag(m.Parameter(3)));
|
|
m.Return(m.ExtractFixedArray(
|
|
m.CAST(m.Parameter(1)), p1_untagged, p2_untagged,
|
|
base::Optional<TNode<IntPtrT>>(base::nullopt), flags));
|
|
}
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<FixedArray> source(isolate->factory()->NewFixedArrayWithHoles(5));
|
|
source->set(1, Smi::FromInt(1234));
|
|
Handle<Object> result_raw =
|
|
ft.Call(source, Handle<Smi>(Smi::FromInt(1), isolate),
|
|
Handle<Smi>(Smi::FromInt(2), isolate))
|
|
.ToHandleChecked();
|
|
FixedArray result(FixedArray::cast(*result_raw));
|
|
CHECK_EQ(2, result.length());
|
|
CHECK_EQ(Smi::cast(result.get(0)).value(), 1234);
|
|
CHECK(result.get(1).IsTheHole(isolate));
|
|
}
|
|
|
|
TEST(ExtractFixedArraySimpleSmiConstant) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester.state());
|
|
CodeStubAssembler::ExtractFixedArrayFlags flags;
|
|
flags |= CodeStubAssembler::ExtractFixedArrayFlag::kAllFixedArrays;
|
|
flags |= CodeStubAssembler::ExtractFixedArrayFlag::kDontCopyCOW;
|
|
base::Optional<TNode<Smi>> constant_1(m.SmiConstant(1));
|
|
base::Optional<TNode<Smi>> constant_2(m.SmiConstant(2));
|
|
m.Return(m.ExtractFixedArray(m.CAST(m.Parameter(1)), constant_1, constant_2,
|
|
base::Optional<TNode<Smi>>(base::nullopt),
|
|
flags));
|
|
}
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<FixedArray> source(isolate->factory()->NewFixedArrayWithHoles(5));
|
|
source->set(1, Smi::FromInt(1234));
|
|
Handle<Object> result_raw = ft.Call(source).ToHandleChecked();
|
|
FixedArray result(FixedArray::cast(*result_raw));
|
|
CHECK_EQ(2, result.length());
|
|
CHECK_EQ(Smi::cast(result.get(0)).value(), 1234);
|
|
CHECK(result.get(1).IsTheHole(isolate));
|
|
}
|
|
|
|
TEST(ExtractFixedArraySimpleIntPtrConstant) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester.state());
|
|
CodeStubAssembler::ExtractFixedArrayFlags flags;
|
|
flags |= CodeStubAssembler::ExtractFixedArrayFlag::kAllFixedArrays;
|
|
flags |= CodeStubAssembler::ExtractFixedArrayFlag::kDontCopyCOW;
|
|
base::Optional<TNode<IntPtrT>> constant_1(m.IntPtrConstant(1));
|
|
base::Optional<TNode<IntPtrT>> constant_2(m.IntPtrConstant(2));
|
|
m.Return(m.ExtractFixedArray(m.CAST(m.Parameter(1)), constant_1, constant_2,
|
|
base::Optional<TNode<IntPtrT>>(base::nullopt),
|
|
flags));
|
|
}
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<FixedArray> source(isolate->factory()->NewFixedArrayWithHoles(5));
|
|
source->set(1, Smi::FromInt(1234));
|
|
Handle<Object> result_raw = ft.Call(source).ToHandleChecked();
|
|
FixedArray result(FixedArray::cast(*result_raw));
|
|
CHECK_EQ(2, result.length());
|
|
CHECK_EQ(Smi::cast(result.get(0)).value(), 1234);
|
|
CHECK(result.get(1).IsTheHole(isolate));
|
|
}
|
|
|
|
TEST(ExtractFixedArraySimpleIntPtrConstantNoDoubles) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester.state());
|
|
base::Optional<TNode<IntPtrT>> constant_1(m.IntPtrConstant(1));
|
|
base::Optional<TNode<IntPtrT>> constant_2(m.IntPtrConstant(2));
|
|
m.Return(m.ExtractFixedArray(
|
|
m.CAST(m.Parameter(1)), constant_1, constant_2,
|
|
base::Optional<TNode<IntPtrT>>(base::nullopt),
|
|
CodeStubAssembler::ExtractFixedArrayFlag::kFixedArrays));
|
|
}
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<FixedArray> source(isolate->factory()->NewFixedArrayWithHoles(5));
|
|
source->set(1, Smi::FromInt(1234));
|
|
Handle<Object> result_raw = ft.Call(source).ToHandleChecked();
|
|
FixedArray result(FixedArray::cast(*result_raw));
|
|
CHECK_EQ(2, result.length());
|
|
CHECK_EQ(Smi::cast(result.get(0)).value(), 1234);
|
|
CHECK(result.get(1).IsTheHole(isolate));
|
|
}
|
|
|
|
TEST(ExtractFixedArraySimpleIntPtrParameters) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 3;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester.state());
|
|
base::Optional<TNode<IntPtrT>> p1_untagged(m.SmiUntag(m.Parameter(2)));
|
|
base::Optional<TNode<IntPtrT>> p2_untagged(m.SmiUntag(m.Parameter(3)));
|
|
m.Return(
|
|
m.ExtractFixedArray(m.CAST(m.Parameter(1)), p1_untagged, p2_untagged));
|
|
}
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Handle<FixedArray> source(isolate->factory()->NewFixedArrayWithHoles(5));
|
|
source->set(1, Smi::FromInt(1234));
|
|
Handle<Object> result_raw =
|
|
ft.Call(source, Handle<Smi>(Smi::FromInt(1), isolate),
|
|
Handle<Smi>(Smi::FromInt(2), isolate))
|
|
.ToHandleChecked();
|
|
FixedArray result(FixedArray::cast(*result_raw));
|
|
CHECK_EQ(2, result.length());
|
|
CHECK_EQ(Smi::cast(result.get(0)).value(), 1234);
|
|
CHECK(result.get(1).IsTheHole(isolate));
|
|
|
|
Handle<FixedDoubleArray> source_double = Handle<FixedDoubleArray>::cast(
|
|
isolate->factory()->NewFixedDoubleArray(5));
|
|
source_double->set(0, 10);
|
|
source_double->set(1, 11);
|
|
source_double->set(2, 12);
|
|
source_double->set(3, 13);
|
|
source_double->set(4, 14);
|
|
Handle<Object> double_result_raw =
|
|
ft.Call(source_double, Handle<Smi>(Smi::FromInt(1), isolate),
|
|
Handle<Smi>(Smi::FromInt(2), isolate))
|
|
.ToHandleChecked();
|
|
FixedDoubleArray double_result = FixedDoubleArray::cast(*double_result_raw);
|
|
CHECK_EQ(2, double_result.length());
|
|
CHECK_EQ(double_result.get_scalar(0), 11);
|
|
CHECK_EQ(double_result.get_scalar(1), 12);
|
|
}
|
|
|
|
TEST(SingleInputPhiElimination) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 2;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
{
|
|
CodeStubAssembler m(asm_tester.state());
|
|
TVariable<Smi> temp1(&m);
|
|
TVariable<Smi> temp2(&m);
|
|
Label temp_label(&m, {&temp1, &temp2});
|
|
Label end_label(&m, {&temp1, &temp2});
|
|
temp1 = m.CAST(m.Parameter(1));
|
|
temp2 = m.CAST(m.Parameter(1));
|
|
m.Branch(m.TaggedEqual(m.UncheckedCast<Object>(m.Parameter(0)),
|
|
m.UncheckedCast<Object>(m.Parameter(1))),
|
|
&end_label, &temp_label);
|
|
m.BIND(&temp_label);
|
|
temp1 = m.CAST(m.Parameter(2));
|
|
temp2 = m.CAST(m.Parameter(2));
|
|
m.Goto(&end_label);
|
|
m.BIND(&end_label);
|
|
m.Return(m.UncheckedCast<Object>(temp1.value()));
|
|
}
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
// Generating code without an assert is enough to make sure that the
|
|
// single-input phi is properly eliminated.
|
|
}
|
|
|
|
TEST(SmallOrderedHashMapAllocate) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester.state());
|
|
TNode<Smi> capacity = m.CAST(m.Parameter(1));
|
|
m.Return(m.AllocateSmallOrderedHashMap(m.SmiToIntPtr(capacity)));
|
|
}
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
Factory* factory = isolate->factory();
|
|
int capacity = SmallOrderedHashMap::kMinCapacity;
|
|
while (capacity <= SmallOrderedHashMap::kMaxCapacity) {
|
|
Handle<SmallOrderedHashMap> expected =
|
|
factory->NewSmallOrderedHashMap(capacity);
|
|
Handle<Object> result_raw =
|
|
ft.Call(Handle<Smi>(Smi::FromInt(capacity), isolate)).ToHandleChecked();
|
|
Handle<SmallOrderedHashMap> actual = Handle<SmallOrderedHashMap>(
|
|
SmallOrderedHashMap::cast(*result_raw), isolate);
|
|
CHECK_EQ(capacity, actual->Capacity());
|
|
CHECK_EQ(0, actual->NumberOfElements());
|
|
CHECK_EQ(0, actual->NumberOfDeletedElements());
|
|
CHECK_EQ(capacity / SmallOrderedHashMap::kLoadFactor,
|
|
actual->NumberOfBuckets());
|
|
CHECK_EQ(0, memcmp(reinterpret_cast<void*>(expected->address()),
|
|
reinterpret_cast<void*>(actual->address()),
|
|
SmallOrderedHashMap::SizeFor(capacity)));
|
|
#ifdef VERIFY_HEAP
|
|
actual->SmallOrderedHashMapVerify(isolate);
|
|
#endif
|
|
capacity = capacity << 1;
|
|
}
|
|
#ifdef VERIFY_HEAP
|
|
isolate->heap()->Verify();
|
|
#endif
|
|
}
|
|
|
|
TEST(SmallOrderedHashSetAllocate) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(asm_tester.state());
|
|
TNode<Smi> capacity = m.CAST(m.Parameter(1));
|
|
m.Return(m.AllocateSmallOrderedHashSet(m.SmiToIntPtr(capacity)));
|
|
}
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
int capacity = SmallOrderedHashSet::kMinCapacity;
|
|
Factory* factory = isolate->factory();
|
|
while (capacity <= SmallOrderedHashSet::kMaxCapacity) {
|
|
Handle<SmallOrderedHashSet> expected =
|
|
factory->NewSmallOrderedHashSet(capacity);
|
|
Handle<Object> result_raw =
|
|
ft.Call(Handle<Smi>(Smi::FromInt(capacity), isolate)).ToHandleChecked();
|
|
Handle<SmallOrderedHashSet> actual = Handle<SmallOrderedHashSet>(
|
|
SmallOrderedHashSet::cast(*result_raw), isolate);
|
|
CHECK_EQ(capacity, actual->Capacity());
|
|
CHECK_EQ(0, actual->NumberOfElements());
|
|
CHECK_EQ(0, actual->NumberOfDeletedElements());
|
|
CHECK_EQ(capacity / SmallOrderedHashSet::kLoadFactor,
|
|
actual->NumberOfBuckets());
|
|
CHECK_EQ(0, memcmp(reinterpret_cast<void*>(expected->address()),
|
|
reinterpret_cast<void*>(actual->address()),
|
|
SmallOrderedHashSet::SizeFor(capacity)));
|
|
#ifdef VERIFY_HEAP
|
|
actual->SmallOrderedHashSetVerify(isolate);
|
|
#endif
|
|
capacity = capacity << 1;
|
|
}
|
|
#ifdef VERIFY_HEAP
|
|
isolate->heap()->Verify();
|
|
#endif
|
|
}
|
|
|
|
TEST(IsDoubleElementsKind) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 2;
|
|
CodeAssemblerTester ft_tester(isolate, kNumParams + 1); // Include receiver.
|
|
{
|
|
CodeStubAssembler m(ft_tester.state());
|
|
m.Return(m.SmiFromInt32(m.UncheckedCast<Int32T>(
|
|
m.IsDoubleElementsKind(m.SmiToInt32(m.Parameter(1))))));
|
|
}
|
|
FunctionTester ft(ft_tester.GenerateCode(), kNumParams);
|
|
CHECK_EQ(
|
|
(*Handle<Smi>::cast(
|
|
ft.Call(Handle<Smi>(Smi::FromInt(PACKED_DOUBLE_ELEMENTS), isolate))
|
|
.ToHandleChecked()))
|
|
.value(),
|
|
1);
|
|
CHECK_EQ(
|
|
(*Handle<Smi>::cast(
|
|
ft.Call(Handle<Smi>(Smi::FromInt(HOLEY_DOUBLE_ELEMENTS), isolate))
|
|
.ToHandleChecked()))
|
|
.value(),
|
|
1);
|
|
CHECK_EQ((*Handle<Smi>::cast(
|
|
ft.Call(Handle<Smi>(Smi::FromInt(HOLEY_ELEMENTS), isolate))
|
|
.ToHandleChecked()))
|
|
.value(),
|
|
0);
|
|
CHECK_EQ((*Handle<Smi>::cast(
|
|
ft.Call(Handle<Smi>(Smi::FromInt(PACKED_ELEMENTS), isolate))
|
|
.ToHandleChecked()))
|
|
.value(),
|
|
0);
|
|
CHECK_EQ((*Handle<Smi>::cast(
|
|
ft.Call(Handle<Smi>(Smi::FromInt(PACKED_SMI_ELEMENTS), isolate))
|
|
.ToHandleChecked()))
|
|
.value(),
|
|
0);
|
|
CHECK_EQ((*Handle<Smi>::cast(
|
|
ft.Call(Handle<Smi>(Smi::FromInt(HOLEY_SMI_ELEMENTS), isolate))
|
|
.ToHandleChecked()))
|
|
.value(),
|
|
0);
|
|
CHECK_EQ((*Handle<Smi>::cast(
|
|
ft.Call(Handle<Smi>(Smi::FromInt(DICTIONARY_ELEMENTS), isolate))
|
|
.ToHandleChecked()))
|
|
.value(),
|
|
0);
|
|
}
|
|
|
|
TEST(TestCallBuiltinInlineTrampoline) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
const int kContextOffset = 3;
|
|
Node* str = m.Parameter(1);
|
|
TNode<Context> context = m.CAST(m.Parameter(kNumParams + kContextOffset));
|
|
|
|
TNode<Smi> index = m.SmiConstant(2);
|
|
|
|
m.Return(m.CallStub(Builtins::CallableFor(isolate, Builtins::kStringRepeat),
|
|
context, str, index));
|
|
AssemblerOptions options = AssemblerOptions::Default(isolate);
|
|
options.inline_offheap_trampolines = true;
|
|
options.use_pc_relative_calls_and_jumps = false;
|
|
options.isolate_independent_code = false;
|
|
FunctionTester ft(asm_tester.GenerateCode(options), kNumParams);
|
|
MaybeHandle<Object> result = ft.Call(CcTest::MakeString("abcdef"));
|
|
CHECK(String::Equals(isolate, CcTest::MakeString("abcdefabcdef"),
|
|
Handle<String>::cast(result.ToHandleChecked())));
|
|
}
|
|
|
|
// TODO(v8:9821): Remove the option to disable inlining off-heap trampolines
|
|
// along with this test.
|
|
DISABLED_TEST(TestCallBuiltinIndirectLoad) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
const int kContextOffset = 2;
|
|
Node* str = m.Parameter(0);
|
|
TNode<Context> context = m.CAST(m.Parameter(kNumParams + kContextOffset));
|
|
|
|
TNode<Smi> index = m.SmiConstant(2);
|
|
|
|
m.Return(m.CallStub(Builtins::CallableFor(isolate, Builtins::kStringRepeat),
|
|
context, str, index));
|
|
AssemblerOptions options = AssemblerOptions::Default(isolate);
|
|
options.inline_offheap_trampolines = false;
|
|
options.use_pc_relative_calls_and_jumps = false;
|
|
options.isolate_independent_code = true;
|
|
FunctionTester ft(asm_tester.GenerateCode(options), kNumParams);
|
|
MaybeHandle<Object> result = ft.Call(CcTest::MakeString("abcdef"));
|
|
CHECK(String::Equals(isolate, CcTest::MakeString("abcdefabcdef"),
|
|
Handle<String>::cast(result.ToHandleChecked())));
|
|
}
|
|
|
|
TEST(InstructionSchedulingCallerSavedRegisters) {
|
|
// This is a regression test for v8:9775, where TF's instruction scheduler
|
|
// incorrectly moved pure operations in between a ArchSaveCallerRegisters and
|
|
// a ArchRestoreCallerRegisters instruction.
|
|
bool old_turbo_instruction_scheduling = FLAG_turbo_instruction_scheduling;
|
|
FLAG_turbo_instruction_scheduling = true;
|
|
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
{
|
|
Node* x = m.SmiUntag(m.Parameter(1));
|
|
Node* y = m.WordOr(m.WordShr(x, 1), m.IntPtrConstant(1));
|
|
TNode<ExternalReference> isolate_ptr =
|
|
m.ExternalConstant(ExternalReference::isolate_address(isolate));
|
|
m.CallCFunctionWithCallerSavedRegisters(
|
|
m.ExternalConstant(
|
|
ExternalReference::smi_lexicographic_compare_function()),
|
|
MachineType::Int32(), kSaveFPRegs,
|
|
std::make_pair(MachineType::Pointer(), isolate_ptr),
|
|
std::make_pair(MachineType::TaggedSigned(), m.SmiConstant(0)),
|
|
std::make_pair(MachineType::TaggedSigned(), m.SmiConstant(0)));
|
|
m.Return(m.SmiTag(m.Signed(m.WordOr(x, y))));
|
|
}
|
|
|
|
AssemblerOptions options = AssemblerOptions::Default(isolate);
|
|
FunctionTester ft(asm_tester.GenerateCode(options), kNumParams);
|
|
Handle<Object> input = isolate->factory()->NewNumber(8);
|
|
MaybeHandle<Object> result = ft.Call(input);
|
|
CHECK(result.ToHandleChecked()->IsSmi());
|
|
CHECK_EQ(result.ToHandleChecked()->Number(), 13);
|
|
|
|
FLAG_turbo_instruction_scheduling = old_turbo_instruction_scheduling;
|
|
}
|
|
|
|
TEST(WasmInt32ToHeapNumber) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
int32_t test_values[] = {
|
|
// Smi values.
|
|
1,
|
|
0,
|
|
-1,
|
|
kSmiMaxValue,
|
|
kSmiMinValue,
|
|
// Test integers that can't be Smis (only possible if Smis are 31 bits).
|
|
#if defined(V8_HOST_ARCH_32_BIT) || defined(V8_31BIT_SMIS_ON_64BIT_ARCH)
|
|
kSmiMaxValue + 1,
|
|
kSmiMinValue - 1,
|
|
#endif
|
|
};
|
|
|
|
// FunctionTester can't handle Wasm type arguments, so for each test value,
|
|
// build a function with the arguments baked in, then generate a no-argument
|
|
// function to call.
|
|
const int kNumParams = 1;
|
|
for (size_t i = 0; i < arraysize(test_values); ++i) {
|
|
int32_t test_value = test_values[i];
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
Node* context = m.Parameter(kNumParams + 1);
|
|
const TNode<Int32T> arg = m.Int32Constant(test_value);
|
|
const TNode<Object> call_result =
|
|
m.CallBuiltin(Builtins::kWasmInt32ToHeapNumber, context, arg);
|
|
m.Return(call_result);
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result = ft.Call().ToHandleChecked();
|
|
CHECK(result->IsNumber());
|
|
Handle<Object> expected(isolate->factory()->NewNumber(test_value));
|
|
CHECK(result->StrictEquals(*expected));
|
|
}
|
|
}
|
|
|
|
int32_t NumberToInt32(Handle<Object> number) {
|
|
if (number->IsSmi()) {
|
|
return Smi::ToInt(*number);
|
|
}
|
|
if (number->IsHeapNumber()) {
|
|
double num = HeapNumber::cast(*number).value();
|
|
return DoubleToInt32(num);
|
|
}
|
|
UNREACHABLE();
|
|
}
|
|
|
|
TEST(WasmTaggedNonSmiToInt32) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
Factory* factory = isolate->factory();
|
|
HandleScope scope(isolate);
|
|
|
|
Handle<Object> test_values[] = {
|
|
// No Smis here; the builtin can't handle them correctly.
|
|
factory->NewNumber(-0.0),
|
|
factory->NewNumber(1.5),
|
|
factory->NewNumber(-1.5),
|
|
factory->NewNumber(2 * static_cast<double>(kSmiMaxValue)),
|
|
factory->NewNumber(2 * static_cast<double>(kSmiMinValue)),
|
|
factory->NewNumber(std::numeric_limits<double>::infinity()),
|
|
factory->NewNumber(-std::numeric_limits<double>::infinity()),
|
|
factory->NewNumber(-std::numeric_limits<double>::quiet_NaN()),
|
|
};
|
|
|
|
const int kNumParams = 2;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
Node* context = m.Parameter(kNumParams + 3);
|
|
const TNode<Object> arg = m.CAST(m.Parameter(1));
|
|
int32_t result = 0;
|
|
Node* base = m.IntPtrConstant(reinterpret_cast<intptr_t>(&result));
|
|
Node* value = m.CallBuiltin(Builtins::kWasmTaggedNonSmiToInt32, context, arg);
|
|
m.StoreNoWriteBarrier(MachineRepresentation::kWord32, base, value);
|
|
m.Return(m.UndefinedConstant());
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
for (size_t i = 0; i < arraysize(test_values); ++i) {
|
|
Handle<Object> test_value = test_values[i];
|
|
ft.Call(test_value);
|
|
int32_t expected = NumberToInt32(test_value);
|
|
CHECK_EQ(result, expected);
|
|
}
|
|
}
|
|
|
|
TEST(WasmFloat32ToNumber) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
float test_values[] = {
|
|
// Smi values.
|
|
1,
|
|
0,
|
|
-1,
|
|
// Max and min Smis can't be represented as floats.
|
|
// Non-Smi values.
|
|
-0.0,
|
|
1.5,
|
|
std::numeric_limits<float>::quiet_NaN(),
|
|
std::numeric_limits<float>::infinity(),
|
|
};
|
|
|
|
// FunctionTester can't handle Wasm type arguments, so for each test value,
|
|
// build a function with the arguments baked in, then generate a no-argument
|
|
// function to call.
|
|
const int kNumParams = 1;
|
|
for (size_t i = 0; i < arraysize(test_values); ++i) {
|
|
double test_value = test_values[i];
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
Node* context = m.Parameter(kNumParams + 1);
|
|
const TNode<Float32T> arg = m.Float32Constant(test_value);
|
|
const TNode<Object> call_result =
|
|
m.CallBuiltin(Builtins::kWasmFloat32ToNumber, context, arg);
|
|
m.Return(call_result);
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result = ft.Call().ToHandleChecked();
|
|
CHECK(result->IsNumber());
|
|
Handle<Object> expected(isolate->factory()->NewNumber(test_value));
|
|
CHECK(result->StrictEquals(*expected) ||
|
|
(std::isnan(test_value) && std::isnan(result->Number())));
|
|
CHECK_EQ(result->IsSmi(), expected->IsSmi());
|
|
}
|
|
}
|
|
|
|
TEST(WasmFloat64ToNumber) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
|
|
double test_values[] = {
|
|
// Smi values.
|
|
1,
|
|
0,
|
|
-1,
|
|
kSmiMaxValue,
|
|
kSmiMinValue,
|
|
// Non-Smi values.
|
|
-0.0,
|
|
1.5,
|
|
std::numeric_limits<double>::quiet_NaN(),
|
|
std::numeric_limits<double>::infinity(),
|
|
};
|
|
|
|
// FunctionTester can't handle Wasm type arguments, so for each test value,
|
|
// build a function with the arguments baked in, then generate a no-argument
|
|
// function to call.
|
|
const int kNumParams = 1;
|
|
for (size_t i = 0; i < arraysize(test_values); ++i) {
|
|
double test_value = test_values[i];
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
Node* context = m.Parameter(kNumParams + 1);
|
|
const TNode<Float64T> arg = m.Float64Constant(test_value);
|
|
const TNode<Object> call_result =
|
|
m.CallBuiltin(Builtins::kWasmFloat64ToNumber, context, arg);
|
|
m.Return(call_result);
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
Handle<Object> result = ft.Call().ToHandleChecked();
|
|
CHECK(result->IsNumber());
|
|
Handle<Object> expected(isolate->factory()->NewNumber(test_value));
|
|
CHECK(result->StrictEquals(*expected) ||
|
|
(std::isnan(test_value) && std::isnan(result->Number())));
|
|
CHECK_EQ(result->IsSmi(), expected->IsSmi());
|
|
}
|
|
}
|
|
|
|
double NumberToFloat64(Handle<Object> number) {
|
|
if (number->IsSmi()) {
|
|
return Smi::ToInt(*number);
|
|
}
|
|
if (number->IsHeapNumber()) {
|
|
return HeapNumber::cast(*number).value();
|
|
}
|
|
UNREACHABLE();
|
|
}
|
|
|
|
TEST(WasmTaggedToFloat64) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
Factory* factory = isolate->factory();
|
|
HandleScope scope(isolate);
|
|
|
|
Handle<Object> test_values[] = {
|
|
// Smi values.
|
|
handle(Smi::FromInt(1), isolate),
|
|
handle(Smi::FromInt(0), isolate),
|
|
handle(Smi::FromInt(-1), isolate),
|
|
handle(Smi::FromInt(kSmiMaxValue), isolate),
|
|
handle(Smi::FromInt(kSmiMinValue), isolate),
|
|
// Test some non-Smis.
|
|
factory->NewNumber(-0.0),
|
|
factory->NewNumber(1.5),
|
|
factory->NewNumber(-1.5),
|
|
// Integer Overflows on platforms with 32 bit Smis.
|
|
#if defined(V8_HOST_ARCH_32_BIT) || defined(V8_31BIT_SMIS_ON_64BIT_ARCH)
|
|
factory->NewNumber(2 * kSmiMaxValue),
|
|
factory->NewNumber(2 * kSmiMinValue),
|
|
#endif
|
|
factory->NewNumber(std::numeric_limits<double>::infinity()),
|
|
factory->NewNumber(-std::numeric_limits<double>::infinity()),
|
|
factory->NewNumber(-std::numeric_limits<double>::quiet_NaN()),
|
|
};
|
|
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams + 1); // Include receiver.
|
|
CodeStubAssembler m(asm_tester.state());
|
|
Node* context = m.Parameter(kNumParams + 3);
|
|
const TNode<Object> arg = m.CAST(m.Parameter(1));
|
|
double result = 0;
|
|
Node* base = m.IntPtrConstant(reinterpret_cast<intptr_t>(&result));
|
|
Node* value = m.CallBuiltin(Builtins::kWasmTaggedToFloat64, context, arg);
|
|
m.StoreNoWriteBarrier(MachineRepresentation::kFloat64, base, value);
|
|
m.Return(m.UndefinedConstant());
|
|
|
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
|
|
|
for (size_t i = 0; i < arraysize(test_values); ++i) {
|
|
Handle<Object> test_value = test_values[i];
|
|
ft.Call(test_value);
|
|
double expected = NumberToFloat64(test_value);
|
|
if (std::isnan(expected)) {
|
|
CHECK(std::isnan(result));
|
|
} else {
|
|
CHECK_EQ(result, expected);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(SmiUntagLeftShiftOptimization) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 1;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
{
|
|
TNode<TaggedIndex> param =
|
|
TNode<TaggedIndex>::UncheckedCast(m.Parameter(0));
|
|
TNode<WordT> unoptimized =
|
|
m.IntPtrMul(m.TaggedIndexToIntPtr(param), m.IntPtrConstant(8));
|
|
TNode<WordT> optimized = m.WordShl(
|
|
m.BitcastTaggedToWordForTagAndSmiBits(param), 3 - kSmiTagSize);
|
|
m.StaticAssert(m.WordEqual(unoptimized, optimized));
|
|
m.Return(m.UndefinedConstant());
|
|
}
|
|
|
|
AssemblerOptions options = AssemblerOptions::Default(isolate);
|
|
FunctionTester ft(asm_tester.GenerateCode(options), kNumParams);
|
|
}
|
|
|
|
TEST(SmiUntagComparisonOptimization) {
|
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
|
const int kNumParams = 2;
|
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
|
CodeStubAssembler m(asm_tester.state());
|
|
|
|
{
|
|
TNode<Smi> a = TNode<Smi>::UncheckedCast(m.Parameter(0));
|
|
TNode<Smi> b = TNode<Smi>::UncheckedCast(m.Parameter(1));
|
|
TNode<BoolT> unoptimized = m.UintPtrLessThan(m.SmiUntag(a), m.SmiUntag(b));
|
|
#ifdef V8_COMPRESS_POINTERS
|
|
TNode<BoolT> optimized = m.Uint32LessThan(
|
|
m.TruncateIntPtrToInt32(m.BitcastTaggedToWordForTagAndSmiBits(a)),
|
|
m.TruncateIntPtrToInt32(m.BitcastTaggedToWordForTagAndSmiBits(b)));
|
|
#else
|
|
TNode<BoolT> optimized =
|
|
m.UintPtrLessThan(m.BitcastTaggedToWordForTagAndSmiBits(a),
|
|
m.BitcastTaggedToWordForTagAndSmiBits(b));
|
|
#endif
|
|
m.StaticAssert(m.Word32Equal(unoptimized, optimized));
|
|
m.Return(m.UndefinedConstant());
|
|
}
|
|
|
|
AssemblerOptions options = AssemblerOptions::Default(isolate);
|
|
FunctionTester ft(asm_tester.GenerateCode(options), kNumParams);
|
|
}
|
|
|
|
} // namespace compiler
|
|
} // namespace internal
|
|
} // namespace v8
|