diff --git a/BUILD.gn b/BUILD.gn index 99c9f453f6..d2e47863a9 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1134,6 +1134,7 @@ action("postmortem-metadata") { torque_files = [ "src/builtins/aggregate-error.tq", + "src/builtins/array-at.tq", "src/builtins/array-copywithin.tq", "src/builtins/array-every.tq", "src/builtins/array-filter.tq", @@ -1216,6 +1217,7 @@ torque_files = [ "src/builtins/regexp-split.tq", "src/builtins/regexp-test.tq", "src/builtins/regexp.tq", + "src/builtins/string-at.tq", "src/builtins/string-endswith.tq", "src/builtins/string-html.tq", "src/builtins/string-iterator.tq", @@ -1229,6 +1231,7 @@ torque_files = [ "src/builtins/string-trim.tq", "src/builtins/symbol.tq", "src/builtins/torque-internal.tq", + "src/builtins/typed-array-at.tq", "src/builtins/typed-array-createtypedarray.tq", "src/builtins/typed-array-every.tq", "src/builtins/typed-array-entries.tq", diff --git a/src/builtins/array-at.tq b/src/builtins/array-at.tq new file mode 100644 index 0000000000..b1707745e8 --- /dev/null +++ b/src/builtins/array-at.tq @@ -0,0 +1,27 @@ +// Copyright 2020 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. + +namespace array { +// https://tc39.es/proposal-item-method/#sec-array.prototype.at +transitioning javascript builtin ArrayPrototypeAt( + js-implicit context: NativeContext, receiver: JSAny)(index: JSAny): JSAny { + // 1. Let O be ? ToObject(this value). + const o = ToObject_Inline(context, receiver); + // 2. Let len be ? LengthOfArrayLike(O). + const len = GetLengthProperty(o); + // 3. Let relativeIndex be ? ToInteger(index). + const relativeIndex = ToInteger_Inline(index); + // 4. If relativeIndex ≥ 0, then + // a. Let k be relativeIndex. + // 5. Else, + // a. Let k be len + relativeIndex. + const k = relativeIndex >= 0 ? relativeIndex : len + relativeIndex; + // 6. If k < 0 or k ≥ len, then return undefined. + if (k < 0 || k >= len) { + return Undefined; + } + // 7. Return ? Get(O, ! ToString(k)). + return GetProperty(o, k); +} +} diff --git a/src/builtins/string-at.tq b/src/builtins/string-at.tq new file mode 100644 index 0000000000..a85e4d5290 --- /dev/null +++ b/src/builtins/string-at.tq @@ -0,0 +1,29 @@ +// Copyright 2020 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. + +namespace string { +// https://tc39.es/proposal-item-method/#sec-string.prototype.at +transitioning javascript builtin StringPrototypeAt( + js-implicit context: NativeContext, receiver: JSAny)(index: JSAny): JSAny { + // 1. Let O be ? RequireObjectCoercible(this value). + // 2. Let S be ? ToString(O). + const s = ToThisString(receiver, 'String.prototype.at'); + // 3. Let len be the length of S. + const len = s.length_smi; + // 4. Let relativeIndex be ? ToInteger(index). + const relativeIndex = ToInteger_Inline(index); + // 5. If relativeIndex ≥ 0, then + // a. Let k be relativeIndex. + // 6. Else, + // a. Let k be len + relativeIndex. + const k = relativeIndex >= 0 ? relativeIndex : len + relativeIndex; + // 7. If k < 0 or k ≥ len, then return undefined. + if (k < 0 || k >= len) { + return Undefined; + } + // 8. Return the String value consisting of only the code unit at position k + // in S. + return StringFromSingleCharCode(StringCharCodeAt(s, Convert(k))); +} +} diff --git a/src/builtins/typed-array-at.tq b/src/builtins/typed-array-at.tq new file mode 100644 index 0000000000..6ec4730d94 --- /dev/null +++ b/src/builtins/typed-array-at.tq @@ -0,0 +1,28 @@ +// Copyright 2020 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. + +namespace typed_array { +// https://tc39.es/proposal-item-method/#sec-%typedarray%.prototype.at +transitioning javascript builtin TypedArrayPrototypeAt( + js-implicit context: NativeContext, receiver: JSAny)(index: JSAny): JSAny { + // 1. Let O be the this value. + // 2. Perform ? ValidateTypedArray(O). + const o = ValidateTypedArray(context, receiver, '%TypedArray%.prototype.at'); + // 3. Let len be O.[[ArrayLength]]. + const len = Convert(o.length); + // 4. Let relativeIndex be ? ToInteger(index). + const relativeIndex = ToInteger_Inline(index); + // 5. If relativeIndex ≥ 0, then + // a. Let k be relativeIndex. + // 6. Else, + // a. Let k be len + relativeIndex. + const k = relativeIndex >= 0 ? relativeIndex : len + relativeIndex; + // 7. If k < 0 or k ≥ len, then return undefined. + if (k < 0 || k >= len) { + return Undefined; + } + // 8. Return ? Get(O, ! ToString(k)). + return GetProperty(o, k); +} +} diff --git a/src/debug/debug-evaluate.cc b/src/debug/debug-evaluate.cc index bac23701d4..5ad72c8b82 100644 --- a/src/debug/debug-evaluate.cc +++ b/src/debug/debug-evaluate.cc @@ -567,6 +567,7 @@ DebugInfo::SideEffectState BuiltinGetSideEffectState(Builtins::Name id) { case Builtins::kArrayIndexOf: case Builtins::kArrayPrototypeValues: case Builtins::kArrayIncludes: + case Builtins::kArrayPrototypeAt: case Builtins::kArrayPrototypeEntries: case Builtins::kArrayPrototypeFill: case Builtins::kArrayPrototypeFind: @@ -592,6 +593,7 @@ DebugInfo::SideEffectState BuiltinGetSideEffectState(Builtins::Name id) { case Builtins::kTrace: // TypedArray builtins. case Builtins::kTypedArrayConstructor: + case Builtins::kTypedArrayPrototypeAt: case Builtins::kTypedArrayPrototypeBuffer: case Builtins::kTypedArrayPrototypeByteLength: case Builtins::kTypedArrayPrototypeByteOffset: @@ -759,6 +761,7 @@ DebugInfo::SideEffectState BuiltinGetSideEffectState(Builtins::Name id) { case Builtins::kStringFromCodePoint: case Builtins::kStringConstructor: case Builtins::kStringPrototypeAnchor: + case Builtins::kStringPrototypeAt: case Builtins::kStringPrototypeBig: case Builtins::kStringPrototypeBlink: case Builtins::kStringPrototypeBold: diff --git a/src/flags/flag-definitions.h b/src/flags/flag-definitions.h index 37f7afaaec..8b8837276d 100644 --- a/src/flags/flag-definitions.h +++ b/src/flags/flag-definitions.h @@ -253,7 +253,8 @@ DEFINE_IMPLICATION(harmony_weak_refs_with_cleanup_some, harmony_weak_refs) V(harmony_weak_refs_with_cleanup_some, \ "harmony weak references with FinalizationRegistry.prototype.cleanupSome") \ V(harmony_regexp_match_indices, "harmony regexp match indices") \ - V(harmony_import_assertions, "harmony import assertions") + V(harmony_import_assertions, "harmony import assertions") \ + V(harmony_relative_indexing_methods, "harmony relative indexing methods") #ifdef V8_INTL_SUPPORT #define HARMONY_INPROGRESS(V) \ diff --git a/src/init/bootstrapper.cc b/src/init/bootstrapper.cc index 2446ff969a..c076b91756 100644 --- a/src/init/bootstrapper.cc +++ b/src/init/bootstrapper.cc @@ -4391,6 +4391,40 @@ void Genesis::InitializeGlobal_regexp_linear_flag() { native_context()->set_regexp_prototype_map(regexp_prototype->map()); } +void Genesis::InitializeGlobal_harmony_relative_indexing_methods() { + if (!FLAG_harmony_relative_indexing_methods) return; + + { + Handle array_function(native_context()->array_function(), + isolate()); + Handle array_prototype( + JSObject::cast(array_function->instance_prototype()), isolate()); + + SimpleInstallFunction(isolate(), array_prototype, "at", + Builtins::kArrayPrototypeAt, 1, true); + } + + { + Handle string_function(native_context()->string_function(), + isolate()); + Handle string_prototype( + JSObject::cast(string_function->instance_prototype()), isolate()); + + SimpleInstallFunction(isolate(), string_prototype, "at", + Builtins::kStringPrototypeAt, 1, true); + } + + { + Handle typed_array_function( + native_context()->typed_array_function(), isolate()); + Handle typed_array_prototype( + JSObject::cast(typed_array_function->instance_prototype()), isolate()); + + SimpleInstallFunction(isolate(), typed_array_prototype, "at", + Builtins::kTypedArrayPrototypeAt, 1, true); + } +} + #ifdef V8_INTL_SUPPORT void Genesis::InitializeGlobal_harmony_intl_segmenter() { diff --git a/test/mjsunit/harmony/relative-indexing-methods.js b/test/mjsunit/harmony/relative-indexing-methods.js new file mode 100644 index 0000000000..523e10c398 --- /dev/null +++ b/test/mjsunit/harmony/relative-indexing-methods.js @@ -0,0 +1,47 @@ +// Copyright 2020 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. + +// Flags: --harmony-relative-indexing-methods --allow-natives-syntax + +'use strict'; + +[ + [1, 2, 3], + '123', + new Uint8Array([1, 2, 3]), +].forEach((v) => { + assertEquals(v.at(0), v[0]); + assertEquals(v.at(-1), v[2]); + assertEquals(v.at(-4), undefined); + assertEquals(v.at(3), undefined); + assertEquals(v.at(1337), undefined); + assertEquals(v.at(-Infinity), undefined); + assertEquals(v.at(Infinity), undefined); + assertEquals(v.at(NaN), v[0]); + assertEquals(v.at(undefined), v[0]) + assertEquals(v.at(), v[0]); +}); + +{ + const props = ['length', '2']; + const proxy = new Proxy([1, 2, 3], { + get(t, p, r) { + assertEquals(p, props.shift()); + return Reflect.get(t, p, r); + } + }); + assertEquals(Array.prototype.at.call(proxy, -1), 3); +} + +assertThrows(() => { + Uint8Array.prototype.at.call({}); +}, TypeError); + +{ + const a = new Uint8Array([1, 2, 3]); + %ArrayBufferDetach(a.buffer); + assertThrows(() => { + a.at(0); + }, TypeError); +} diff --git a/tools/v8heapconst.py b/tools/v8heapconst.py index af24db8cc7..f7fcf86325 100644 --- a/tools/v8heapconst.py +++ b/tools/v8heapconst.py @@ -449,27 +449,27 @@ KNOWN_OBJECTS = { ("old_space", 0x02a71): "StringSplitCache", ("old_space", 0x02e79): "RegExpMultipleCache", ("old_space", 0x03281): "BuiltinsConstantsTable", - ("old_space", 0x03661): "AsyncFunctionAwaitRejectSharedFun", - ("old_space", 0x03689): "AsyncFunctionAwaitResolveSharedFun", - ("old_space", 0x036b1): "AsyncGeneratorAwaitRejectSharedFun", - ("old_space", 0x036d9): "AsyncGeneratorAwaitResolveSharedFun", - ("old_space", 0x03701): "AsyncGeneratorYieldResolveSharedFun", - ("old_space", 0x03729): "AsyncGeneratorReturnResolveSharedFun", - ("old_space", 0x03751): "AsyncGeneratorReturnClosedRejectSharedFun", - ("old_space", 0x03779): "AsyncGeneratorReturnClosedResolveSharedFun", - ("old_space", 0x037a1): "AsyncIteratorValueUnwrapSharedFun", - ("old_space", 0x037c9): "PromiseAllResolveElementSharedFun", - ("old_space", 0x037f1): "PromiseAllSettledResolveElementSharedFun", - ("old_space", 0x03819): "PromiseAllSettledRejectElementSharedFun", - ("old_space", 0x03841): "PromiseAnyRejectElementSharedFun", - ("old_space", 0x03869): "PromiseCapabilityDefaultRejectSharedFun", - ("old_space", 0x03891): "PromiseCapabilityDefaultResolveSharedFun", - ("old_space", 0x038b9): "PromiseCatchFinallySharedFun", - ("old_space", 0x038e1): "PromiseGetCapabilitiesExecutorSharedFun", - ("old_space", 0x03909): "PromiseThenFinallySharedFun", - ("old_space", 0x03931): "PromiseThrowerFinallySharedFun", - ("old_space", 0x03959): "PromiseValueThunkFinallySharedFun", - ("old_space", 0x03981): "ProxyRevokeSharedFun", + ("old_space", 0x03669): "AsyncFunctionAwaitRejectSharedFun", + ("old_space", 0x03691): "AsyncFunctionAwaitResolveSharedFun", + ("old_space", 0x036b9): "AsyncGeneratorAwaitRejectSharedFun", + ("old_space", 0x036e1): "AsyncGeneratorAwaitResolveSharedFun", + ("old_space", 0x03709): "AsyncGeneratorYieldResolveSharedFun", + ("old_space", 0x03731): "AsyncGeneratorReturnResolveSharedFun", + ("old_space", 0x03759): "AsyncGeneratorReturnClosedRejectSharedFun", + ("old_space", 0x03781): "AsyncGeneratorReturnClosedResolveSharedFun", + ("old_space", 0x037a9): "AsyncIteratorValueUnwrapSharedFun", + ("old_space", 0x037d1): "PromiseAllResolveElementSharedFun", + ("old_space", 0x037f9): "PromiseAllSettledResolveElementSharedFun", + ("old_space", 0x03821): "PromiseAllSettledRejectElementSharedFun", + ("old_space", 0x03849): "PromiseAnyRejectElementSharedFun", + ("old_space", 0x03871): "PromiseCapabilityDefaultRejectSharedFun", + ("old_space", 0x03899): "PromiseCapabilityDefaultResolveSharedFun", + ("old_space", 0x038c1): "PromiseCatchFinallySharedFun", + ("old_space", 0x038e9): "PromiseGetCapabilitiesExecutorSharedFun", + ("old_space", 0x03911): "PromiseThenFinallySharedFun", + ("old_space", 0x03939): "PromiseThrowerFinallySharedFun", + ("old_space", 0x03961): "PromiseValueThunkFinallySharedFun", + ("old_space", 0x03989): "ProxyRevokeSharedFun", } # Lower 32 bits of first page addresses for various heap spaces.