Commit d1c15973 authored by peterwmwong's avatar peterwmwong Committed by Commit Bot

[builtins] Port TypedArray join, toString, and toLocaleString to Torque.

Micro-benchmarks show speed improvements across the various types:

TypedArrays-JoinBigIntTypes                7246   8297  14.50%
TypedArrays-JoinBigIntTypes                7194   8637  20.06%
TypedArrays-JoinBigIntTypes                7258   8586  18.30%
TypedArrays-JoinFloatTypes                24461  28628  17.04%
TypedArrays-JoinFloatTypes                24523  29647  20.89%
TypedArrays-JoinFloatTypes                24419  29327  20.10%
TypedArrays-JoinIntTypes                  23378  33928  45.13%
TypedArrays-JoinIntTypes                  23333  34034  45.86%
TypedArrays-JoinIntTypes                  21653  34000  57.02%
TypedArrays-JoinWithSeparatorBigIntTypes   6620   7339  10.86%
TypedArrays-JoinWithSeparatorBigIntTypes   6566   7579  15.43%
TypedArrays-JoinWithSeparatorBigIntTypes   6631   7481  12.82%
TypedArrays-JoinWithSeparatorFloatTypes   18695  19670   5.22%
TypedArrays-JoinWithSeparatorFloatTypes   18518  20088   8.48%
TypedArrays-JoinWithSeparatorFloatTypes   18482  20193   9.26%
TypedArrays-JoinWithSeparatorIntTypes     17849  21482  20.35%
TypedArrays-JoinWithSeparatorIntTypes     17831  21578  21.01%
TypedArrays-JoinWithSeparatorIntTypes     17937  21578  20.30%

Drive-by: Removed unused CSA helper InternalArrayCreate.

Bug: v8:7624
Change-Id: I8e63815982439cfd2267417d03cd2b71b4b7a812
Reviewed-on: https://chromium-review.googlesource.com/c/1369330
Commit-Queue: Peter Wong <peter.wm.wong@gmail.com>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarPeter Marshall <petermarshall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58167}
parent 9b4f14de
......@@ -724,8 +724,6 @@ action("js2c") {
"src/js/macros.py",
"src/message-template.h",
"src/js/prologue.js",
"src/js/array.js",
"src/js/typedarray.js",
]
outputs = [
......
......@@ -1629,6 +1629,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
*info);
}
Handle<JSFunction> array_prototype_to_string_fun;
{ // --- A r r a y ---
Handle<JSFunction> array_function = InstallFunction(
isolate_, global, "Array", JS_ARRAY_TYPE, JSArray::kSize, 0,
......@@ -1745,8 +1746,9 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kArrayReduceRight, 1, false);
SimpleInstallFunction(isolate_, proto, "toLocaleString",
Builtins::kArrayPrototypeToLocaleString, 0, false);
SimpleInstallFunction(isolate_, proto, "toString",
Builtins::kArrayPrototypeToString, 0, false);
array_prototype_to_string_fun =
SimpleInstallFunction(isolate_, proto, "toString",
Builtins::kArrayPrototypeToString, 0, false);
}
{ // --- A r r a y I t e r a t o r ---
......@@ -3201,6 +3203,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kTypedArrayPrototypeIncludes, 1, false);
SimpleInstallFunction(isolate_, prototype, "indexOf",
Builtins::kTypedArrayPrototypeIndexOf, 1, false);
SimpleInstallFunction(isolate_, prototype, "join",
Builtins::kTypedArrayPrototypeJoin, 1, false);
SimpleInstallFunction(isolate_, prototype, "lastIndexOf",
Builtins::kTypedArrayPrototypeLastIndexOf, 1, false);
SimpleInstallFunction(isolate_, prototype, "map",
......@@ -3221,6 +3225,11 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kTypedArrayPrototypeSort, 1, false);
SimpleInstallFunction(isolate_, prototype, "subarray",
Builtins::kTypedArrayPrototypeSubArray, 2, false);
SimpleInstallFunction(isolate_, prototype, "toLocaleString",
Builtins::kTypedArrayPrototypeToLocaleString, 0,
false);
JSObject::AddProperty(isolate_, prototype, factory->toString_string(),
array_prototype_to_string_fun, DONT_ENUM);
}
{ // -- T y p e d A r r a y s
......
......@@ -55,6 +55,14 @@ namespace array {
}
}
builtin LoadJoinTypedElement<T: type>(
context: Context, receiver: JSReceiver, k: Number): Object {
const typedArray: JSTypedArray = UnsafeCast<JSTypedArray>(receiver);
return typed_array::LoadFixedTypedArrayElementAsTagged(
typedArray.data_ptr, UnsafeCast<Smi>(k),
typed_array::KindForArrayType<T>(), SMI_PARAMETERS);
}
transitioning builtin ConvertToLocaleString(
context: Context, element: Object, locales: Object,
options: Object): String {
......@@ -80,9 +88,17 @@ namespace array {
// Verifies the current element JSArray accessor can still be safely used
// (see LoadJoinElement<ElementsAccessor>).
macro CannotUseSameArrayAccessor(implicit context: Context)(
originalMap: Object, originalLen: Object, receiver: JSReceiver): never
macro CannotUseSameArrayAccessor<T: type>(implicit context: Context)(
loadFn: LoadJoinElementFn, receiver: JSReceiver, originalMap: Map,
originalLen: Number): never
labels Cannot, Can;
CannotUseSameArrayAccessor<JSArray>(implicit context: Context)(
loadFn: LoadJoinElementFn, receiver: JSReceiver, originalMap: Map,
originalLen: Number): never
labels Cannot, Can {
if (loadFn == LoadJoinElement<GenericElementsAccessor>) goto Can;
const array: JSArray = UnsafeCast<JSArray>(receiver);
if (originalMap != array.map) goto Cannot;
if (originalLen != array.length) goto Cannot;
......@@ -90,6 +106,18 @@ namespace array {
goto Can;
}
CannotUseSameArrayAccessor<JSTypedArray>(implicit context: Context)(
loadFn: LoadJoinElementFn, receiver: JSReceiver, initialMap: Map,
initialLen: Number): never
labels Cannot, Can {
// It is assumed that neither loading a typed array element nor converting a
// number to string have side-effects. As such, it safe to use the initial
// LoadJoinElement specialization and it cannot change through out the join
// call.
assert(!IsDetachedBuffer(UnsafeCast<JSTypedArray>(receiver).buffer));
goto Can;
}
// Calculates the running total length of the resulting string. If the
// calculated length exceeds the maximum string length (see
// String::kMaxLength), throws a range error.
......@@ -245,15 +273,15 @@ namespace array {
buffer.fixedArray, buffer.index, sep, r);
}
transitioning macro ArrayJoinImpl(
context: Context, receiver: JSReceiver, sep: String, lengthNumber: Number,
transitioning macro ArrayJoinImpl<T: type>(implicit context: Context)(
receiver: JSReceiver, sep: String, lengthNumber: Number,
useToLocaleString: constexpr bool, locales: Object, options: Object,
initialLoadJoinElement: LoadJoinElementFn): String {
initialLoadFn: LoadJoinElementFn): String {
const initialMap: Map = receiver.map;
const len: uintptr = Convert<uintptr>(lengthNumber);
const separatorLength: intptr = sep.length;
let nofSeparators: intptr = 0;
let loadJoinElements: LoadJoinElementFn = initialLoadJoinElement;
let loadFn: LoadJoinElementFn = initialLoadFn;
let buffer: Buffer = BufferInit(len, sep);
// 6. Let k be 0.
......@@ -264,20 +292,15 @@ namespace array {
if (k > 0) {
// a. If k > 0, let R be the string-concatenation of R and sep.
nofSeparators = nofSeparators + 1;
// Verify the current LoadJoinElement specialization can safely be
// used. Otherwise, fall back to generic element access (see
// LoadJoinElement<GenericElementsAccessor>).
if (loadJoinElements != LoadJoinElement<GenericElementsAccessor>&&
CannotUseSameArrayAccessor(initialMap, lengthNumber, receiver))
if (CannotUseSameArrayAccessor<T>(
loadFn, receiver, initialMap, lengthNumber))
deferred {
loadJoinElements = LoadJoinElement<GenericElementsAccessor>;
loadFn = LoadJoinElement<GenericElementsAccessor>;
}
}
// b. Let element be ? Get(O, ! ToString(k)).
const element: Object =
loadJoinElements(context, receiver, Convert<Number>(k++));
const element: Object = loadFn(context, receiver, Convert<Number>(k++));
// c. If element is undefined or null, let next be the empty String;
// otherwise, let next be ? ToString(element).
......@@ -313,12 +336,16 @@ namespace array {
return BufferJoin(buffer, sep);
}
transitioning macro ArrayJoin(implicit context: Context)(
transitioning macro ArrayJoin<T: type>(implicit context: Context)(
useToLocaleString: constexpr bool, receiver: JSReceiver, sep: String,
lenNumber: Number, locales: Object, options: Object): Object;
ArrayJoin<JSArray>(implicit context: Context)(
useToLocaleString: constexpr bool, receiver: JSReceiver, sep: String,
lenNumber: Number, locales: Object, options: Object): Object {
const map: Map = receiver.map;
const kind: ElementsKind = map.elements_kind;
let loadJoinElements: LoadJoinElementFn;
let loadFn: LoadJoinElementFn;
try {
const array: JSArray = Cast<JSArray>(receiver) otherwise IfSlowPath;
......@@ -327,9 +354,9 @@ namespace array {
if (IsNoElementsProtectorCellInvalid()) goto IfSlowPath;
if (IsElementsKindLessThanOrEqual(kind, HOLEY_ELEMENTS)) {
loadJoinElements = LoadJoinElement<FastSmiOrObjectElements>;
loadFn = LoadJoinElement<FastSmiOrObjectElements>;
} else if (IsElementsKindLessThanOrEqual(kind, HOLEY_DOUBLE_ELEMENTS)) {
loadJoinElements = LoadJoinElement<FastDoubleElements>;
loadFn = LoadJoinElement<FastDoubleElements>;
} else if (kind == DICTIONARY_ELEMENTS)
deferred {
const dict: NumberDictionary =
......@@ -346,7 +373,7 @@ namespace array {
ThrowInvalidStringLength(context);
}
} else {
loadJoinElements = LoadJoinElement<DictionaryElements>;
loadFn = LoadJoinElement<DictionaryElements>;
}
}
else {
......@@ -354,11 +381,52 @@ namespace array {
}
}
label IfSlowPath {
loadJoinElements = LoadJoinElement<GenericElementsAccessor>;
loadFn = LoadJoinElement<GenericElementsAccessor>;
}
return ArrayJoinImpl<JSArray>(
receiver, sep, lenNumber, useToLocaleString, locales, options, loadFn);
}
ArrayJoin<JSTypedArray>(implicit context: Context)(
useToLocaleString: constexpr bool, receiver: JSReceiver, sep: String,
lenNumber: Number, locales: Object, options: Object): Object {
const map: Map = receiver.map;
const kind: ElementsKind = map.elements_kind;
let loadFn: LoadJoinElementFn;
if (IsElementsKindGreaterThan(kind, UINT32_ELEMENTS)) {
if (kind == INT32_ELEMENTS) {
loadFn = LoadJoinTypedElement<FixedInt32Array>;
} else if (kind == FLOAT32_ELEMENTS) {
loadFn = LoadJoinTypedElement<FixedFloat32Array>;
} else if (kind == FLOAT64_ELEMENTS) {
loadFn = LoadJoinTypedElement<FixedFloat64Array>;
} else if (kind == UINT8_CLAMPED_ELEMENTS) {
loadFn = LoadJoinTypedElement<FixedUint8ClampedArray>;
} else if (kind == BIGUINT64_ELEMENTS) {
loadFn = LoadJoinTypedElement<FixedBigUint64Array>;
} else if (kind == BIGINT64_ELEMENTS) {
loadFn = LoadJoinTypedElement<FixedBigInt64Array>;
} else {
unreachable;
}
} else {
if (kind == UINT8_ELEMENTS) {
loadFn = LoadJoinTypedElement<FixedUint8Array>;
} else if (kind == INT8_ELEMENTS) {
loadFn = LoadJoinTypedElement<FixedInt8Array>;
} else if (kind == UINT16_ELEMENTS) {
loadFn = LoadJoinTypedElement<FixedUint16Array>;
} else if (kind == INT16_ELEMENTS) {
loadFn = LoadJoinTypedElement<FixedInt16Array>;
} else if (kind == UINT32_ELEMENTS) {
loadFn = LoadJoinTypedElement<FixedUint32Array>;
} else {
unreachable;
}
}
return ArrayJoinImpl(
context, receiver, sep, lenNumber, useToLocaleString, locales, options,
loadJoinElements);
return ArrayJoinImpl<JSTypedArray>(
receiver, sep, lenNumber, useToLocaleString, locales, options, loadFn);
}
// The Join Stack detects cyclical calls to Array Join builtins
......@@ -474,18 +542,10 @@ namespace array {
}
// Main entry point for all builtins using Array Join functionality.
transitioning macro CycleProtectedArrayJoin(implicit context: Context)(
useToLocaleString: constexpr bool, receiver: Object, sepObj: Object,
locales: Object, options: Object): Object {
// 1. Let O be ? ToObject(this value).
const o: JSReceiver = ToObject_Inline(context, receiver);
// 2. Let len be ? ToLength(? Get(O, "length")).
const len: Number = GetLengthProperty(o);
// Only handle valid array lengths. Although the spec allows larger values,
// this matches historical V8 behavior.
if (len > kMaxArrayIndex + 1) ThrowTypeError(context, kInvalidArrayLength);
transitioning macro CycleProtectedArrayJoin<T: type>(implicit context:
Context)(
useToLocaleString: constexpr bool, o: JSReceiver, len: Number,
sepObj: Object, locales: Object, options: Object): Object {
// 3. If separator is undefined, let sep be the single-element String ",".
// 4. Else, let sep be ? ToString(separator).
let sep: String =
......@@ -496,7 +556,7 @@ namespace array {
if (len > 0 && JoinStackPushInline(o)) {
try {
const result: Object =
ArrayJoin(useToLocaleString, o, sep, len, locales, options);
ArrayJoin<T>(useToLocaleString, o, sep, len, locales, options);
JoinStackPopInline(o);
return result;
} catch (e) deferred {
......@@ -512,19 +572,42 @@ namespace array {
transitioning javascript builtin
ArrayPrototypeJoin(context: Context, receiver: Object, ...arguments): Object {
const separator: Object = arguments[0];
return CycleProtectedArrayJoin(
false, receiver, separator, Undefined, Undefined);
// 1. Let O be ? ToObject(this value).
const o: JSReceiver = ToObject_Inline(context, receiver);
// 2. Let len be ? ToLength(? Get(O, "length")).
const len: Number = GetLengthProperty(o);
// Only handle valid array lengths. Although the spec allows larger values,
// this matches historical V8 behavior.
if (len > kMaxArrayIndex + 1) ThrowTypeError(context, kInvalidArrayLength);
return CycleProtectedArrayJoin<JSArray>(
false, o, len, separator, Undefined, Undefined);
}
// https://tc39.github.io/ecma262/#sec-array.prototype.toLocaleString
// https://tc39.github.io/ecma262/#sec-array.prototype.tolocalestring
transitioning javascript builtin ArrayPrototypeToLocaleString(
context: Context, receiver: Object, ...arguments): Object {
const locales: Object = arguments[0];
const options: Object = arguments[1];
return CycleProtectedArrayJoin(true, receiver, ',', locales, options);
// 1. Let O be ? ToObject(this value).
const o: JSReceiver = ToObject_Inline(context, receiver);
// 2. Let len be ? ToLength(? Get(O, "length")).
const len: Number = GetLengthProperty(o);
// Only handle valid array lengths. Although the spec allows larger values,
// this matches historical V8 behavior.
if (len > kMaxArrayIndex + 1) ThrowTypeError(context, kInvalidArrayLength);
return CycleProtectedArrayJoin<JSArray>(
true, o, len, ',', locales, options);
}
// https://tc39.github.io/ecma262/#sec-array.prototype.toString
// https://tc39.github.io/ecma262/#sec-array.prototype.tostring
transitioning javascript builtin ArrayPrototypeToString(
context: Context, receiver: Object, ...arguments): Object {
// 1. Let array be ? ToObject(this value).
......@@ -544,4 +627,35 @@ namespace array {
return ObjectToString(context, array);
}
}
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.join
transitioning javascript builtin TypedArrayPrototypeJoin(
context: Context, receiver: Object, ...arguments): Object {
const separator: Object = arguments[0];
// Spec: ValidateTypedArray is applied to the this value prior to evaluating
// the algorithm.
const typedArray: JSTypedArray = typed_array::ValidateTypedArray(
context, receiver, '%TypedArray%.prototype.join');
const length: Smi = typedArray.length;
return CycleProtectedArrayJoin<JSTypedArray>(
false, typedArray, length, separator, Undefined, Undefined);
}
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.tolocalestring
transitioning javascript builtin TypedArrayPrototypeToLocaleString(
context: Context, receiver: Object, ...arguments): Object {
const locales: Object = arguments[0];
const options: Object = arguments[1];
// Spec: ValidateTypedArray is applied to the this value prior to evaluating
// the algorithm.
const typedArray: JSTypedArray = typed_array::ValidateTypedArray(
context, receiver, '%TypedArray%.prototype.toLocaleString');
const length: Smi = typedArray.length;
return CycleProtectedArrayJoin<JSTypedArray>(
true, typedArray, length, ',', locales, options);
}
}
......@@ -322,7 +322,6 @@ extern macro ThrowTypeError(
Context, constexpr MessageTemplate, Object, Object, Object): never;
extern macro ArraySpeciesCreate(Context, Object, Number): JSReceiver;
extern macro ArrayCreate(implicit context: Context)(Number): JSArray;
extern macro InternalArrayCreate(Context, Number): JSArray;
extern macro EnsureArrayPushable(Map): ElementsKind
labels Bailout;
extern macro EnsureArrayLengthWritable(Map) labels Bailout;
......@@ -526,6 +525,8 @@ extern macro IsValidPositiveSmi(intptr): bool;
extern macro HeapObjectToJSDataView(HeapObject): JSDataView
labels CastError;
extern macro HeapObjectToJSTypedArray(HeapObject): JSTypedArray
labels CastError;
extern macro TaggedToHeapObject(Object): HeapObject
labels CastError;
extern macro TaggedToSmi(Object): Smi
......@@ -578,6 +579,12 @@ CastHeapObject<JSDataView>(o: HeapObject): JSDataView
return HeapObjectToJSDataView(o) otherwise CastError;
}
CastHeapObject<JSTypedArray>(o: HeapObject): JSTypedArray
labels CastError {
if (IsJSTypedArray(o)) return %RawObjectCast<JSTypedArray>(o);
goto CastError;
}
CastHeapObject<Callable>(o: HeapObject): Callable
labels CastError {
return HeapObjectToCallable(o) otherwise CastError;
......@@ -1180,6 +1187,7 @@ extern macro IsJSArray(HeapObject): bool;
extern macro IsMap(HeapObject): bool;
extern macro IsJSFunction(HeapObject): bool;
extern macro IsJSObject(HeapObject): bool;
extern macro IsJSTypedArray(HeapObject): bool;
extern macro IsNumberDictionary(HeapObject): bool;
extern macro IsFixedTypedArray(HeapObject): bool;
extern macro IsContext(HeapObject): bool;
......
......@@ -12928,14 +12928,6 @@ TNode<JSReceiver> CodeStubAssembler::ArraySpeciesCreate(TNode<Context> context,
return Construct(context, constructor, len);
}
TNode<JSReceiver> CodeStubAssembler::InternalArrayCreate(TNode<Context> context,
TNode<Number> len) {
TNode<Context> native_context = LoadNativeContext(context);
TNode<JSReceiver> constructor = CAST(LoadContextElement(
native_context, Context::INTERNAL_ARRAY_FUNCTION_INDEX));
return Construct(context, constructor, len);
}
Node* CodeStubAssembler::IsDetachedBuffer(Node* buffer) {
CSA_ASSERT(this, HasInstanceType(buffer, JS_ARRAY_BUFFER_TYPE));
TNode<Uint32T> buffer_bit_field = LoadJSArrayBufferBitField(CAST(buffer));
......
......@@ -1516,8 +1516,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<JSReceiver> ArraySpeciesCreate(TNode<Context> context,
TNode<Object> originalArray,
TNode<Number> len);
TNode<JSReceiver> InternalArrayCreate(TNode<Context> context,
TNode<Number> len);
void FillFixedArrayWithValue(ElementsKind kind, Node* array, Node* from_index,
Node* to_index, RootIndex value_root_index,
......
// Copyright 2012 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, extrasUtils) {
"use strict";
%CheckIsBootstrapping();
// -------------------------------------------------------------------
// Imports
var GlobalArray = global.Array;
var InternalArray = utils.InternalArray;
var ObjectToString = global.Object.prototype.toString;
var iteratorSymbol = utils.ImportNow("iterator_symbol");
var unscopablesSymbol = utils.ImportNow("unscopables_symbol");
// -------------------------------------------------------------------
macro INVERT_NEG_ZERO(arg)
((arg) + 0)
endmacro
function ArraySpeciesCreate(array, length) {
length = INVERT_NEG_ZERO(length);
var constructor = %ArraySpeciesConstructor(array);
return new constructor(length);
}
// TODO(pwong): Remove once TypedArray.prototype.join() is ported to Torque.
function KeySortCompare(a, b) {
return a - b;
}
// TODO(pwong): Remove once TypedArray.prototype.join() is ported to Torque.
function GetSortedArrayKeys(array, indices) {
if (IS_NUMBER(indices)) {
// It's an interval
var limit = indices;
var keys = new InternalArray();
for (var i = 0; i < limit; ++i) {
var e = array[i];
if (!IS_UNDEFINED(e) || i in array) {
keys.push(i);
}
}
return keys;
}
return InnerArraySort(indices, indices.length, KeySortCompare);
}
// TODO(pwong): Remove once TypedArray.prototype.join() is ported to Torque.
function SparseJoinWithSeparatorJS(
array, keys, length, use_locale, separator, locales, options) {
var keys_length = keys.length;
var elements = new InternalArray(keys_length * 2);
for (var i = 0; i < keys_length; i++) {
var key = keys[i];
elements[i * 2] = key;
elements[i * 2 + 1] = ConvertToString(
use_locale, array[key], locales, options);
}
return %SparseJoinWithSeparator(elements, length, separator);
}
// TODO(pwong): Remove once TypedArray.prototype.join() is ported to Torque.
// Optimized for sparse arrays if separator is ''.
function SparseJoin(array, keys, use_locale, locales, options) {
var keys_length = keys.length;
var elements = new InternalArray(keys_length);
for (var i = 0; i < keys_length; i++) {
elements[i] = ConvertToString(use_locale, array[keys[i]], locales, options);
}
return %StringBuilderConcat(elements, keys_length, '');
}
// TODO(pwong): Remove once TypedArray.prototype.join() is ported to Torque.
function UseSparseVariant(array, length, is_array, touched) {
// Only use the sparse variant on arrays that are likely to be sparse and the
// number of elements touched in the operation is relatively small compared to
// the overall size of the array.
if (!is_array || length < 1000 || %HasComplexElements(array)) {
return false;
}
if (!%_IsSmi(length)) {
return true;
}
var elements_threshold = length >> 2; // No more than 75% holes
var estimated_elements = %EstimateNumberOfElements(array);
return (estimated_elements < elements_threshold) &&
(touched > estimated_elements * 4);
}
// TODO(pwong): Remove once TypedArray.prototype.join() is ported to Torque.
function Stack() {
this.length = 0;
this.values = new InternalArray();
}
// Predeclare the instance variables on the prototype. Otherwise setting them in
// the constructor will leak the instance through settings on Object.prototype.
Stack.prototype.length = null;
Stack.prototype.values = null;
function StackPush(stack, value) {
stack.values[stack.length++] = value;
}
function StackPop(stack) {
stack.values[--stack.length] = null
}
function StackHas(stack, v) {
var length = stack.length;
var values = stack.values;
for (var i = 0; i < length; i++) {
if (values[i] === v) return true;
}
return false;
}
// Global list of arrays visited during toString, toLocaleString and
// join invocations.
var visited_arrays = new Stack();
// TODO(pwong): Remove once TypedArray.prototype.join() is ported to Torque.
function DoJoin(
array, length, is_array, separator, use_locale, locales, options) {
if (UseSparseVariant(array, length, is_array, length)) {
%NormalizeElements(array);
var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, length));
if (separator === '') {
if (keys.length === 0) return '';
return SparseJoin(array, keys, use_locale, locales, options);
} else {
return SparseJoinWithSeparatorJS(
array, keys, length, use_locale, separator, locales, options);
}
}
// Fast case for one-element arrays.
if (length === 1) {
return ConvertToString(use_locale, array[0], locales, options);
}
// Construct an array for the elements.
var elements = new InternalArray(length);
for (var i = 0; i < length; i++) {
elements[i] = ConvertToString(use_locale, array[i], locales, options);
}
if (separator === '') {
return %StringBuilderConcat(elements, length, '');
} else {
return %StringBuilderJoin(elements, length, separator);
}
}
// TODO(pwong): Remove once TypedArray.prototype.join() is ported to Torque.
function Join(array, length, separator, use_locale, locales, options) {
if (length === 0) return '';
var is_array = IS_ARRAY(array);
if (is_array) {
// If the array is cyclic, return the empty string for already
// visited arrays.
if (StackHas(visited_arrays, array)) return '';
StackPush(visited_arrays, array);
}
// Attempt to convert the elements.
try {
return DoJoin(
array, length, is_array, separator, use_locale, locales, options);
} finally {
// Make sure to remove the last element of the visited array no
// matter what happens.
if (is_array) StackPop(visited_arrays);
}
}
// TODO(pwong): Remove once TypedArray.prototype.join() is ported to Torque.
function ConvertToString(use_locale, x, locales, options) {
if (IS_NULL_OR_UNDEFINED(x)) return '';
if (use_locale) {
if (IS_NULL_OR_UNDEFINED(locales)) {
return TO_STRING(x.toLocaleString());
} else if (IS_NULL_OR_UNDEFINED(options)) {
return TO_STRING(x.toLocaleString(locales));
}
return TO_STRING(x.toLocaleString(locales, options));
}
return TO_STRING(x);
}
// -------------------------------------------------------------------
// ecma402 #sup-array.prototype.tolocalestring
// TODO(pwong): Remove once TypedArray.prototype.join() is ported to Torque.
function InnerArrayToLocaleString(array, length, locales, options) {
return Join(array, TO_LENGTH(length), ',', true, locales, options);
}
// TODO(pwong): Remove once TypedArray.prototype.join() is ported to Torque.
function InnerArrayJoin(separator, array, length) {
if (IS_UNDEFINED(separator)) {
separator = ',';
} else {
separator = TO_STRING(separator);
}
// Fast case for one-element arrays.
if (length === 1) {
var e = array[0];
if (IS_NULL_OR_UNDEFINED(e)) return '';
return TO_STRING(e);
}
return Join(array, length, separator, false);
}
// Oh the humanity... don't remove the following function because js2c for some
// reason gets symbol minifiation wrong if it's not there. Instead of spending
// the time fixing js2c (which will go away when all of the internal .js runtime
// files are gone), just keep this work-around.
function ArraySliceFallback(start, end) {
return null;
}
// TODO(pwong): Remove once TypedArray.prototype.join() is ported to Torque.
function InnerArraySort(array, length, comparefn) {
// In-place QuickSort algorithm.
// For short (length <= 10) arrays, insertion sort is used for efficiency.
if (!IS_CALLABLE(comparefn)) {
comparefn = function (x, y) {
if (x === y) return 0;
if (%_IsSmi(x) && %_IsSmi(y)) {
return %SmiLexicographicCompare(x, y);
}
x = TO_STRING(x);
y = TO_STRING(y);
if (x == y) return 0;
else return x < y ? -1 : 1;
};
}
function InsertionSort(a, from, to) {
for (var i = from + 1; i < to; i++) {
var element = a[i];
for (var j = i - 1; j >= from; j--) {
var tmp = a[j];
var order = comparefn(tmp, element);
if (order > 0) {
a[j + 1] = tmp;
} else {
break;
}
}
a[j + 1] = element;
}
};
function GetThirdIndex(a, from, to) {
var t_array = new InternalArray();
// Use both 'from' and 'to' to determine the pivot candidates.
var increment = 200 + ((to - from) & 15);
var j = 0;
from += 1;
to -= 1;
for (var i = from; i < to; i += increment) {
t_array[j] = [i, a[i]];
j++;
}
t_array.sort(function(a, b) {
return comparefn(a[1], b[1]);
});
var third_index = t_array[t_array.length >> 1][0];
return third_index;
}
function QuickSort(a, from, to) {
var third_index = 0;
while (true) {
// Insertion sort is faster for short arrays.
if (to - from <= 10) {
InsertionSort(a, from, to);
return;
}
if (to - from > 1000) {
third_index = GetThirdIndex(a, from, to);
} else {
third_index = from + ((to - from) >> 1);
}
// Find a pivot as the median of first, last and middle element.
var v0 = a[from];
var v1 = a[to - 1];
var v2 = a[third_index];
var c01 = comparefn(v0, v1);
if (c01 > 0) {
// v1 < v0, so swap them.
var tmp = v0;
v0 = v1;
v1 = tmp;
} // v0 <= v1.
var c02 = comparefn(v0, v2);
if (c02 >= 0) {
// v2 <= v0 <= v1.
var tmp = v0;
v0 = v2;
v2 = v1;
v1 = tmp;
} else {
// v0 <= v1 && v0 < v2
var c12 = comparefn(v1, v2);
if (c12 > 0) {
// v0 <= v2 < v1
var tmp = v1;
v1 = v2;
v2 = tmp;
}
}
// v0 <= v1 <= v2
a[from] = v0;
a[to - 1] = v2;
var pivot = v1;
var low_end = from + 1; // Upper bound of elements lower than pivot.
var high_start = to - 1; // Lower bound of elements greater than pivot.
a[third_index] = a[low_end];
a[low_end] = pivot;
// From low_end to i are elements equal to pivot.
// From i to high_start are elements that haven't been compared yet.
partition: for (var i = low_end + 1; i < high_start; i++) {
var element = a[i];
var order = comparefn(element, pivot);
if (order < 0) {
a[i] = a[low_end];
a[low_end] = element;
low_end++;
} else if (order > 0) {
do {
high_start--;
if (high_start == i) break partition;
var top_elem = a[high_start];
order = comparefn(top_elem, pivot);
} while (order > 0);
a[i] = a[high_start];
a[high_start] = element;
if (order < 0) {
element = a[i];
a[i] = a[low_end];
a[low_end] = element;
low_end++;
}
}
}
if (to - high_start < low_end - from) {
QuickSort(a, high_start, to);
to = low_end;
} else {
QuickSort(a, from, low_end);
from = high_start;
}
}
};
if (length < 2) return array;
// For compatibility with JSC, we also sort elements inherited from
// the prototype chain on non-Array objects.
// We do this by copying them to this object and sorting only
// own elements. This is not very efficient, but sorting with
// inherited elements happens very, very rarely, if at all.
// The specification allows "implementation dependent" behavior
// if an element on the prototype chain has an element that
// might interact with sorting.
//
// We also move all non-undefined elements to the front of the
// array and move the undefineds after that. Holes are removed.
// This happens for Array as well as non-Array objects.
var num_non_undefined = %PrepareElementsForSort(array, length);
QuickSort(array, 0, num_non_undefined);
return array;
}
// Set up unscopable properties on the Array.prototype object.
var unscopables = {
__proto__: null,
copyWithin: true,
entries: true,
fill: true,
find: true,
findIndex: true,
includes: true,
keys: true,
};
%ToFastProperties(unscopables);
%AddNamedProperty(GlobalArray.prototype, unscopablesSymbol, unscopables,
DONT_ENUM | READ_ONLY);
var ArrayIndexOf = GlobalArray.prototype.indexOf;
var ArrayJoin = GlobalArray.prototype.join;
var ArrayPop = GlobalArray.prototype.pop;
var ArrayPush = GlobalArray.prototype.push;
var ArraySlice = GlobalArray.prototype.slice;
var ArrayShift = GlobalArray.prototype.shift;
var ArraySort = GlobalArray.prototype.sort;
var ArraySplice = GlobalArray.prototype.splice;
var ArrayToString = GlobalArray.prototype.toString;
var ArrayUnshift = GlobalArray.prototype.unshift;
// Array prototype functions that return iterators. They are exposed to the
// public API via Template::SetIntrinsicDataProperty().
var ArrayEntries = GlobalArray.prototype.entries;
var ArrayForEach = GlobalArray.prototype.forEach;
var ArrayKeys = GlobalArray.prototype.keys;
var ArrayValues = GlobalArray.prototype[iteratorSymbol];
// The internal Array prototype doesn't need to be fancy, since it's never
// exposed to user code.
// Adding only the functions that are actually used.
utils.SetUpLockedPrototype(InternalArray, GlobalArray(), [
"indexOf", ArrayIndexOf,
"join", ArrayJoin,
"pop", ArrayPop,
"push", ArrayPush,
"shift", ArrayShift,
"sort", ArraySort,
"splice", ArraySplice
]);
// V8 extras get a separate copy of InternalPackedArray. We give them the basic
// manipulation methods.
utils.SetUpLockedPrototype(extrasUtils.InternalPackedArray, GlobalArray(), [
"push", ArrayPush,
"pop", ArrayPop,
"shift", ArrayShift,
"unshift", ArrayUnshift,
"splice", ArraySplice,
"slice", ArraySlice
]);
// -------------------------------------------------------------------
// Exports
utils.Export(function(to) {
to.ArrayJoin = ArrayJoin;
to.ArrayPush = ArrayPush;
to.ArrayToString = ArrayToString;
to.ArrayValues = ArrayValues;
// TODO(pwong): Remove once TypedArray.prototype.join() is ported to Torque.
to.InnerArrayJoin = InnerArrayJoin;
// TODO(pwong): Remove once TypedArray.prototype.join() is ported to Torque.
to.InnerArrayToLocaleString = InnerArrayToLocaleString;
});
%InstallToContext([
"array_entries_iterator", ArrayEntries,
"array_for_each_iterator", ArrayForEach,
"array_keys_iterator", ArrayKeys,
"array_values_iterator", ArrayValues,
]);
});
......@@ -68,6 +68,8 @@ function SetUpLockedPrototype(
%ToFastProperties(prototype);
}
var GlobalArray = global.Array;
var InternalArray;
// -----------------------------------------------------------------------
// To be called by bootstrapper
......@@ -75,6 +77,81 @@ function SetUpLockedPrototype(
function PostNatives(utils) {
%CheckIsBootstrapping();
// -------------------------------------------------------------------
// Array
InternalArray = utils.InternalArray;
var iteratorSymbol = ImportNow("iterator_symbol");
var unscopablesSymbol = ImportNow("unscopables_symbol");
// Set up unscopable properties on the Array.prototype object.
var unscopables = {
__proto__: null,
copyWithin: true,
entries: true,
fill: true,
find: true,
findIndex: true,
includes: true,
keys: true,
};
%ToFastProperties(unscopables);
%AddNamedProperty(GlobalArray.prototype, unscopablesSymbol, unscopables,
DONT_ENUM | READ_ONLY);
var ArrayIndexOf = GlobalArray.prototype.indexOf;
var ArrayJoin = GlobalArray.prototype.join;
var ArrayPop = GlobalArray.prototype.pop;
var ArrayPush = GlobalArray.prototype.push;
var ArraySlice = GlobalArray.prototype.slice;
var ArrayShift = GlobalArray.prototype.shift;
var ArraySort = GlobalArray.prototype.sort;
var ArraySplice = GlobalArray.prototype.splice;
var ArrayUnshift = GlobalArray.prototype.unshift;
// Array prototype functions that return iterators. They are exposed to the
// public API via Template::SetIntrinsicDataProperty().
var ArrayEntries = GlobalArray.prototype.entries;
var ArrayForEach = GlobalArray.prototype.forEach;
var ArrayKeys = GlobalArray.prototype.keys;
var ArrayValues = GlobalArray.prototype[iteratorSymbol];
// The internal Array prototype doesn't need to be fancy, since it's never
// exposed to user code.
// Adding only the functions that are actually used.
SetUpLockedPrototype(InternalArray, GlobalArray(), [
"indexOf", ArrayIndexOf,
"join", ArrayJoin,
"pop", ArrayPop,
"push", ArrayPush,
"shift", ArrayShift,
"sort", ArraySort,
"splice", ArraySplice
]);
// V8 extras get a separate copy of InternalPackedArray. We give them the basic
// manipulation methods.
SetUpLockedPrototype(extrasUtils.InternalPackedArray, GlobalArray(), [
"push", ArrayPush,
"pop", ArrayPop,
"shift", ArrayShift,
"unshift", ArrayUnshift,
"splice", ArraySplice,
"slice", ArraySlice
]);
%InstallToContext([
"array_entries_iterator", ArrayEntries,
"array_for_each_iterator", ArrayForEach,
"array_keys_iterator", ArrayKeys,
"array_values_iterator", ArrayValues,
]);
// -------------------------------------------------------------------
for ( ; !IS_UNDEFINED(imports); imports = imports.next) {
imports(exports_container);
}
......@@ -94,7 +171,6 @@ function PostNatives(utils) {
utils.Import = Import;
utils.ImportNow = ImportNow;
utils.Export = Export;
utils.SetUpLockedPrototype = SetUpLockedPrototype;
utils.PostNatives = PostNatives;
%ToFastProperties(utils);
......
// 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
// array.js has to come before typedarray.js for this to work
var ArrayToString = utils.ImportNow("ArrayToString");
var InnerArrayJoin;
var InnerArrayToLocaleString;
macro TYPED_ARRAYS(FUNCTION)
FUNCTION(Uint8Array, 1)
FUNCTION(Int8Array, 1)
FUNCTION(Uint16Array, 2)
FUNCTION(Int16Array, 2)
FUNCTION(Uint32Array, 4)
FUNCTION(Int32Array, 4)
FUNCTION(Float32Array, 4)
FUNCTION(Float64Array, 8)
FUNCTION(Uint8ClampedArray, 1)
FUNCTION(BigUint64Array, 8)
FUNCTION(BigInt64Array, 8)
endmacro
macro DECLARE_GLOBALS(NAME, SIZE)
var GlobalNAME = global.NAME;
endmacro
TYPED_ARRAYS(DECLARE_GLOBALS)
macro IS_TYPEDARRAY(arg)
(%_IsTypedArray(arg))
endmacro
var GlobalTypedArray = %object_get_prototype_of(GlobalUint8Array);
utils.Import(function(from) {
InnerArrayJoin = from.InnerArrayJoin;
InnerArrayToLocaleString = from.InnerArrayToLocaleString;
});
// --------------- Typed Arrays ---------------------
// ES6 section 22.2.3.5.1 ValidateTypedArray ( O )
function ValidateTypedArray(array, methodName) {
if (!IS_TYPEDARRAY(array)) throw %make_type_error(kNotTypedArray);
if (%ArrayBufferViewWasDetached(array))
throw %make_type_error(kDetachedOperation, methodName);
}
// ES6 section 22.2.3.27
// ecma402 #sup-array.prototype.tolocalestring
DEFINE_METHOD(
GlobalTypedArray.prototype,
toLocaleString() {
ValidateTypedArray(this, "%TypedArray%.prototype.toLocaleString");
var locales = arguments[0];
var options = arguments[1];
var length = %TypedArrayGetLength(this);
return InnerArrayToLocaleString(this, length, locales, options);
}
);
// ES6 section 22.2.3.14
DEFINE_METHOD(
GlobalTypedArray.prototype,
join(separator) {
ValidateTypedArray(this, "%TypedArray%.prototype.join");
var length = %TypedArrayGetLength(this);
return InnerArrayJoin(separator, this, length);
}
);
// -------------------------------------------------------------------
%AddNamedProperty(GlobalTypedArray.prototype, "toString", ArrayToString,
DONT_ENUM);
})
......@@ -4296,9 +4296,9 @@ UNINITIALIZED_TEST(LoadedAtStartupScripts) {
CHECK_EQ(count_by_type[i::Script::TYPE_WASM], 0);
CHECK_EQ(count_by_type[i::Script::TYPE_INSPECTOR], 0);
i::Handle<i::Script> native_array_script =
FindScript(i_isolate, scripts, "native array.js").ToHandleChecked();
CHECK_EQ(native_array_script->type(), i::Script::TYPE_NATIVE);
i::Handle<i::Script> native_prologue_script =
FindScript(i_isolate, scripts, "native prologue.js").ToHandleChecked();
CHECK_EQ(native_prologue_script->type(), i::Script::TYPE_NATIVE);
i::Handle<i::Script> gc_script =
FindScript(i_isolate, scripts, "v8/gc").ToHandleChecked();
......
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