Commit cb8fb46a authored by binji's avatar binji Committed by Commit bot

Move ArrayBuffer.prototype.slice implementation to C++

This will be useful for sharing the implementation with
SharedArrayBuffer.prototype.slice.

BUG=v8:5897

Review-Url: https://codereview.chromium.org/2697013009
Cr-Commit-Position: refs/heads/master@{#43503}
parent 9c385f04
......@@ -438,7 +438,6 @@ action("js2c") {
"src/js/v8natives.js",
"src/js/array.js",
"src/js/string.js",
"src/js/arraybuffer.js",
"src/js/typedarray.js",
"src/js/collection.js",
"src/js/weak-collection.js",
......
......@@ -223,7 +223,7 @@ class Genesis BASE_EMBEDDED {
Handle<JSFunction> InstallArrayBuffer(Handle<JSObject> target,
const char* name, Builtins::Name call,
BuiltinFunctionId id);
BuiltinFunctionId id, bool is_shared);
Handle<JSFunction> InstallInternalArray(Handle<JSObject> target,
const char* name,
ElementsKind elements_kind);
......@@ -2550,7 +2550,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
{ // -- A r r a y B u f f e r
Handle<JSFunction> array_buffer_fun = InstallArrayBuffer(
global, "ArrayBuffer", Builtins::kArrayBufferPrototypeGetByteLength,
BuiltinFunctionId::kArrayBufferByteLength);
BuiltinFunctionId::kArrayBufferByteLength, false);
InstallWithIntrinsicDefaultProto(isolate, array_buffer_fun,
Context::ARRAY_BUFFER_FUN_INDEX);
InstallSpeciesGetter(array_buffer_fun);
......@@ -3688,7 +3688,7 @@ void Genesis::InitializeGlobal_harmony_sharedarraybuffer() {
Handle<JSFunction> shared_array_buffer_fun =
InstallArrayBuffer(global, "SharedArrayBuffer",
Builtins::kSharedArrayBufferPrototypeGetByteLength,
BuiltinFunctionId::kSharedArrayBufferByteLength);
BuiltinFunctionId::kSharedArrayBufferByteLength, true);
native_context()->set_shared_array_buffer_fun(*shared_array_buffer_fun);
Handle<String> name = factory->InternalizeUtf8String("Atomics");
......@@ -3867,7 +3867,8 @@ void Genesis::InitializeGlobal_icu_case_mapping() {
Handle<JSFunction> Genesis::InstallArrayBuffer(Handle<JSObject> target,
const char* name,
Builtins::Name call,
BuiltinFunctionId id) {
BuiltinFunctionId id,
bool is_shared) {
// Create the %ArrayBufferPrototype%
// Setup the {prototype} with the given {name} for @@toStringTag.
Handle<JSObject> prototype =
......@@ -3897,6 +3898,12 @@ Handle<JSFunction> Genesis::InstallArrayBuffer(Handle<JSObject> target,
SimpleInstallGetter(prototype, factory()->byte_length_string(), call, false,
id);
// TODO(binji): support SharedArrayBuffer.prototype.slice as well.
if (!is_shared) {
SimpleInstallFunction(prototype, "slice",
Builtins::kArrayBufferPrototypeSlice, 2, true);
}
return array_buffer_fun;
}
......
......@@ -11,6 +11,15 @@
namespace v8 {
namespace internal {
#define CHECK_IS_NOT_SHARED_ARRAY_BUFFER(name, method) \
if (name->is_shared()) { \
THROW_NEW_ERROR_RETURN_FAILURE( \
isolate, \
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, \
isolate->factory()->NewStringFromAsciiChecked(method), \
name)); \
}
// -----------------------------------------------------------------------------
// ES6 section 21.1 ArrayBuffer Objects
......@@ -63,17 +72,10 @@ BUILTIN(ArrayBufferConstructor_ConstructStub) {
// ES6 section 24.1.4.1 get ArrayBuffer.prototype.byteLength
BUILTIN(ArrayBufferPrototypeGetByteLength) {
const char* const kMethodName = "get ArrayBuffer.prototype.byteLength";
HandleScope scope(isolate);
CHECK_RECEIVER(JSArrayBuffer, array_buffer,
"get ArrayBuffer.prototype.byteLength");
if (array_buffer->is_shared()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
isolate->factory()->NewStringFromAsciiChecked(
"get ArrayBuffer.prototype.byteLength"),
args.receiver()));
}
CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName);
CHECK_IS_NOT_SHARED_ARRAY_BUFFER(array_buffer, kMethodName);
// TODO(franzih): According to the ES6 spec, we should throw a TypeError
// here if the JSArrayBuffer is detached.
return array_buffer->byte_length();
......@@ -87,5 +89,154 @@ BUILTIN(ArrayBufferIsView) {
return isolate->heap()->ToBoolean(arg->IsJSArrayBufferView());
}
// ES #sec-arraybuffer.prototype.slice
// ArrayBuffer.prototype.slice ( start, end )
BUILTIN(ArrayBufferPrototypeSlice) {
const char* const kMethodName = "ArrayBuffer.prototype.slice";
HandleScope scope(isolate);
Handle<Object> start = args.at(1);
Handle<Object> end = args.atOrUndefined(isolate, 2);
// 2. If Type(O) is not Object, throw a TypeError exception.
// 3. If O does not have an [[ArrayBufferData]] internal slot, throw a
// TypeError exception.
CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName);
// 4. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
CHECK_IS_NOT_SHARED_ARRAY_BUFFER(array_buffer, kMethodName);
// 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (array_buffer->was_neutered()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kDetachedOperation,
isolate->factory()->NewStringFromAsciiChecked(
kMethodName)));
}
// 6. Let len be O.[[ArrayBufferByteLength]].
double const len = array_buffer->byte_length()->Number();
// 7. Let relativeStart be ? ToInteger(start).
Handle<Object> relative_start;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, relative_start,
Object::ToInteger(isolate, start));
// 8. If relativeStart < 0, let first be max((len + relativeStart), 0); else
// let first be min(relativeStart, len).
double const first = (relative_start->Number() < 0)
? Max(len + relative_start->Number(), 0.0)
: Min(relative_start->Number(), len);
Handle<Object> first_obj = isolate->factory()->NewNumber(first);
// 9. If end is undefined, let relativeEnd be len; else let relativeEnd be ?
// ToInteger(end).
double relative_end;
if (end->IsUndefined(isolate)) {
relative_end = len;
} else {
Handle<Object> relative_end_obj;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, relative_end_obj,
Object::ToInteger(isolate, end));
relative_end = relative_end_obj->Number();
}
// 10. If relativeEnd < 0, let final be max((len + relativeEnd), 0); else let
// final be min(relativeEnd, len).
double const final_ = (relative_end < 0) ? Max(len + relative_end, 0.0)
: Min(relative_end, len);
// 11. Let newLen be max(final-first, 0).
double const new_len = Max(final_ - first, 0.0);
Handle<Object> new_len_obj = isolate->factory()->NewNumber(new_len);
// 12. Let ctor be ? SpeciesConstructor(O, %ArrayBuffer%).
Handle<JSFunction> arraybuffer_fun = isolate->array_buffer_fun();
Handle<Object> ctor;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, ctor,
Object::SpeciesConstructor(
isolate, Handle<JSReceiver>::cast(args.receiver()), arraybuffer_fun));
// 13. Let new be ? Construct(ctor, newLen).
Handle<JSReceiver> new_;
{
const int argc = 1;
ScopedVector<Handle<Object>> argv(argc);
argv[0] = new_len_obj;
Handle<Object> new_obj;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, new_obj,
Execution::New(Handle<JSFunction>::cast(ctor), argc, argv.start()));
new_ = Handle<JSReceiver>::cast(new_obj);
}
// 14. If new does not have an [[ArrayBufferData]] internal slot, throw a
// TypeError exception.
if (!new_->IsJSArrayBuffer()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
isolate->factory()->NewStringFromAsciiChecked(kMethodName),
new_));
}
// 15. If IsSharedArrayBuffer(new) is true, throw a TypeError exception.
Handle<JSArrayBuffer> new_array_buffer = Handle<JSArrayBuffer>::cast(new_);
CHECK_IS_NOT_SHARED_ARRAY_BUFFER(new_array_buffer, kMethodName);
// 16. If IsDetachedBuffer(new) is true, throw a TypeError exception.
if (new_array_buffer->was_neutered()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kDetachedOperation,
isolate->factory()->NewStringFromAsciiChecked(
kMethodName)));
}
// 17. If SameValue(new, O) is true, throw a TypeError exception.
if (new_->SameValue(*args.receiver())) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kArrayBufferSpeciesThis));
}
// 18. If new.[[ArrayBufferByteLength]] < newLen, throw a TypeError exception.
if (new_array_buffer->byte_length()->Number() < new_len) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kArrayBufferTooShort));
}
// 19. NOTE: Side-effects of the above steps may have detached O.
// 20. If IsDetachedBuffer(O) is true, throw a TypeError exception.
if (array_buffer->was_neutered()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kDetachedOperation,
isolate->factory()->NewStringFromAsciiChecked(
kMethodName)));
}
// 21. Let fromBuf be O.[[ArrayBufferData]].
// 22. Let toBuf be new.[[ArrayBufferData]].
// 23. Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen).
size_t first_size = 0, new_len_size = 0;
CHECK(TryNumberToSize(*first_obj, &first_size));
CHECK(TryNumberToSize(*new_len_obj, &new_len_size));
DCHECK(NumberToSize(new_array_buffer->byte_length()) >= new_len_size);
if (new_len_size != 0) {
size_t from_byte_length = NumberToSize(array_buffer->byte_length());
USE(from_byte_length);
DCHECK(first_size <= from_byte_length);
DCHECK(from_byte_length - first_size >= new_len_size);
uint8_t* from_data =
reinterpret_cast<uint8_t*>(array_buffer->backing_store());
uint8_t* to_data =
reinterpret_cast<uint8_t*>(new_array_buffer->backing_store());
CopyBytes(to_data, from_data + first_size, new_len_size);
}
return *new_;
}
} // namespace internal
} // namespace v8
......@@ -289,6 +289,7 @@ class Isolate;
CPP(ArrayBufferConstructor_ConstructStub) \
CPP(ArrayBufferPrototypeGetByteLength) \
CPP(ArrayBufferIsView) \
CPP(ArrayBufferPrototypeSlice) \
\
/* AsyncFunction */ \
TFJ(AsyncFunctionAwaitCaught, 3) \
......
// Copyright 2013 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.
(function(global, utils) {
"use strict";
%CheckIsBootstrapping();
// -------------------------------------------------------------------
// Imports
var GlobalArrayBuffer = global.ArrayBuffer;
var MaxSimple;
var MinSimple;
var SpeciesConstructor;
utils.Import(function(from) {
MaxSimple = from.MaxSimple;
MinSimple = from.MinSimple;
SpeciesConstructor = from.SpeciesConstructor;
});
// -------------------------------------------------------------------
// ES6 Draft 15.13.5.5.3
function ArrayBufferSlice(start, end) {
if (!IS_ARRAYBUFFER(this)) {
throw %make_type_error(kIncompatibleMethodReceiver,
'ArrayBuffer.prototype.slice', this);
}
var relativeStart = TO_INTEGER(start);
if (!IS_UNDEFINED(end)) {
end = TO_INTEGER(end);
}
var first;
var byte_length = %_ArrayBufferGetByteLength(this);
if (relativeStart < 0) {
first = MaxSimple(byte_length + relativeStart, 0);
} else {
first = MinSimple(relativeStart, byte_length);
}
var relativeEnd = IS_UNDEFINED(end) ? byte_length : end;
var fin;
if (relativeEnd < 0) {
fin = MaxSimple(byte_length + relativeEnd, 0);
} else {
fin = MinSimple(relativeEnd, byte_length);
}
if (fin < first) {
fin = first;
}
var newLen = fin - first;
var constructor = SpeciesConstructor(this, GlobalArrayBuffer, true);
var result = new constructor(newLen);
if (!IS_ARRAYBUFFER(result)) {
throw %make_type_error(kIncompatibleMethodReceiver,
'ArrayBuffer.prototype.slice', result);
}
// Checks for detached source/target ArrayBuffers are done inside of
// %ArrayBufferSliceImpl; the reordering of checks does not violate
// the spec because all exceptions thrown are TypeErrors.
if (result === this) {
throw %make_type_error(kArrayBufferSpeciesThis);
}
if (%_ArrayBufferGetByteLength(result) < newLen) {
throw %make_type_error(kArrayBufferTooShort);
}
%ArrayBufferSliceImpl(this, result, first, newLen);
return result;
}
utils.InstallFunctions(GlobalArrayBuffer.prototype, DONT_ENUM, [
"slice", ArrayBufferSlice
]);
})
......@@ -2331,6 +2331,42 @@ MaybeHandle<Object> Object::ArraySpeciesConstructor(
}
}
// ES6 section 7.3.20 SpeciesConstructor ( O, defaultConstructor )
MUST_USE_RESULT MaybeHandle<Object> Object::SpeciesConstructor(
Isolate* isolate, Handle<JSReceiver> recv,
Handle<JSFunction> default_ctor) {
Handle<Object> ctor_obj;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, ctor_obj,
JSObject::GetProperty(recv, isolate->factory()->constructor_string()),
Object);
if (ctor_obj->IsUndefined(isolate)) return default_ctor;
if (!ctor_obj->IsJSReceiver()) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kConstructorNotReceiver),
Object);
}
Handle<JSReceiver> ctor = Handle<JSReceiver>::cast(ctor_obj);
Handle<Object> species;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, species,
JSObject::GetProperty(ctor, isolate->factory()->species_symbol()),
Object);
if (species->IsNullOrUndefined(isolate)) {
return default_ctor;
}
if (species->IsConstructor()) return species;
THROW_NEW_ERROR(
isolate, NewTypeError(MessageTemplate::kSpeciesNotConstructor), Object);
}
bool Object::IterationHasObservableEffects() {
// Check that this object is an array.
if (!IsJSArray()) return true;
......
......@@ -1437,6 +1437,11 @@ class Object {
MUST_USE_RESULT static MaybeHandle<Object> ArraySpeciesConstructor(
Isolate* isolate, Handle<Object> original_array);
// ES6 section 7.3.20 SpeciesConstructor ( O, defaultConstructor )
MUST_USE_RESULT static MaybeHandle<Object> SpeciesConstructor(
Isolate* isolate, Handle<JSReceiver> recv,
Handle<JSFunction> default_ctor);
// Tries to convert an object to an array length. Returns true and sets the
// output parameter if it succeeds.
inline bool ToArrayLength(uint32_t* index);
......
......@@ -1306,43 +1306,6 @@ RUNTIME_FUNCTION(Runtime_StringReplaceNonGlobalRegExpWithFunction) {
namespace {
// ES##sec-speciesconstructor
// SpeciesConstructor ( O, defaultConstructor )
MUST_USE_RESULT MaybeHandle<Object> SpeciesConstructor(
Isolate* isolate, Handle<JSReceiver> recv,
Handle<JSFunction> default_ctor) {
Handle<Object> ctor_obj;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, ctor_obj,
JSObject::GetProperty(recv, isolate->factory()->constructor_string()),
Object);
if (ctor_obj->IsUndefined(isolate)) return default_ctor;
if (!ctor_obj->IsJSReceiver()) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kConstructorNotReceiver),
Object);
}
Handle<JSReceiver> ctor = Handle<JSReceiver>::cast(ctor_obj);
Handle<Object> species;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, species,
JSObject::GetProperty(ctor, isolate->factory()->species_symbol()),
Object);
if (species->IsNullOrUndefined(isolate)) {
return default_ctor;
}
if (species->IsConstructor()) return species;
THROW_NEW_ERROR(
isolate, NewTypeError(MessageTemplate::kSpeciesNotConstructor), Object);
}
MUST_USE_RESULT MaybeHandle<Object> ToUint32(Isolate* isolate,
Handle<Object> object,
uint32_t* out) {
......@@ -1384,7 +1347,7 @@ RUNTIME_FUNCTION(Runtime_RegExpSplit) {
Handle<JSFunction> regexp_fun = isolate->regexp_function();
Handle<Object> ctor;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, ctor, SpeciesConstructor(isolate, recv, regexp_fun));
isolate, ctor, Object::SpeciesConstructor(isolate, recv, regexp_fun));
Handle<Object> flags_obj;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
......
......@@ -21,39 +21,6 @@ RUNTIME_FUNCTION(Runtime_ArrayBufferGetByteLength) {
}
RUNTIME_FUNCTION(Runtime_ArrayBufferSliceImpl) {
HandleScope scope(isolate);
DCHECK_EQ(4, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, source, 0);
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, target, 1);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(first, 2);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(new_length, 3);
if (source->was_neutered() || target->was_neutered()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kDetachedOperation,
isolate->factory()->NewStringFromAsciiChecked(
"ArrayBuffer.prototype.slice")));
}
CHECK(!source.is_identical_to(target));
size_t start = 0, target_length = 0;
CHECK(TryNumberToSize(*first, &start));
CHECK(TryNumberToSize(*new_length, &target_length));
CHECK(NumberToSize(target->byte_length()) >= target_length);
if (target_length == 0) return isolate->heap()->undefined_value();
size_t source_byte_length = NumberToSize(source->byte_length());
CHECK(start <= source_byte_length);
CHECK(source_byte_length - start >= target_length);
uint8_t* source_data = reinterpret_cast<uint8_t*>(source->backing_store());
uint8_t* target_data = reinterpret_cast<uint8_t*>(target->backing_store());
CopyBytes(target_data, source_data + start, target_length);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_ArrayBufferNeuter) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
......
......@@ -615,7 +615,6 @@ namespace internal {
#define FOR_EACH_INTRINSIC_TYPEDARRAY(F) \
F(ArrayBufferGetByteLength, 1, 1) \
F(ArrayBufferSliceImpl, 4, 1) \
F(ArrayBufferNeuter, 1, 1) \
F(TypedArrayInitialize, 6, 1) \
F(TypedArrayInitializeFromArrayLike, 4, 1) \
......
......@@ -2266,7 +2266,6 @@
'js/v8natives.js',
'js/array.js',
'js/string.js',
'js/arraybuffer.js',
'js/typedarray.js',
'js/collection.js',
'js/weak-collection.js',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment