Upgrade V8 to 5.1.281.57 DO NOT MERGE
FPIIM-449
Change-Id: Id981b686b4d587ac31697662eb98bb34be42ad90
(cherry picked from commit 3b9bc31999c9787eb726ecdbfd5796bfdec32a18)
diff --git a/src/builtins.cc b/src/builtins.cc
index 23c41f7..9c3ff59 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -5,10 +5,12 @@
#include "src/builtins.h"
#include "src/api.h"
+#include "src/api-arguments.h"
#include "src/api-natives.h"
-#include "src/arguments.h"
#include "src/base/once.h"
#include "src/bootstrapper.h"
+#include "src/code-factory.h"
+#include "src/compiler/code-stub-assembler.h"
#include "src/dateparser-inl.h"
#include "src/elements.h"
#include "src/frames-inl.h"
@@ -142,11 +144,18 @@
Isolate* isolate); \
MUST_USE_RESULT static Object* Builtin_##name( \
int args_length, Object** args_object, Isolate* isolate) { \
+ Object* value; \
isolate->counters()->runtime_calls()->Increment(); \
- RuntimeCallStats* stats = isolate->counters()->runtime_call_stats(); \
- RuntimeCallTimerScope timer(isolate, &stats->Builtin_##name); \
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.runtime"), \
+ "V8.Builtin_" #name); \
name##ArgumentsType args(args_length, args_object); \
- Object* value = Builtin_Impl_##name(args, isolate); \
+ if (FLAG_runtime_call_stats) { \
+ RuntimeCallStats* stats = isolate->counters()->runtime_call_stats(); \
+ RuntimeCallTimerScope timer(isolate, &stats->Builtin_##name); \
+ value = Builtin_Impl_##name(args, isolate); \
+ } else { \
+ value = Builtin_Impl_##name(args, isolate); \
+ } \
return value; \
} \
\
@@ -207,58 +216,38 @@
return *out <= object->elements()->length();
}
-
-inline bool PrototypeHasNoElements(PrototypeIterator* iter) {
+inline bool PrototypeHasNoElements(Isolate* isolate, JSObject* object) {
DisallowHeapAllocation no_gc;
- for (; !iter->IsAtEnd(); iter->Advance()) {
- if (iter->GetCurrent()->IsJSProxy()) return false;
- JSObject* current = iter->GetCurrent<JSObject>();
- if (current->IsAccessCheckNeeded()) return false;
- if (current->HasIndexedInterceptor()) return false;
- if (current->HasStringWrapperElements()) return false;
- if (current->elements()->length() != 0) return false;
+ HeapObject* prototype = HeapObject::cast(object->map()->prototype());
+ HeapObject* null = isolate->heap()->null_value();
+ HeapObject* empty = isolate->heap()->empty_fixed_array();
+ while (prototype != null) {
+ Map* map = prototype->map();
+ if (map->instance_type() <= LAST_CUSTOM_ELEMENTS_RECEIVER) return false;
+ if (JSObject::cast(prototype)->elements() != empty) return false;
+ prototype = HeapObject::cast(map->prototype());
}
return true;
}
inline bool IsJSArrayFastElementMovingAllowed(Isolate* isolate,
JSArray* receiver) {
- DisallowHeapAllocation no_gc;
- // If the array prototype chain is intact (and free of elements), and if the
- // receiver's prototype is the array prototype, then we are done.
- Object* prototype = receiver->map()->prototype();
- if (prototype->IsJSArray() &&
- isolate->is_initial_array_prototype(JSArray::cast(prototype)) &&
- isolate->IsFastArrayConstructorPrototypeChainIntact()) {
- return true;
- }
-
- // Slow case.
- PrototypeIterator iter(isolate, receiver);
- return PrototypeHasNoElements(&iter);
+ return PrototypeHasNoElements(isolate, receiver);
}
inline bool HasSimpleElements(JSObject* current) {
- if (current->IsAccessCheckNeeded()) return false;
- if (current->HasIndexedInterceptor()) return false;
- if (current->HasStringWrapperElements()) return false;
- if (current->GetElementsAccessor()->HasAccessors(current)) return false;
- return true;
+ return current->map()->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER &&
+ !current->GetElementsAccessor()->HasAccessors(current);
}
inline bool HasOnlySimpleReceiverElements(Isolate* isolate,
- JSReceiver* receiver) {
+ JSObject* receiver) {
// Check that we have no accessors on the receiver's elements.
- JSObject* object = JSObject::cast(receiver);
- if (!HasSimpleElements(object)) return false;
- // Check that ther are not elements on the prototype.
- DisallowHeapAllocation no_gc;
- PrototypeIterator iter(isolate, receiver);
- return PrototypeHasNoElements(&iter);
+ if (!HasSimpleElements(receiver)) return false;
+ return PrototypeHasNoElements(isolate, receiver);
}
inline bool HasOnlySimpleElements(Isolate* isolate, JSReceiver* receiver) {
- // Check that ther are not elements on the prototype.
DisallowHeapAllocation no_gc;
PrototypeIterator iter(isolate, receiver,
PrototypeIterator::START_AT_RECEIVER);
@@ -270,65 +259,39 @@
return true;
}
-// Returns empty handle if not applicable.
+// Returns |false| if not applicable.
MUST_USE_RESULT
-inline MaybeHandle<FixedArrayBase> EnsureJSArrayWithWritableFastElements(
- Isolate* isolate, Handle<Object> receiver, Arguments* args,
- int first_added_arg) {
- // We explicitly add a HandleScope to avoid creating several copies of the
- // same handle which would otherwise cause issue when left-trimming later-on.
- HandleScope scope(isolate);
- if (!receiver->IsJSArray()) return MaybeHandle<FixedArrayBase>();
+inline bool EnsureJSArrayWithWritableFastElements(Isolate* isolate,
+ Handle<Object> receiver,
+ Arguments* args,
+ int first_added_arg) {
+ if (!receiver->IsJSArray()) return false;
Handle<JSArray> array = Handle<JSArray>::cast(receiver);
+ ElementsKind origin_kind = array->GetElementsKind();
+ if (IsDictionaryElementsKind(origin_kind)) return false;
+ if (array->map()->is_observed()) return false;
+ if (!array->map()->is_extensible()) return false;
+ if (args == nullptr) return true;
+
// If there may be elements accessors in the prototype chain, the fast path
// cannot be used if there arguments to add to the array.
- Heap* heap = isolate->heap();
- if (args != NULL && !IsJSArrayFastElementMovingAllowed(isolate, *array)) {
- return MaybeHandle<FixedArrayBase>();
- }
- if (array->map()->is_observed()) return MaybeHandle<FixedArrayBase>();
- if (!array->map()->is_extensible()) return MaybeHandle<FixedArrayBase>();
- Handle<FixedArrayBase> elms(array->elements(), isolate);
- Map* map = elms->map();
- if (map == heap->fixed_array_map()) {
- if (args == NULL || array->HasFastObjectElements()) {
- return scope.CloseAndEscape(elms);
- }
- } else if (map == heap->fixed_cow_array_map()) {
- elms = JSObject::EnsureWritableFastElements(array);
- if (args == NULL || array->HasFastObjectElements()) {
- return scope.CloseAndEscape(elms);
- }
- } else if (map == heap->fixed_double_array_map()) {
- if (args == NULL) {
- return scope.CloseAndEscape(elms);
- }
- } else {
- return MaybeHandle<FixedArrayBase>();
- }
+ if (!IsJSArrayFastElementMovingAllowed(isolate, *array)) return false;
// Adding elements to the array prototype would break code that makes sure
// it has no elements. Handle that elsewhere.
- if (isolate->IsAnyInitialArrayPrototype(array)) {
- return MaybeHandle<FixedArrayBase>();
- }
+ if (isolate->IsAnyInitialArrayPrototype(array)) return false;
// Need to ensure that the arguments passed in args can be contained in
// the array.
int args_length = args->length();
- if (first_added_arg >= args_length) {
- return scope.CloseAndEscape(elms);
- }
+ if (first_added_arg >= args_length) return true;
- ElementsKind origin_kind = array->map()->elements_kind();
- DCHECK(!IsFastObjectElementsKind(origin_kind));
+ if (IsFastObjectElementsKind(origin_kind)) return true;
ElementsKind target_kind = origin_kind;
{
DisallowHeapAllocation no_gc;
- int arg_count = args_length - first_added_arg;
- Object** arguments = args->arguments() - first_added_arg - (arg_count - 1);
- for (int i = 0; i < arg_count; i++) {
- Object* arg = arguments[i];
+ for (int i = first_added_arg; i < args_length; i++) {
+ Object* arg = (*args)[i];
if (arg->IsHeapObject()) {
if (arg->IsHeapNumber()) {
target_kind = FAST_DOUBLE_ELEMENTS;
@@ -340,10 +303,12 @@
}
}
if (target_kind != origin_kind) {
+ // Use a short-lived HandleScope to avoid creating several copies of the
+ // elements handle which would cause issues when left-trimming later-on.
+ HandleScope scope(isolate);
JSObject::TransitionElementsKind(array, target_kind);
- elms = handle(array->elements(), isolate);
}
- return scope.CloseAndEscape(elms);
+ return true;
}
@@ -379,41 +344,235 @@
BUILTIN(EmptyFunction) { return isolate->heap()->undefined_value(); }
+void Builtins::Generate_ObjectHasOwnProperty(
+ compiler::CodeStubAssembler* assembler) {
+ typedef compiler::Node Node;
+ typedef compiler::CodeStubAssembler::Label Label;
+ typedef compiler::CodeStubAssembler::Variable Variable;
-BUILTIN(ArrayPush) {
+ Node* object = assembler->Parameter(0);
+ Node* key = assembler->Parameter(1);
+ Node* context = assembler->Parameter(4);
+
+ Label call_runtime(assembler), return_true(assembler),
+ return_false(assembler);
+
+ // Smi receivers do not have own properties.
+ Label if_objectisnotsmi(assembler);
+ assembler->Branch(assembler->WordIsSmi(object), &return_false,
+ &if_objectisnotsmi);
+ assembler->Bind(&if_objectisnotsmi);
+
+ Node* map = assembler->LoadMap(object);
+ Node* instance_type = assembler->LoadMapInstanceType(map);
+
+ Variable var_index(assembler, MachineRepresentation::kWord32);
+
+ Label if_keyissmi(assembler), if_keyisnotsmi(assembler),
+ keyisindex(assembler);
+ assembler->Branch(assembler->WordIsSmi(key), &if_keyissmi, &if_keyisnotsmi);
+ assembler->Bind(&if_keyissmi);
+ {
+ // Negative smi keys are named properties. Handle in the runtime.
+ Label if_keyispositive(assembler);
+ assembler->Branch(assembler->WordIsPositiveSmi(key), &if_keyispositive,
+ &call_runtime);
+ assembler->Bind(&if_keyispositive);
+
+ var_index.Bind(assembler->SmiUntag(key));
+ assembler->Goto(&keyisindex);
+ }
+
+ assembler->Bind(&if_keyisnotsmi);
+
+ Node* key_instance_type = assembler->LoadInstanceType(key);
+ Label if_iskeyunique(assembler), if_iskeynotsymbol(assembler);
+ assembler->Branch(
+ assembler->Word32Equal(key_instance_type,
+ assembler->Int32Constant(SYMBOL_TYPE)),
+ &if_iskeyunique, &if_iskeynotsymbol);
+ assembler->Bind(&if_iskeynotsymbol);
+ {
+ Label if_iskeyinternalized(assembler);
+ Node* bits = assembler->WordAnd(
+ key_instance_type,
+ assembler->Int32Constant(kIsNotStringMask | kIsNotInternalizedMask));
+ assembler->Branch(
+ assembler->Word32Equal(
+ bits, assembler->Int32Constant(kStringTag | kInternalizedTag)),
+ &if_iskeyinternalized, &call_runtime);
+ assembler->Bind(&if_iskeyinternalized);
+
+ // Check whether the key is an array index passed in as string. Handle
+ // uniform with smi keys if so.
+ // TODO(verwaest): Also support non-internalized strings.
+ Node* hash = assembler->LoadNameHash(key);
+ Node* bit = assembler->Word32And(
+ hash, assembler->Int32Constant(internal::Name::kIsNotArrayIndexMask));
+ Label if_isarrayindex(assembler);
+ assembler->Branch(assembler->Word32Equal(bit, assembler->Int32Constant(0)),
+ &if_isarrayindex, &if_iskeyunique);
+ assembler->Bind(&if_isarrayindex);
+ var_index.Bind(
+ assembler->BitFieldDecode<internal::Name::ArrayIndexValueBits>(hash));
+ assembler->Goto(&keyisindex);
+ }
+ assembler->Bind(&if_iskeyunique);
+
+ {
+ Label if_objectissimple(assembler);
+ assembler->Branch(assembler->Int32LessThanOrEqual(
+ instance_type,
+ assembler->Int32Constant(LAST_SPECIAL_RECEIVER_TYPE)),
+ &call_runtime, &if_objectissimple);
+ assembler->Bind(&if_objectissimple);
+ }
+
+ // TODO(verwaest): Perform a dictonary lookup on slow-mode receivers.
+ Node* bit_field3 = assembler->LoadMapBitField3(map);
+ Node* bit = assembler->BitFieldDecode<Map::DictionaryMap>(bit_field3);
+ Label if_isfastmap(assembler);
+ assembler->Branch(assembler->Word32Equal(bit, assembler->Int32Constant(0)),
+ &if_isfastmap, &call_runtime);
+ assembler->Bind(&if_isfastmap);
+ Node* nof =
+ assembler->BitFieldDecode<Map::NumberOfOwnDescriptorsBits>(bit_field3);
+ // Bail out to the runtime for large numbers of own descriptors. The stub only
+ // does linear search, which becomes too expensive in that case.
+ {
+ static const int32_t kMaxLinear = 256;
+ Label above_max(assembler), below_max(assembler);
+ assembler->Branch(assembler->Int32LessThanOrEqual(
+ nof, assembler->Int32Constant(kMaxLinear)),
+ &below_max, &call_runtime);
+ assembler->Bind(&below_max);
+ }
+ Node* descriptors = assembler->LoadMapDescriptors(map);
+
+ Variable var_descriptor(assembler, MachineRepresentation::kWord32);
+ Label loop(assembler, &var_descriptor);
+ var_descriptor.Bind(assembler->Int32Constant(0));
+ assembler->Goto(&loop);
+ assembler->Bind(&loop);
+ {
+ Node* index = var_descriptor.value();
+ Node* offset = assembler->Int32Constant(DescriptorArray::ToKeyIndex(0));
+ Node* factor = assembler->Int32Constant(DescriptorArray::kDescriptorSize);
+ Label if_notdone(assembler);
+ assembler->Branch(assembler->Word32Equal(index, nof), &return_false,
+ &if_notdone);
+ assembler->Bind(&if_notdone);
+ {
+ Node* array_index =
+ assembler->Int32Add(offset, assembler->Int32Mul(index, factor));
+ Node* current =
+ assembler->LoadFixedArrayElementInt32Index(descriptors, array_index);
+ Label if_unequal(assembler);
+ assembler->Branch(assembler->WordEqual(current, key), &return_true,
+ &if_unequal);
+ assembler->Bind(&if_unequal);
+
+ var_descriptor.Bind(
+ assembler->Int32Add(index, assembler->Int32Constant(1)));
+ assembler->Goto(&loop);
+ }
+ }
+
+ assembler->Bind(&keyisindex);
+ {
+ Label if_objectissimple(assembler);
+ assembler->Branch(assembler->Int32LessThanOrEqual(
+ instance_type, assembler->Int32Constant(
+ LAST_CUSTOM_ELEMENTS_RECEIVER)),
+ &call_runtime, &if_objectissimple);
+ assembler->Bind(&if_objectissimple);
+ }
+
+ Node* index = var_index.value();
+ Node* bit_field2 = assembler->LoadMapBitField2(map);
+ Node* elements_kind =
+ assembler->BitFieldDecode<Map::ElementsKindBits>(bit_field2);
+
+ // TODO(verwaest): Support other elements kinds as well.
+ Label if_isobjectorsmi(assembler);
+ assembler->Branch(
+ assembler->Int32LessThanOrEqual(
+ elements_kind, assembler->Int32Constant(FAST_HOLEY_ELEMENTS)),
+ &if_isobjectorsmi, &call_runtime);
+ assembler->Bind(&if_isobjectorsmi);
+ {
+ Node* elements = assembler->LoadElements(object);
+ Node* length = assembler->LoadFixedArrayBaseLength(elements);
+
+ Label if_iskeyinrange(assembler);
+ assembler->Branch(
+ assembler->Int32LessThan(index, assembler->SmiToWord32(length)),
+ &if_iskeyinrange, &return_false);
+
+ assembler->Bind(&if_iskeyinrange);
+ Node* element = assembler->LoadFixedArrayElementInt32Index(elements, index);
+ Node* the_hole = assembler->LoadRoot(Heap::kTheHoleValueRootIndex);
+ assembler->Branch(assembler->WordEqual(element, the_hole), &return_false,
+ &return_true);
+ }
+
+ assembler->Bind(&return_true);
+ assembler->Return(assembler->BooleanConstant(true));
+
+ assembler->Bind(&return_false);
+ assembler->Return(assembler->BooleanConstant(false));
+
+ assembler->Bind(&call_runtime);
+ assembler->Return(assembler->CallRuntime(Runtime::kObjectHasOwnProperty,
+ context, object, key));
+}
+
+namespace {
+
+Object* DoArrayPush(Isolate* isolate,
+ BuiltinArguments<BuiltinExtraArguments::kNone> args) {
HandleScope scope(isolate);
Handle<Object> receiver = args.receiver();
- MaybeHandle<FixedArrayBase> maybe_elms_obj =
- EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1);
- Handle<FixedArrayBase> elms_obj;
- if (!maybe_elms_obj.ToHandle(&elms_obj)) {
+ if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1)) {
return CallJsIntrinsic(isolate, isolate->array_push(), args);
}
// Fast Elements Path
- int push_size = args.length() - 1;
+ int to_add = args.length() - 1;
Handle<JSArray> array = Handle<JSArray>::cast(receiver);
int len = Smi::cast(array->length())->value();
- if (push_size == 0) {
- return Smi::FromInt(len);
- }
- if (push_size > 0 &&
- JSArray::WouldChangeReadOnlyLength(array, len + push_size)) {
+ if (to_add == 0) return Smi::FromInt(len);
+
+ // Currently fixed arrays cannot grow too big, so we should never hit this.
+ DCHECK_LE(to_add, Smi::kMaxValue - Smi::cast(array->length())->value());
+
+ if (JSArray::HasReadOnlyLength(array)) {
return CallJsIntrinsic(isolate, isolate->array_push(), args);
}
- DCHECK(!array->map()->is_observed());
+
ElementsAccessor* accessor = array->GetElementsAccessor();
- int new_length = accessor->Push(array, elms_obj, &args, push_size);
+ int new_length = accessor->Push(array, &args, to_add);
return Smi::FromInt(new_length);
}
+} // namespace
+
+BUILTIN(ArrayPush) { return DoArrayPush(isolate, args); }
+
+// TODO(verwaest): This is a temporary helper until the FastArrayPush stub can
+// tailcall to the builtin directly.
+RUNTIME_FUNCTION(Runtime_ArrayPush) {
+ DCHECK_EQ(2, args.length());
+ Arguments* incoming = reinterpret_cast<Arguments*>(args[0]);
+ // Rewrap the arguments as builtins arguments.
+ BuiltinArguments<BuiltinExtraArguments::kNone> caller_args(
+ incoming->length() + 1, incoming->arguments() + 1);
+ return DoArrayPush(isolate, caller_args);
+}
BUILTIN(ArrayPop) {
HandleScope scope(isolate);
Handle<Object> receiver = args.receiver();
- MaybeHandle<FixedArrayBase> maybe_elms_obj =
- EnsureJSArrayWithWritableFastElements(isolate, receiver, NULL, 0);
- Handle<FixedArrayBase> elms_obj;
- if (!maybe_elms_obj.ToHandle(&elms_obj)) {
+ if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, nullptr, 0)) {
return CallJsIntrinsic(isolate, isolate->array_pop(), args);
}
@@ -430,12 +589,12 @@
Handle<Object> result;
if (IsJSArrayFastElementMovingAllowed(isolate, JSArray::cast(*receiver))) {
// Fast Elements Path
- result = array->GetElementsAccessor()->Pop(array, elms_obj);
+ result = array->GetElementsAccessor()->Pop(array);
} else {
// Use Slow Lookup otherwise
uint32_t new_length = len - 1;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, result, Object::GetElement(isolate, array, new_length));
+ isolate, result, JSReceiver::GetElement(isolate, array, new_length));
JSArray::SetLength(array, new_length);
}
return *result;
@@ -446,10 +605,7 @@
HandleScope scope(isolate);
Heap* heap = isolate->heap();
Handle<Object> receiver = args.receiver();
- MaybeHandle<FixedArrayBase> maybe_elms_obj =
- EnsureJSArrayWithWritableFastElements(isolate, receiver, NULL, 0);
- Handle<FixedArrayBase> elms_obj;
- if (!maybe_elms_obj.ToHandle(&elms_obj) ||
+ if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, nullptr, 0) ||
!IsJSArrayFastElementMovingAllowed(isolate, JSArray::cast(*receiver))) {
return CallJsIntrinsic(isolate, isolate->array_shift(), args);
}
@@ -463,7 +619,7 @@
return CallJsIntrinsic(isolate, isolate->array_shift(), args);
}
- Handle<Object> first = array->GetElementsAccessor()->Shift(array, elms_obj);
+ Handle<Object> first = array->GetElementsAccessor()->Shift(array);
return *first;
}
@@ -471,28 +627,23 @@
BUILTIN(ArrayUnshift) {
HandleScope scope(isolate);
Handle<Object> receiver = args.receiver();
- MaybeHandle<FixedArrayBase> maybe_elms_obj =
- EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1);
- Handle<FixedArrayBase> elms_obj;
- if (!maybe_elms_obj.ToHandle(&elms_obj)) {
+ if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1)) {
return CallJsIntrinsic(isolate, isolate->array_unshift(), args);
}
Handle<JSArray> array = Handle<JSArray>::cast(receiver);
DCHECK(!array->map()->is_observed());
int to_add = args.length() - 1;
- if (to_add == 0) {
- return array->length();
- }
- // Currently fixed arrays cannot grow too big, so
- // we should never hit this case.
- DCHECK(to_add <= (Smi::kMaxValue - Smi::cast(array->length())->value()));
+ if (to_add == 0) return array->length();
- if (to_add > 0 && JSArray::HasReadOnlyLength(array)) {
+ // Currently fixed arrays cannot grow too big, so we should never hit this.
+ DCHECK_LE(to_add, Smi::kMaxValue - Smi::cast(array->length())->value());
+
+ if (JSArray::HasReadOnlyLength(array)) {
return CallJsIntrinsic(isolate, isolate->array_unshift(), args);
}
ElementsAccessor* accessor = array->GetElementsAccessor();
- int new_length = accessor->Unshift(array, elms_obj, &args, to_add);
+ int new_length = accessor->Unshift(array, &args, to_add);
return Smi::FromInt(new_length);
}
@@ -500,41 +651,34 @@
BUILTIN(ArraySlice) {
HandleScope scope(isolate);
Handle<Object> receiver = args.receiver();
- Handle<JSObject> object;
- Handle<FixedArrayBase> elms_obj;
int len = -1;
int relative_start = 0;
int relative_end = 0;
- bool is_sloppy_arguments = false;
if (receiver->IsJSArray()) {
DisallowHeapAllocation no_gc;
JSArray* array = JSArray::cast(*receiver);
- if (!array->HasFastElements() ||
- !IsJSArrayFastElementMovingAllowed(isolate, array) ||
- !isolate->IsArraySpeciesLookupChainIntact() ||
- // If this is a subclass of Array, then call out to JS
- !array->map()->new_target_is_base()) {
+ if (V8_UNLIKELY(!array->HasFastElements() ||
+ !IsJSArrayFastElementMovingAllowed(isolate, array) ||
+ !isolate->IsArraySpeciesLookupChainIntact() ||
+ // If this is a subclass of Array, then call out to JS
+ !array->HasArrayPrototype(isolate))) {
AllowHeapAllocation allow_allocation;
return CallJsIntrinsic(isolate, isolate->array_slice(), args);
}
len = Smi::cast(array->length())->value();
- object = Handle<JSObject>::cast(receiver);
- elms_obj = handle(array->elements(), isolate);
} else if (receiver->IsJSObject() &&
GetSloppyArgumentsLength(isolate, Handle<JSObject>::cast(receiver),
&len)) {
+ DCHECK_EQ(FAST_ELEMENTS, JSObject::cast(*receiver)->GetElementsKind());
// Array.prototype.slice(arguments, ...) is quite a common idiom
// (notably more than 50% of invocations in Web apps).
// Treat it in C++ as well.
- is_sloppy_arguments = true;
- object = Handle<JSObject>::cast(receiver);
- elms_obj = handle(object->elements(), isolate);
} else {
AllowHeapAllocation allow_allocation;
return CallJsIntrinsic(isolate, isolate->array_slice(), args);
}
- DCHECK(len >= 0);
+ DCHECK_LE(0, len);
int argument_count = args.length() - 1;
// Note carefully chosen defaults---if argument is missing,
// it's undefined which gets converted to 0 for relative_start
@@ -567,36 +711,21 @@
uint32_t actual_end =
(relative_end < 0) ? Max(len + relative_end, 0) : Min(relative_end, len);
- if (actual_end <= actual_start) {
- Handle<JSArray> result_array = isolate->factory()->NewJSArray(
- GetPackedElementsKind(object->GetElementsKind()), 0, 0);
- return *result_array;
- }
-
+ Handle<JSObject> object = Handle<JSObject>::cast(receiver);
ElementsAccessor* accessor = object->GetElementsAccessor();
- if (is_sloppy_arguments &&
- !accessor->IsPacked(object, elms_obj, actual_start, actual_end)) {
- // Don't deal with arguments with holes in C++
- AllowHeapAllocation allow_allocation;
- return CallJsIntrinsic(isolate, isolate->array_slice(), args);
- }
- Handle<JSArray> result_array =
- accessor->Slice(object, elms_obj, actual_start, actual_end);
- return *result_array;
+ return *accessor->Slice(object, actual_start, actual_end);
}
BUILTIN(ArraySplice) {
HandleScope scope(isolate);
Handle<Object> receiver = args.receiver();
- MaybeHandle<FixedArrayBase> maybe_elms_obj =
- EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 3);
- Handle<FixedArrayBase> elms_obj;
- if (!maybe_elms_obj.ToHandle(&elms_obj) ||
- // If this is a subclass of Array, then call out to JS
- !JSArray::cast(*receiver)->map()->new_target_is_base() ||
- // If anything with @@species has been messed with, call out to JS
- !isolate->IsArraySpeciesLookupChainIntact()) {
+ if (V8_UNLIKELY(
+ !EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 3) ||
+ // If this is a subclass of Array, then call out to JS.
+ !Handle<JSArray>::cast(receiver)->HasArrayPrototype(isolate) ||
+ // If anything with @@species has been messed with, call out to JS.
+ !isolate->IsArraySpeciesLookupChainIntact())) {
return CallJsIntrinsic(isolate, isolate->array_splice(), args);
}
Handle<JSArray> array = Handle<JSArray>::cast(receiver);
@@ -645,7 +774,7 @@
}
ElementsAccessor* accessor = array->GetElementsAccessor();
Handle<JSArray> result_array = accessor->Splice(
- array, elms_obj, actual_start, actual_delete_count, &args, add_count);
+ array, actual_start, actual_delete_count, &args, add_count);
return *result_array;
}
@@ -680,17 +809,9 @@
~ArrayConcatVisitor() { clear_storage(); }
- bool visit(uint32_t i, Handle<Object> elm) {
+ MUST_USE_RESULT bool visit(uint32_t i, Handle<Object> elm) {
uint32_t index = index_offset_ + i;
- if (!is_fixed_array()) {
- Handle<Object> element_value;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(
- isolate_, element_value,
- Object::SetElement(isolate_, storage_, index, elm, STRICT), false);
- return true;
- }
-
if (i >= JSObject::kMaxElementCount - index_offset_) {
set_exceeds_array_limit(true);
// Exception hasn't been thrown at this point. Return true to
@@ -699,6 +820,14 @@
return true;
}
+ if (!is_fixed_array()) {
+ LookupIterator it(isolate_, storage_, index, LookupIterator::OWN);
+ MAYBE_RETURN(
+ JSReceiver::CreateDataProperty(&it, elm, Object::THROW_ON_ERROR),
+ false);
+ return true;
+ }
+
if (fast_elements()) {
if (index < static_cast<uint32_t>(storage_fixed_array()->length())) {
storage_fixed_array()->set(index, *elm);
@@ -778,28 +907,26 @@
Handle<SeededNumberDictionary> slow_storage(
SeededNumberDictionary::New(isolate_, current_storage->length()));
uint32_t current_length = static_cast<uint32_t>(current_storage->length());
- for (uint32_t i = 0; i < current_length; i++) {
- HandleScope loop_scope(isolate_);
- Handle<Object> element(current_storage->get(i), isolate_);
- if (!element->IsTheHole()) {
- // The object holding this backing store has just been allocated, so
- // it cannot yet be used as a prototype.
- Handle<SeededNumberDictionary> new_storage =
- SeededNumberDictionary::AtNumberPut(slow_storage, i, element,
- false);
- if (!new_storage.is_identical_to(slow_storage)) {
- slow_storage = loop_scope.CloseAndEscape(new_storage);
- }
- }
- }
+ FOR_WITH_HANDLE_SCOPE(
+ isolate_, uint32_t, i = 0, i, i < current_length, i++, {
+ Handle<Object> element(current_storage->get(i), isolate_);
+ if (!element->IsTheHole()) {
+ // The object holding this backing store has just been allocated, so
+ // it cannot yet be used as a prototype.
+ Handle<SeededNumberDictionary> new_storage =
+ SeededNumberDictionary::AtNumberPut(slow_storage, i, element,
+ false);
+ if (!new_storage.is_identical_to(slow_storage)) {
+ slow_storage = loop_scope.CloseAndEscape(new_storage);
+ }
+ }
+ });
clear_storage();
set_storage(*slow_storage);
set_fast_elements(false);
}
- inline void clear_storage() {
- GlobalHandles::Destroy(Handle<Object>::cast(storage_).location());
- }
+ inline void clear_storage() { GlobalHandles::Destroy(storage_.location()); }
inline void set_storage(FixedArray* storage) {
DCHECK(is_fixed_array());
@@ -913,7 +1040,8 @@
case FAST_ELEMENTS:
case FAST_HOLEY_SMI_ELEMENTS:
case FAST_HOLEY_ELEMENTS: {
- Handle<FixedArray> elements(FixedArray::cast(object->elements()));
+ DisallowHeapAllocation no_gc;
+ FixedArray* elements = FixedArray::cast(object->elements());
uint32_t length = static_cast<uint32_t>(elements->length());
if (range < length) length = range;
for (uint32_t i = 0; i < length; i++) {
@@ -941,20 +1069,23 @@
break;
}
case DICTIONARY_ELEMENTS: {
- Handle<SeededNumberDictionary> dict(
- SeededNumberDictionary::cast(object->elements()));
+ DisallowHeapAllocation no_gc;
+ SeededNumberDictionary* dict =
+ SeededNumberDictionary::cast(object->elements());
uint32_t capacity = dict->Capacity();
- for (uint32_t j = 0; j < capacity; j++) {
- HandleScope loop_scope(isolate);
- Handle<Object> k(dict->KeyAt(j), isolate);
- if (dict->IsKey(*k)) {
- DCHECK(k->IsNumber());
- uint32_t index = static_cast<uint32_t>(k->Number());
- if (index < range) {
- indices->Add(index);
- }
+ Heap* heap = isolate->heap();
+ Object* undefined = heap->undefined_value();
+ Object* the_hole = heap->the_hole_value();
+ FOR_WITH_HANDLE_SCOPE(isolate, uint32_t, j = 0, j, j < capacity, j++, {
+ Object* k = dict->KeyAt(j);
+ if (k == undefined) continue;
+ if (k == the_hole) continue;
+ DCHECK(k->IsNumber());
+ uint32_t index = static_cast<uint32_t>(k->Number());
+ if (index < range) {
+ indices->Add(index);
}
- }
+ });
break;
}
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS:
@@ -1022,18 +1153,17 @@
bool IterateElementsSlow(Isolate* isolate, Handle<JSReceiver> receiver,
uint32_t length, ArrayConcatVisitor* visitor) {
- for (uint32_t i = 0; i < length; ++i) {
- HandleScope loop_scope(isolate);
+ FOR_WITH_HANDLE_SCOPE(isolate, uint32_t, i = 0, i, i < length, ++i, {
Maybe<bool> maybe = JSReceiver::HasElement(receiver, i);
if (!maybe.IsJust()) return false;
if (maybe.FromJust()) {
Handle<Object> element_value;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_value,
- Object::GetElement(isolate, receiver, i),
- false);
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, element_value, JSReceiver::GetElement(isolate, receiver, i),
+ false);
if (!visitor->visit(i, element_value)) return false;
}
- }
+ });
visitor->increase_index_offset(length);
return true;
}
@@ -1086,9 +1216,8 @@
// to check the prototype for missing elements.
Handle<FixedArray> elements(FixedArray::cast(array->elements()));
int fast_length = static_cast<int>(length);
- DCHECK_LE(fast_length, elements->length());
- for (int j = 0; j < fast_length; j++) {
- HandleScope loop_scope(isolate);
+ DCHECK(fast_length <= elements->length());
+ FOR_WITH_HANDLE_SCOPE(isolate, int, j = 0, j, j < fast_length, j++, {
Handle<Object> element_value(elements->get(j), isolate);
if (!element_value->IsTheHole()) {
if (!visitor->visit(j, element_value)) return false;
@@ -1099,12 +1228,12 @@
// Call GetElement on array, not its prototype, or getters won't
// have the correct receiver.
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
- isolate, element_value, Object::GetElement(isolate, array, j),
- false);
+ isolate, element_value,
+ JSReceiver::GetElement(isolate, array, j), false);
if (!visitor->visit(j, element_value)) return false;
}
}
- }
+ });
break;
}
case FAST_HOLEY_DOUBLE_ELEMENTS:
@@ -1121,8 +1250,7 @@
FixedDoubleArray::cast(array->elements()));
int fast_length = static_cast<int>(length);
DCHECK(fast_length <= elements->length());
- for (int j = 0; j < fast_length; j++) {
- HandleScope loop_scope(isolate);
+ FOR_WITH_HANDLE_SCOPE(isolate, int, j = 0, j, j < fast_length, j++, {
if (!elements->is_the_hole(j)) {
double double_value = elements->get_scalar(j);
Handle<Object> element_value =
@@ -1136,12 +1264,12 @@
// have the correct receiver.
Handle<Object> element_value;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
- isolate, element_value, Object::GetElement(isolate, array, j),
- false);
+ isolate, element_value,
+ JSReceiver::GetElement(isolate, array, j), false);
if (!visitor->visit(j, element_value)) return false;
}
}
- }
+ });
break;
}
@@ -1152,31 +1280,31 @@
// than length. This might introduce duplicates in the indices list.
CollectElementIndices(array, length, &indices);
indices.Sort(&compareUInt32);
- int j = 0;
int n = indices.length();
- while (j < n) {
- HandleScope loop_scope(isolate);
+ FOR_WITH_HANDLE_SCOPE(isolate, int, j = 0, j, j < n, (void)0, {
uint32_t index = indices[j];
Handle<Object> element;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
- isolate, element, Object::GetElement(isolate, array, index), false);
+ isolate, element, JSReceiver::GetElement(isolate, array, index),
+ false);
if (!visitor->visit(index, element)) return false;
// Skip to next different index (i.e., omit duplicates).
do {
j++;
} while (j < n && indices[j] == index);
- }
+ });
break;
}
case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: {
- for (uint32_t index = 0; index < length; index++) {
- HandleScope loop_scope(isolate);
- Handle<Object> element;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(
- isolate, element, Object::GetElement(isolate, array, index), false);
- if (!visitor->visit(index, element)) return false;
- }
+ FOR_WITH_HANDLE_SCOPE(
+ isolate, uint32_t, index = 0, index, index < length, index++, {
+ Handle<Object> element;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, element, JSReceiver::GetElement(isolate, array, index),
+ false);
+ if (!visitor->visit(index, element)) return false;
+ });
break;
}
case NO_ELEMENTS:
@@ -1231,8 +1359,7 @@
uint32_t estimate_result_length = 0;
uint32_t estimate_nof_elements = 0;
- for (int i = 0; i < argument_count; i++) {
- HandleScope loop_scope(isolate);
+ FOR_WITH_HANDLE_SCOPE(isolate, int, i = 0, i, i < argument_count, i++, {
Handle<Object> obj((*args)[i], isolate);
uint32_t length_estimate;
uint32_t element_estimate;
@@ -1264,7 +1391,7 @@
} else {
estimate_nof_elements += element_estimate;
}
- }
+ });
// If estimated number of elements is more than half of length, a
// fixed array (fast case) is more time and space-efficient than a
@@ -1289,6 +1416,7 @@
double_storage->set(j, obj->Number());
j++;
} else {
+ DisallowHeapAllocation no_gc;
JSArray* array = JSArray::cast(*obj);
uint32_t length = static_cast<uint32_t>(array->length()->Number());
switch (array->GetElementsKind()) {
@@ -1316,10 +1444,11 @@
}
case FAST_HOLEY_SMI_ELEMENTS:
case FAST_SMI_ELEMENTS: {
+ Object* the_hole = isolate->heap()->the_hole_value();
FixedArray* elements(FixedArray::cast(array->elements()));
for (uint32_t i = 0; i < length; i++) {
Object* element = elements->get(i);
- if (element->IsTheHole()) {
+ if (element == the_hole) {
failure = true;
break;
}
@@ -1381,7 +1510,7 @@
return isolate->heap()->exception();
}
} else {
- visitor.visit(0, obj);
+ if (!visitor.visit(0, obj)) return isolate->heap()->exception();
visitor.increase_index_offset(1);
}
}
@@ -1400,6 +1529,12 @@
MaybeHandle<JSArray> Fast_ArrayConcat(Isolate* isolate, Arguments* args) {
+ // We shouldn't overflow when adding another len.
+ const int kHalfOfMaxInt = 1 << (kBitsPerInt - 2);
+ STATIC_ASSERT(FixedArray::kMaxLength < kHalfOfMaxInt);
+ STATIC_ASSERT(FixedDoubleArray::kMaxLength < kHalfOfMaxInt);
+ USE(kHalfOfMaxInt);
+
int n_arguments = args->length();
int result_len = 0;
{
@@ -1409,27 +1544,24 @@
for (int i = 0; i < n_arguments; i++) {
Object* arg = (*args)[i];
if (!arg->IsJSArray()) return MaybeHandle<JSArray>();
- if (!HasOnlySimpleReceiverElements(isolate, JSObject::cast(arg))) {
+ if (!JSObject::cast(arg)->HasFastElements()) {
return MaybeHandle<JSArray>();
}
- // TODO(cbruni): support fast concatenation of DICTIONARY_ELEMENTS.
- if (!JSObject::cast(arg)->HasFastElements()) {
+ if (!HasOnlySimpleReceiverElements(isolate, JSObject::cast(arg))) {
return MaybeHandle<JSArray>();
}
Handle<JSArray> array(JSArray::cast(arg), isolate);
if (HasConcatSpreadableModifier(isolate, array)) {
return MaybeHandle<JSArray>();
}
- int len = Smi::cast(array->length())->value();
-
- // We shouldn't overflow when adding another len.
- const int kHalfOfMaxInt = 1 << (kBitsPerInt - 2);
- STATIC_ASSERT(FixedArray::kMaxLength < kHalfOfMaxInt);
- USE(kHalfOfMaxInt);
- result_len += len;
+ // The Array length is guaranted to be <= kHalfOfMaxInt thus we won't
+ // overflow.
+ result_len += Smi::cast(array->length())->value();
DCHECK(result_len >= 0);
// Throw an Error if we overflow the FixedArray limits
- if (FixedArray::kMaxLength < result_len) {
+ if (FixedDoubleArray::kMaxLength < result_len ||
+ FixedArray::kMaxLength < result_len) {
+ AllowHeapAllocation allow_gc;
THROW_NEW_ERROR(isolate,
NewRangeError(MessageTemplate::kInvalidArrayLength),
JSArray);
@@ -1460,12 +1592,21 @@
Handle<JSArray> result_array;
+ // Avoid a real species read to avoid extra lookups to the array constructor
+ if (V8_LIKELY(receiver->IsJSArray() &&
+ Handle<JSArray>::cast(receiver)->HasArrayPrototype(isolate) &&
+ isolate->IsArraySpeciesLookupChainIntact())) {
+ if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) {
+ return *result_array;
+ }
+ if (isolate->has_pending_exception()) return isolate->heap()->exception();
+ }
// Reading @@species happens before anything else with a side effect, so
// we can do it here to determine whether to take the fast path.
Handle<Object> species;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, species, Object::ArraySpeciesConstructor(isolate, receiver));
- if (*species == isolate->context()->native_context()->array_function()) {
+ if (*species == *isolate->array_function()) {
if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) {
return *result_array;
}
@@ -1528,15 +1669,16 @@
prop_value = JSObject::FastPropertyAt(from, representation, index);
}
} else {
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, prop_value,
- Object::GetProperty(from, next_key),
- Nothing<bool>());
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, prop_value, JSReceiver::GetProperty(from, next_key),
+ Nothing<bool>());
stable = from->map() == *map;
}
} else {
// If the map did change, do a slower lookup. We are still guaranteed that
// the object has a simple shape, and that the key is a name.
- LookupIterator it(from, next_key, LookupIterator::OWN_SKIP_INTERCEPTOR);
+ LookupIterator it(from, next_key, from,
+ LookupIterator::OWN_SKIP_INTERCEPTOR);
if (!it.IsFound()) continue;
DCHECK(it.state() == LookupIterator::DATA ||
it.state() == LookupIterator::ACCESSOR);
@@ -1544,7 +1686,7 @@
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, prop_value, Object::GetProperty(&it), Nothing<bool>());
}
- LookupIterator it(to, next_key);
+ LookupIterator it(to, next_key, to);
bool call_to_js = it.IsFound() && it.state() != LookupIterator::DATA;
Maybe<bool> result = Object::SetProperty(
&it, prop_value, STRICT, Object::CERTAINLY_NOT_STORE_FROM_KEYED);
@@ -1840,7 +1982,7 @@
isolate, keys, JSReceiver::GetKeys(receiver, OWN_ONLY, ALL_PROPERTIES,
CONVERT_TO_STRING));
- Handle<Object> descriptors =
+ Handle<JSObject> descriptors =
isolate->factory()->NewJSObject(isolate->object_function());
for (int i = 0; i < keys->length(); ++i) {
@@ -1855,7 +1997,7 @@
: undefined;
LookupIterator it = LookupIterator::PropertyOrElement(
- isolate, descriptors, key, LookupIterator::OWN);
+ isolate, descriptors, key, descriptors, LookupIterator::OWN);
Maybe<bool> success = JSReceiver::CreateDataProperty(&it, from_descriptor,
Object::DONT_THROW);
CHECK(success.FromJust());
@@ -1957,6 +2099,233 @@
}
+// -----------------------------------------------------------------------------
+// ES6 section 20.2.2 Function Properties of the Math Object
+
+
+// ES6 section 20.2.2.2 Math.acos ( x )
+BUILTIN(MathAcos) {
+ HandleScope scope(isolate);
+ DCHECK_EQ(2, args.length());
+ Handle<Object> x = args.at<Object>(1);
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x, Object::ToNumber(x));
+ return *isolate->factory()->NewHeapNumber(std::acos(x->Number()));
+}
+
+
+// ES6 section 20.2.2.4 Math.asin ( x )
+BUILTIN(MathAsin) {
+ HandleScope scope(isolate);
+ DCHECK_EQ(2, args.length());
+ Handle<Object> x = args.at<Object>(1);
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x, Object::ToNumber(x));
+ return *isolate->factory()->NewHeapNumber(std::asin(x->Number()));
+}
+
+
+// ES6 section 20.2.2.6 Math.atan ( x )
+BUILTIN(MathAtan) {
+ HandleScope scope(isolate);
+ DCHECK_EQ(2, args.length());
+ Handle<Object> x = args.at<Object>(1);
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x, Object::ToNumber(x));
+ return *isolate->factory()->NewHeapNumber(std::atan(x->Number()));
+}
+
+namespace {
+
+void Generate_MathRoundingOperation(
+ compiler::CodeStubAssembler* assembler,
+ compiler::Node* (compiler::CodeStubAssembler::*float64op)(
+ compiler::Node*)) {
+ typedef compiler::CodeStubAssembler::Label Label;
+ typedef compiler::Node Node;
+ typedef compiler::CodeStubAssembler::Variable Variable;
+
+ Node* context = assembler->Parameter(4);
+
+ // We might need to loop once for ToNumber conversion.
+ Variable var_x(assembler, MachineRepresentation::kTagged);
+ Label loop(assembler, &var_x);
+ var_x.Bind(assembler->Parameter(1));
+ assembler->Goto(&loop);
+ assembler->Bind(&loop);
+ {
+ // Load the current {x} value.
+ Node* x = var_x.value();
+
+ // Check if {x} is a Smi or a HeapObject.
+ Label if_xissmi(assembler), if_xisnotsmi(assembler);
+ assembler->Branch(assembler->WordIsSmi(x), &if_xissmi, &if_xisnotsmi);
+
+ assembler->Bind(&if_xissmi);
+ {
+ // Nothing to do when {x} is a Smi.
+ assembler->Return(x);
+ }
+
+ assembler->Bind(&if_xisnotsmi);
+ {
+ // Check if {x} is a HeapNumber.
+ Label if_xisheapnumber(assembler),
+ if_xisnotheapnumber(assembler, Label::kDeferred);
+ assembler->Branch(
+ assembler->WordEqual(assembler->LoadMap(x),
+ assembler->HeapNumberMapConstant()),
+ &if_xisheapnumber, &if_xisnotheapnumber);
+
+ assembler->Bind(&if_xisheapnumber);
+ {
+ Node* x_value = assembler->LoadHeapNumberValue(x);
+ Node* value = (assembler->*float64op)(x_value);
+ Node* result = assembler->ChangeFloat64ToTagged(value);
+ assembler->Return(result);
+ }
+
+ assembler->Bind(&if_xisnotheapnumber);
+ {
+ // Need to convert {x} to a Number first.
+ Callable callable =
+ CodeFactory::NonNumberToNumber(assembler->isolate());
+ var_x.Bind(assembler->CallStub(callable, context, x));
+ assembler->Goto(&loop);
+ }
+ }
+ }
+}
+
+} // namespace
+
+// ES6 section 20.2.2.10 Math.ceil ( x )
+void Builtins::Generate_MathCeil(compiler::CodeStubAssembler* assembler) {
+ Generate_MathRoundingOperation(assembler,
+ &compiler::CodeStubAssembler::Float64Ceil);
+}
+
+// ES6 section 20.2.2.11 Math.clz32 ( x )
+void Builtins::Generate_MathClz32(compiler::CodeStubAssembler* assembler) {
+ typedef compiler::CodeStubAssembler::Label Label;
+ typedef compiler::Node Node;
+ typedef compiler::CodeStubAssembler::Variable Variable;
+
+ Node* context = assembler->Parameter(4);
+
+ // Shared entry point for the clz32 operation.
+ Variable var_clz32_x(assembler, MachineRepresentation::kWord32);
+ Label do_clz32(assembler);
+
+ // We might need to loop once for ToNumber conversion.
+ Variable var_x(assembler, MachineRepresentation::kTagged);
+ Label loop(assembler, &var_x);
+ var_x.Bind(assembler->Parameter(1));
+ assembler->Goto(&loop);
+ assembler->Bind(&loop);
+ {
+ // Load the current {x} value.
+ Node* x = var_x.value();
+
+ // Check if {x} is a Smi or a HeapObject.
+ Label if_xissmi(assembler), if_xisnotsmi(assembler);
+ assembler->Branch(assembler->WordIsSmi(x), &if_xissmi, &if_xisnotsmi);
+
+ assembler->Bind(&if_xissmi);
+ {
+ var_clz32_x.Bind(assembler->SmiToWord32(x));
+ assembler->Goto(&do_clz32);
+ }
+
+ assembler->Bind(&if_xisnotsmi);
+ {
+ // Check if {x} is a HeapNumber.
+ Label if_xisheapnumber(assembler),
+ if_xisnotheapnumber(assembler, Label::kDeferred);
+ assembler->Branch(
+ assembler->WordEqual(assembler->LoadMap(x),
+ assembler->HeapNumberMapConstant()),
+ &if_xisheapnumber, &if_xisnotheapnumber);
+
+ assembler->Bind(&if_xisheapnumber);
+ {
+ var_clz32_x.Bind(assembler->TruncateHeapNumberValueToWord32(x));
+ assembler->Goto(&do_clz32);
+ }
+
+ assembler->Bind(&if_xisnotheapnumber);
+ {
+ // Need to convert {x} to a Number first.
+ Callable callable =
+ CodeFactory::NonNumberToNumber(assembler->isolate());
+ var_x.Bind(assembler->CallStub(callable, context, x));
+ assembler->Goto(&loop);
+ }
+ }
+ }
+
+ assembler->Bind(&do_clz32);
+ {
+ Node* x_value = var_clz32_x.value();
+ Node* value = assembler->Word32Clz(x_value);
+ Node* result = assembler->ChangeInt32ToTagged(value);
+ assembler->Return(result);
+ }
+}
+
+// ES6 section 20.2.2.16 Math.floor ( x )
+void Builtins::Generate_MathFloor(compiler::CodeStubAssembler* assembler) {
+ Generate_MathRoundingOperation(assembler,
+ &compiler::CodeStubAssembler::Float64Floor);
+}
+
+// ES6 section 20.2.2.17 Math.fround ( x )
+BUILTIN(MathFround) {
+ HandleScope scope(isolate);
+ DCHECK_EQ(2, args.length());
+ Handle<Object> x = args.at<Object>(1);
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x, Object::ToNumber(x));
+ float x32 = DoubleToFloat32(x->Number());
+ return *isolate->factory()->NewNumber(x32);
+}
+
+// ES6 section 20.2.2.19 Math.imul ( x, y )
+BUILTIN(MathImul) {
+ HandleScope scope(isolate);
+ DCHECK_EQ(3, args.length());
+ Handle<Object> x = args.at<Object>(1);
+ Handle<Object> y = args.at<Object>(2);
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x, Object::ToNumber(x));
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, y, Object::ToNumber(y));
+ int product = static_cast<int>(NumberToUint32(*x) * NumberToUint32(*y));
+ return *isolate->factory()->NewNumberFromInt(product);
+}
+
+// ES6 section 20.2.2.28 Math.round ( x )
+void Builtins::Generate_MathRound(compiler::CodeStubAssembler* assembler) {
+ Generate_MathRoundingOperation(assembler,
+ &compiler::CodeStubAssembler::Float64Round);
+}
+
+// ES6 section 20.2.2.32 Math.sqrt ( x )
+void Builtins::Generate_MathSqrt(compiler::CodeStubAssembler* assembler) {
+ using compiler::Node;
+
+ Node* x = assembler->Parameter(1);
+ Node* context = assembler->Parameter(4);
+ Node* x_value = assembler->TruncateTaggedToFloat64(context, x);
+ Node* value = assembler->Float64Sqrt(x_value);
+ Node* result = assembler->ChangeFloat64ToTagged(value);
+ assembler->Return(result);
+}
+
+// ES6 section 20.2.2.35 Math.trunc ( x )
+void Builtins::Generate_MathTrunc(compiler::CodeStubAssembler* assembler) {
+ Generate_MathRoundingOperation(assembler,
+ &compiler::CodeStubAssembler::Float64Trunc);
+}
+
+// -----------------------------------------------------------------------------
+// ES6 section 26.1 The Reflect Object
+
+
// ES6 section 26.1.3 Reflect.defineProperty
BUILTIN(ReflectDefineProperty) {
HandleScope scope(isolate);
@@ -3659,45 +4028,6 @@
return *result;
}
-// ES6 section 19.2.3.6 Function.prototype[@@hasInstance](V)
-BUILTIN(FunctionHasInstance) {
- HandleScope scope(isolate);
- Handle<Object> callable = args.receiver();
- Handle<Object> object = args.atOrUndefined(isolate, 1);
-
- // {callable} must have a [[Call]] internal method.
- if (!callable->IsCallable()) {
- return isolate->heap()->false_value();
- }
- // If {object} is not a receiver, return false.
- if (!object->IsJSReceiver()) {
- return isolate->heap()->false_value();
- }
- // Check if {callable} is bound, if so, get [[BoundTargetFunction]] from it
- // and use that instead of {callable}.
- while (callable->IsJSBoundFunction()) {
- callable =
- handle(Handle<JSBoundFunction>::cast(callable)->bound_target_function(),
- isolate);
- }
- DCHECK(callable->IsCallable());
- // Get the "prototype" of {callable}; raise an error if it's not a receiver.
- Handle<Object> prototype;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, prototype,
- Object::GetProperty(callable, isolate->factory()->prototype_string()));
- if (!prototype->IsJSReceiver()) {
- THROW_NEW_ERROR_RETURN_FAILURE(
- isolate,
- NewTypeError(MessageTemplate::kInstanceofNonobjectProto, prototype));
- }
- // Return whether or not {prototype} is in the prototype chain of {object}.
- Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object);
- Maybe<bool> result =
- JSReceiver::HasInPrototypeChain(isolate, receiver, prototype);
- MAYBE_RETURN(result, isolate->heap()->exception());
- return isolate->heap()->ToBoolean(result.FromJust());
-}
// ES6 section 19.4.1.1 Symbol ( [ description ] ) for the [[Call]] case.
BUILTIN(SymbolConstructor) {
@@ -3728,10 +4058,78 @@
Handle<Object> object = args.at<Object>(0);
Handle<String> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, result, JSObject::ObjectProtoToString(isolate, object));
+ isolate, result, Object::ObjectProtoToString(isolate, object));
return *result;
}
+// -----------------------------------------------------------------------------
+// ES6 section 21.1 String Objects
+
+namespace {
+
+bool ToUint16(Handle<Object> value, uint16_t* result) {
+ if (value->IsNumber() || Object::ToNumber(value).ToHandle(&value)) {
+ *result = DoubleToUint32(value->Number());
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+// ES6 21.1.2.1 String.fromCharCode ( ...codeUnits )
+BUILTIN(StringFromCharCode) {
+ HandleScope scope(isolate);
+ // Check resulting string length.
+ int index = 0;
+ Handle<String> result;
+ int const length = args.length() - 1;
+ if (length == 0) return isolate->heap()->empty_string();
+ DCHECK_LT(0, length);
+ // Load the first character code.
+ uint16_t code;
+ if (!ToUint16(args.at<Object>(1), &code)) return isolate->heap()->exception();
+ // Assume that the resulting String contains only one byte characters.
+ if (code <= String::kMaxOneByteCharCodeU) {
+ // Check for single one-byte character fast case.
+ if (length == 1) {
+ return *isolate->factory()->LookupSingleCharacterStringFromCode(code);
+ }
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result, isolate->factory()->NewRawOneByteString(length));
+ do {
+ Handle<SeqOneByteString>::cast(result)->Set(index, code);
+ if (++index == length) break;
+ if (!ToUint16(args.at<Object>(1 + index), &code)) {
+ return isolate->heap()->exception();
+ }
+ } while (code <= String::kMaxOneByteCharCodeU);
+ }
+ // Check if all characters fit into the one byte range.
+ if (index < length) {
+ // Fallback to two byte string.
+ Handle<String> new_result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, new_result, isolate->factory()->NewRawTwoByteString(length));
+ for (int new_index = 0; new_index < index; ++new_index) {
+ uint16_t new_code =
+ Handle<SeqOneByteString>::cast(result)->Get(new_index);
+ Handle<SeqTwoByteString>::cast(new_result)->Set(new_index, new_code);
+ }
+ while (true) {
+ Handle<SeqTwoByteString>::cast(new_result)->Set(index, code);
+ if (++index == length) break;
+ if (!ToUint16(args.at<Object>(1 + index), &code)) {
+ return isolate->heap()->exception();
+ }
+ }
+ result = new_result;
+ }
+ return *result;
+}
+
+// -----------------------------------------------------------------------------
+// ES6 section 21.1 ArrayBuffer Objects
// ES6 section 24.1.2.1 ArrayBuffer ( length ) for the [[Call]] case.
BUILTIN(ArrayBufferConstructor) {
@@ -3845,9 +4243,9 @@
HandleScope scope(isolate);
Handle<HeapObject> function = args.target<HeapObject>();
Handle<JSReceiver> receiver;
- // TODO(ishell): turn this back to a DCHECK.
- CHECK(function->IsFunctionTemplateInfo() ||
- Handle<JSFunction>::cast(function)->shared()->IsApiFunction());
+
+ DCHECK(function->IsFunctionTemplateInfo() ||
+ Handle<JSFunction>::cast(function)->shared()->IsApiFunction());
Handle<FunctionTemplateInfo> fun_data =
function->IsFunctionTemplateInfo()
@@ -3893,8 +4291,7 @@
Object* raw_call_data = fun_data->call_code();
if (!raw_call_data->IsUndefined()) {
- // TODO(ishell): remove this debugging code.
- CHECK(raw_call_data->IsCallHandlerInfo());
+ DCHECK(raw_call_data->IsCallHandlerInfo());
CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data);
Object* callback_obj = call_data->callback();
v8::FunctionCallback callback =
@@ -3912,14 +4309,8 @@
args.length() - 1,
is_construct);
- v8::Local<v8::Value> value = custom.Call(callback);
- Handle<Object> result;
- if (value.IsEmpty()) {
- result = isolate->factory()->undefined_value();
- } else {
- result = v8::Utils::OpenHandle(*value);
- result->VerifyApiCallResultType();
- }
+ Handle<Object> result = custom.Call(callback);
+ if (result.is_null()) result = isolate->factory()->undefined_value();
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
if (!is_construct || result->IsJSObject()) {
@@ -4053,6 +4444,20 @@
Handle<Object> receiver,
int argc,
Handle<Object> args[]) {
+ Isolate* isolate = function->GetIsolate();
+ // Do proper receiver conversion for non-strict mode api functions.
+ if (!receiver->IsJSReceiver()) {
+ DCHECK(function->IsFunctionTemplateInfo() || function->IsJSFunction());
+ if (function->IsFunctionTemplateInfo() ||
+ is_sloppy(JSFunction::cast(*function)->shared()->language_mode())) {
+ if (receiver->IsUndefined() || receiver->IsNull()) {
+ receiver = handle(isolate->global_proxy(), isolate);
+ } else {
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
+ Object::ToObject(isolate, receiver), Object);
+ }
+ }
+ }
// Construct BuiltinArguments object: function, arguments reversed, receiver.
const int kBufferSize = 32;
Object* small_argv[kBufferSize];
@@ -4069,7 +4474,6 @@
argv[0] = *function;
MaybeHandle<Object> result;
{
- auto isolate = function->GetIsolate();
RelocatableArguments arguments(isolate, argc + 2, &argv[argc + 1]);
result = HandleApiCallHelper<false>(isolate, arguments);
}
@@ -4086,8 +4490,6 @@
MUST_USE_RESULT static Object* HandleApiCallAsFunctionOrConstructor(
Isolate* isolate, bool is_construct_call,
BuiltinArguments<BuiltinExtraArguments::kNone> args) {
- Heap* heap = isolate->heap();
-
Handle<Object> receiver = args.receiver();
// Get the object called.
@@ -4122,12 +4524,11 @@
&args[0] - 1,
args.length() - 1,
is_construct_call);
- v8::Local<v8::Value> value = custom.Call(callback);
- if (value.IsEmpty()) {
- result = heap->undefined_value();
+ Handle<Object> result_handle = custom.Call(callback);
+ if (result_handle.is_null()) {
+ result = isolate->heap()->undefined_value();
} else {
- result = *reinterpret_cast<Object**>(*value);
- result->VerifyApiCallResultType();
+ result = *result_handle;
}
}
// Check for exceptions and return result.
@@ -4280,12 +4681,14 @@
struct BuiltinDesc {
+ Handle<Code> (*builder)(Isolate*, struct BuiltinDesc const*);
byte* generator;
byte* c_code;
const char* s_name; // name is only used for generating log information.
int name;
Code::Flags flags;
BuiltinExtraArguments extra_args;
+ int argc;
};
#define BUILTIN_FUNCTION_TABLE_INIT { V8_ONCE_INIT, {} }
@@ -4303,8 +4706,60 @@
friend class Builtins;
};
-static BuiltinFunctionTable builtin_function_table =
- BUILTIN_FUNCTION_TABLE_INIT;
+namespace {
+
+BuiltinFunctionTable builtin_function_table = BUILTIN_FUNCTION_TABLE_INIT;
+
+Handle<Code> MacroAssemblerBuilder(Isolate* isolate,
+ BuiltinDesc const* builtin_desc) {
+// For now we generate builtin adaptor code into a stack-allocated
+// buffer, before copying it into individual code objects. Be careful
+// with alignment, some platforms don't like unaligned code.
+#ifdef DEBUG
+ // We can generate a lot of debug code on Arm64.
+ const size_t buffer_size = 32 * KB;
+#elif V8_TARGET_ARCH_PPC64
+ // 8 KB is insufficient on PPC64 when FLAG_debug_code is on.
+ const size_t buffer_size = 10 * KB;
+#else
+ const size_t buffer_size = 8 * KB;
+#endif
+ union {
+ int force_alignment;
+ byte buffer[buffer_size]; // NOLINT(runtime/arrays)
+ } u;
+
+ MacroAssembler masm(isolate, u.buffer, sizeof(u.buffer),
+ CodeObjectRequired::kYes);
+ // Generate the code/adaptor.
+ typedef void (*Generator)(MacroAssembler*, int, BuiltinExtraArguments);
+ Generator g = FUNCTION_CAST<Generator>(builtin_desc->generator);
+ // We pass all arguments to the generator, but it may not use all of
+ // them. This works because the first arguments are on top of the
+ // stack.
+ DCHECK(!masm.has_frame());
+ g(&masm, builtin_desc->name, builtin_desc->extra_args);
+ // Move the code into the object heap.
+ CodeDesc desc;
+ masm.GetCode(&desc);
+ Code::Flags flags = builtin_desc->flags;
+ return isolate->factory()->NewCode(desc, flags, masm.CodeObject());
+}
+
+Handle<Code> CodeStubAssemblerBuilder(Isolate* isolate,
+ BuiltinDesc const* builtin_desc) {
+ Zone zone(isolate->allocator());
+ compiler::CodeStubAssembler assembler(isolate, &zone, builtin_desc->argc,
+ builtin_desc->flags,
+ builtin_desc->s_name);
+ // Generate the code/adaptor.
+ typedef void (*Generator)(compiler::CodeStubAssembler*);
+ Generator g = FUNCTION_CAST<Generator>(builtin_desc->generator);
+ g(&assembler);
+ return assembler.GenerateCode();
+}
+
+} // namespace
// Define array of pointers to generators and C builtin functions.
// We do this in a sort of roundabout way so that we can do the initialization
@@ -4312,47 +4767,70 @@
// Code::Flags names a non-abstract type.
void Builtins::InitBuiltinFunctionTable() {
BuiltinDesc* functions = builtin_function_table.functions_;
- functions[builtin_count].generator = NULL;
- functions[builtin_count].c_code = NULL;
- functions[builtin_count].s_name = NULL;
+ functions[builtin_count].builder = nullptr;
+ functions[builtin_count].generator = nullptr;
+ functions[builtin_count].c_code = nullptr;
+ functions[builtin_count].s_name = nullptr;
functions[builtin_count].name = builtin_count;
functions[builtin_count].flags = static_cast<Code::Flags>(0);
functions[builtin_count].extra_args = BuiltinExtraArguments::kNone;
+ functions[builtin_count].argc = 0;
#define DEF_FUNCTION_PTR_C(aname, aextra_args) \
+ functions->builder = &MacroAssemblerBuilder; \
functions->generator = FUNCTION_ADDR(Generate_Adaptor); \
functions->c_code = FUNCTION_ADDR(Builtin_##aname); \
functions->s_name = #aname; \
functions->name = c_##aname; \
functions->flags = Code::ComputeFlags(Code::BUILTIN); \
functions->extra_args = BuiltinExtraArguments::aextra_args; \
+ functions->argc = 0; \
++functions;
#define DEF_FUNCTION_PTR_A(aname, kind, state, extra) \
+ functions->builder = &MacroAssemblerBuilder; \
functions->generator = FUNCTION_ADDR(Generate_##aname); \
functions->c_code = NULL; \
functions->s_name = #aname; \
functions->name = k##aname; \
functions->flags = Code::ComputeFlags(Code::kind, state, extra); \
functions->extra_args = BuiltinExtraArguments::kNone; \
+ functions->argc = 0; \
+ ++functions;
+
+#define DEF_FUNCTION_PTR_T(aname, aargc) \
+ functions->builder = &CodeStubAssemblerBuilder; \
+ functions->generator = FUNCTION_ADDR(Generate_##aname); \
+ functions->c_code = NULL; \
+ functions->s_name = #aname; \
+ functions->name = k##aname; \
+ functions->flags = \
+ Code::ComputeFlags(Code::BUILTIN, UNINITIALIZED, kNoExtraICState); \
+ functions->extra_args = BuiltinExtraArguments::kNone; \
+ functions->argc = aargc; \
++functions;
#define DEF_FUNCTION_PTR_H(aname, kind) \
+ functions->builder = &MacroAssemblerBuilder; \
functions->generator = FUNCTION_ADDR(Generate_##aname); \
functions->c_code = NULL; \
functions->s_name = #aname; \
functions->name = k##aname; \
functions->flags = Code::ComputeHandlerFlags(Code::kind); \
functions->extra_args = BuiltinExtraArguments::kNone; \
+ functions->argc = 0; \
++functions;
BUILTIN_LIST_C(DEF_FUNCTION_PTR_C)
BUILTIN_LIST_A(DEF_FUNCTION_PTR_A)
+ BUILTIN_LIST_T(DEF_FUNCTION_PTR_T)
BUILTIN_LIST_H(DEF_FUNCTION_PTR_H)
BUILTIN_LIST_DEBUG_A(DEF_FUNCTION_PTR_A)
#undef DEF_FUNCTION_PTR_C
#undef DEF_FUNCTION_PTR_A
+#undef DEF_FUNCTION_PTR_H
+#undef DEF_FUNCTION_PTR_T
}
@@ -4364,43 +4842,15 @@
const BuiltinDesc* functions = builtin_function_table.functions();
- // For now we generate builtin adaptor code into a stack-allocated
- // buffer, before copying it into individual code objects. Be careful
- // with alignment, some platforms don't like unaligned code.
-#ifdef DEBUG
- // We can generate a lot of debug code on Arm64.
- const size_t buffer_size = 32*KB;
-#elif V8_TARGET_ARCH_PPC64
- // 8 KB is insufficient on PPC64 when FLAG_debug_code is on.
- const size_t buffer_size = 10 * KB;
-#else
- const size_t buffer_size = 8*KB;
-#endif
- union { int force_alignment; byte buffer[buffer_size]; } u;
-
// Traverse the list of builtins and generate an adaptor in a
// separate code object for each one.
for (int i = 0; i < builtin_count; i++) {
if (create_heap_objects) {
- MacroAssembler masm(isolate, u.buffer, sizeof u.buffer,
- CodeObjectRequired::kYes);
- // Generate the code/adaptor.
- typedef void (*Generator)(MacroAssembler*, int, BuiltinExtraArguments);
- Generator g = FUNCTION_CAST<Generator>(functions[i].generator);
- // We pass all arguments to the generator, but it may not use all of
- // them. This works because the first arguments are on top of the
- // stack.
- DCHECK(!masm.has_frame());
- g(&masm, functions[i].name, functions[i].extra_args);
- // Move the code into the object heap.
- CodeDesc desc;
- masm.GetCode(&desc);
- Code::Flags flags = functions[i].flags;
- Handle<Code> code =
- isolate->factory()->NewCode(desc, flags, masm.CodeObject());
+ Handle<Code> code = (*functions[i].builder)(isolate, functions + i);
// Log the event and add the code to the builtins array.
PROFILE(isolate,
- CodeCreateEvent(Logger::BUILTIN_TAG, *code, functions[i].s_name));
+ CodeCreateEvent(Logger::BUILTIN_TAG, AbstractCode::cast(*code),
+ functions[i].s_name));
builtins_[i] = *code;
code->set_builtin_index(i);
#ifdef ENABLE_DISASSEMBLER
@@ -4470,6 +4920,11 @@
reinterpret_cast<Code**>(builtin_address(k##name)); \
return Handle<Code>(code_address); \
}
+#define DEFINE_BUILTIN_ACCESSOR_T(name, argc) \
+ Handle<Code> Builtins::name() { \
+ Code** code_address = reinterpret_cast<Code**>(builtin_address(k##name)); \
+ return Handle<Code>(code_address); \
+ }
#define DEFINE_BUILTIN_ACCESSOR_H(name, kind) \
Handle<Code> Builtins::name() { \
Code** code_address = \
@@ -4478,11 +4933,13 @@
}
BUILTIN_LIST_C(DEFINE_BUILTIN_ACCESSOR_C)
BUILTIN_LIST_A(DEFINE_BUILTIN_ACCESSOR_A)
+BUILTIN_LIST_T(DEFINE_BUILTIN_ACCESSOR_T)
BUILTIN_LIST_H(DEFINE_BUILTIN_ACCESSOR_H)
BUILTIN_LIST_DEBUG_A(DEFINE_BUILTIN_ACCESSOR_A)
#undef DEFINE_BUILTIN_ACCESSOR_C
#undef DEFINE_BUILTIN_ACCESSOR_A
-
+#undef DEFINE_BUILTIN_ACCESSOR_T
+#undef DEFINE_BUILTIN_ACCESSOR_H
} // namespace internal
} // namespace v8