[torque] format namespaces without indentation

Bug: v8:7793
Change-Id: Id2a93f8ac8c512dbc5cdeb43a97e04d8d6684954
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2196130
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67748}
This commit is contained in:
Tobias Tebbi 2020-05-12 15:28:29 +02:00 committed by Commit Bot
parent 8b2a322110
commit 45557b1f89
106 changed files with 15484 additions and 15645 deletions

View File

@ -3,92 +3,91 @@
// found in the LICENSE file.
namespace array {
macro ConvertToRelativeIndex(index: Number, length: Number): Number {
return index < 0 ? Max(index + length, 0) : Min(index, length);
}
// https://tc39.github.io/ecma262/#sec-array.prototype.copyWithin
transitioning javascript builtin ArrayPrototypeCopyWithin(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSAny {
// 1. Let O be ? ToObject(this value).
const object: JSReceiver = ToObject_Inline(context, receiver);
// 2. Let len be ? ToLength(? Get(O, "length")).
const length: Number = GetLengthProperty(object);
// 3. Let relativeTarget be ? ToInteger(target).
const relativeTarget: Number = ToInteger_Inline(arguments[0]);
// 4. If relativeTarget < 0, let to be max((len + relativeTarget), 0);
// else let to be min(relativeTarget, len).
let to: Number = ConvertToRelativeIndex(relativeTarget, length);
// 5. Let relativeStart be ? ToInteger(start).
const relativeStart: Number = ToInteger_Inline(arguments[1]);
// 6. If relativeStart < 0, let from be max((len + relativeStart), 0);
// else let from be min(relativeStart, len).
let from: Number = ConvertToRelativeIndex(relativeStart, length);
// 7. If end is undefined, let relativeEnd be len;
// else let relativeEnd be ? ToInteger(end).
let relativeEnd: Number = length;
if (arguments[2] != Undefined) {
relativeEnd = ToInteger_Inline(arguments[2]);
}
// 8. If relativeEnd < 0, let final be max((len + relativeEnd), 0);
// else let final be min(relativeEnd, len).
const final: Number = ConvertToRelativeIndex(relativeEnd, length);
// 9. Let count be min(final-from, len-to).
let count: Number = Min(final - from, length - to);
// 10. If from<to and to<from+count, then.
let direction: Number = 1;
if (from < to && to < (from + count)) {
// a. Let direction be -1.
direction = -1;
// b. Let from be from + count - 1.
from = from + count - 1;
// c. Let to be to + count - 1.
to = to + count - 1;
}
// 12. Repeat, while count > 0.
while (count > 0) {
// a. Let fromKey be ! ToString(from).
// b. Let toKey be ! ToString(to).
// c. Let fromPresent be ? HasProperty(O, fromKey).
const fromPresent: Boolean = HasProperty(object, from);
// d. If fromPresent is true, then.
if (fromPresent == True) {
// i. Let fromVal be ? Get(O, fromKey).
const fromVal: JSAny = GetProperty(object, from);
// ii. Perform ? Set(O, toKey, fromVal, true).
SetProperty(object, to, fromVal);
} else {
// i. Perform ? DeletePropertyOrThrow(O, toKey).
DeleteProperty(object, to, LanguageMode::kStrict);
}
// f. Let from be from + direction.
from = from + direction;
// g. Let to be to + direction.
to = to + direction;
// h. Let count be count - 1.
--count;
}
// 13. Return O.
return object;
}
macro ConvertToRelativeIndex(index: Number, length: Number): Number {
return index < 0 ? Max(index + length, 0) : Min(index, length);
}
// https://tc39.github.io/ecma262/#sec-array.prototype.copyWithin
transitioning javascript builtin ArrayPrototypeCopyWithin(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// 1. Let O be ? ToObject(this value).
const object: JSReceiver = ToObject_Inline(context, receiver);
// 2. Let len be ? ToLength(? Get(O, "length")).
const length: Number = GetLengthProperty(object);
// 3. Let relativeTarget be ? ToInteger(target).
const relativeTarget: Number = ToInteger_Inline(arguments[0]);
// 4. If relativeTarget < 0, let to be max((len + relativeTarget), 0);
// else let to be min(relativeTarget, len).
let to: Number = ConvertToRelativeIndex(relativeTarget, length);
// 5. Let relativeStart be ? ToInteger(start).
const relativeStart: Number = ToInteger_Inline(arguments[1]);
// 6. If relativeStart < 0, let from be max((len + relativeStart), 0);
// else let from be min(relativeStart, len).
let from: Number = ConvertToRelativeIndex(relativeStart, length);
// 7. If end is undefined, let relativeEnd be len;
// else let relativeEnd be ? ToInteger(end).
let relativeEnd: Number = length;
if (arguments[2] != Undefined) {
relativeEnd = ToInteger_Inline(arguments[2]);
}
// 8. If relativeEnd < 0, let final be max((len + relativeEnd), 0);
// else let final be min(relativeEnd, len).
const final: Number = ConvertToRelativeIndex(relativeEnd, length);
// 9. Let count be min(final-from, len-to).
let count: Number = Min(final - from, length - to);
// 10. If from<to and to<from+count, then.
let direction: Number = 1;
if (from < to && to < (from + count)) {
// a. Let direction be -1.
direction = -1;
// b. Let from be from + count - 1.
from = from + count - 1;
// c. Let to be to + count - 1.
to = to + count - 1;
}
// 12. Repeat, while count > 0.
while (count > 0) {
// a. Let fromKey be ! ToString(from).
// b. Let toKey be ! ToString(to).
// c. Let fromPresent be ? HasProperty(O, fromKey).
const fromPresent: Boolean = HasProperty(object, from);
// d. If fromPresent is true, then.
if (fromPresent == True) {
// i. Let fromVal be ? Get(O, fromKey).
const fromVal: JSAny = GetProperty(object, from);
// ii. Perform ? Set(O, toKey, fromVal, true).
SetProperty(object, to, fromVal);
} else {
// i. Perform ? DeletePropertyOrThrow(O, toKey).
DeleteProperty(object, to, LanguageMode::kStrict);
}
// f. Let from be from + direction.
from = from + direction;
// g. Let to be to + direction.
to = to + direction;
// h. Let count be count - 1.
--count;
}
// 13. Return O.
return object;
}
}

View File

@ -3,143 +3,142 @@
// found in the LICENSE file.
namespace array {
transitioning javascript builtin
ArrayEveryLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny {
// All continuation points in the optimized every implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
transitioning javascript builtin
ArrayEveryLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny {
// All continuation points in the optimized every implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
return ArrayEveryLoopContinuation(
jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK,
numberLength, Undefined);
return ArrayEveryLoopContinuation(
jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK,
numberLength, Undefined);
}
transitioning javascript builtin
ArrayEveryLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny,
result: JSAny): JSAny {
// All continuation points in the optimized every implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
let numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
// This custom lazy deopt point is right after the callback. every() needs
// to pick up at the next step, which is either continuing to the next
// array element or returning false if {result} is false.
if (!ToBoolean(result)) {
return False;
}
transitioning javascript builtin
ArrayEveryLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny,
result: JSAny): JSAny {
// All continuation points in the optimized every implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
let numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
numberK = numberK + 1;
// This custom lazy deopt point is right after the callback. every() needs
// to pick up at the next step, which is either continuing to the next
// array element or returning false if {result} is false.
if (!ToBoolean(result)) {
return False;
}
return ArrayEveryLoopContinuation(
jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK,
numberLength, Undefined);
}
numberK = numberK + 1;
transitioning builtin ArrayEveryLoopContinuation(implicit context: Context)(
_receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, _array: JSAny,
o: JSReceiver, initialK: Number, length: Number, _initialTo: JSAny): JSAny {
// 5. Let k be 0.
// 6. Repeat, while k < len
for (let k: Number = initialK; k < length; k++) {
// 6a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
return ArrayEveryLoopContinuation(
jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK,
numberLength, Undefined);
}
// 6b. Let kPresent be ? HasProperty(O, Pk).
const kPresent: Boolean = HasProperty_Inline(o, k);
transitioning builtin ArrayEveryLoopContinuation(implicit context: Context)(
_receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny,
_array: JSAny, o: JSReceiver, initialK: Number, length: Number,
_initialTo: JSAny): JSAny {
// 5. Let k be 0.
// 6. Repeat, while k < len
for (let k: Number = initialK; k < length; k++) {
// 6a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
// 6c. If kPresent is true, then
if (kPresent == True) {
// 6c. i. Let kValue be ? Get(O, Pk).
const kValue: JSAny = GetProperty(o, k);
// 6b. Let kPresent be ? HasProperty(O, Pk).
const kPresent: Boolean = HasProperty_Inline(o, k);
// 6c. ii. Perform ? Call(callbackfn, T, <kValue, k, O>).
const result: JSAny = Call(context, callbackfn, thisArg, kValue, k, o);
// 6c. If kPresent is true, then
if (kPresent == True) {
// 6c. i. Let kValue be ? Get(O, Pk).
const kValue: JSAny = GetProperty(o, k);
// 6c. ii. Perform ? Call(callbackfn, T, <kValue, k, O>).
const result: JSAny = Call(context, callbackfn, thisArg, kValue, k, o);
// iii. If selected is true, then...
if (!ToBoolean(result)) {
return False;
}
}
// 6d. Increase k by 1. (done by the loop).
}
return True;
}
transitioning macro FastArrayEvery(implicit context: Context)(
o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny
labels Bailout(Smi) {
let k: Smi = 0;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(k);
const fastO: FastJSArray = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = NewFastJSArrayWitness(fastO);
// Build a fast loop over the smi array.
for (; k < smiLen; k++) {
fastOW.Recheck() otherwise goto Bailout(k);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k);
const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue;
const result: JSAny =
Call(context, callbackfn, thisArg, value, k, fastOW.Get());
// iii. If selected is true, then...
if (!ToBoolean(result)) {
return False;
}
}
return True;
// 6d. Increase k by 1. (done by the loop).
}
return True;
}
// https://tc39.github.io/ecma262/#sec-array.prototype.every
transitioning javascript builtin
ArrayEvery(js-implicit context: NativeContext, receiver: JSAny)(...arguments):
JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.every');
transitioning macro FastArrayEvery(implicit context: Context)(
o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny
labels Bailout(Smi) {
let k: Smi = 0;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(k);
const fastO: FastJSArray = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = NewFastJSArrayWitness(fastO);
// 1. Let O be ? ToObject(this value).
const o: JSReceiver = ToObject_Inline(context, receiver);
// Build a fast loop over the smi array.
for (; k < smiLen; k++) {
fastOW.Recheck() otherwise goto Bailout(k);
// 2. Let len be ? ToLength(? Get(O, "length")).
const len: Number = GetLengthProperty(o);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto TypeError;
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise TypeError;
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: JSAny = arguments[1];
// Special cases.
try {
return FastArrayEvery(o, len, callbackfn, thisArg)
otherwise Bailout;
} label Bailout(kValue: Smi) deferred {
return ArrayEveryLoopContinuation(
o, callbackfn, thisArg, Undefined, o, kValue, len, Undefined);
}
} label TypeError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k);
const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue;
const result: JSAny =
Call(context, callbackfn, thisArg, value, k, fastOW.Get());
if (!ToBoolean(result)) {
return False;
}
}
return True;
}
// https://tc39.github.io/ecma262/#sec-array.prototype.every
transitioning javascript builtin
ArrayEvery(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.every');
// 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);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto TypeError;
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise TypeError;
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: JSAny = arguments[1];
// Special cases.
try {
return FastArrayEvery(o, len, callbackfn, thisArg)
otherwise Bailout;
} label Bailout(kValue: Smi) deferred {
return ArrayEveryLoopContinuation(
o, callbackfn, thisArg, Undefined, o, kValue, len, Undefined);
}
} label TypeError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
}
}
}

View File

@ -3,196 +3,197 @@
// found in the LICENSE file.
namespace array {
transitioning javascript builtin
ArrayFilterLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny,
length: JSAny, initialTo: JSAny): JSAny {
// All continuation points in the optimized filter implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const outputArray = Cast<JSReceiver>(array) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberTo = Cast<Number>(initialTo) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
transitioning javascript builtin
ArrayFilterLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny,
length: JSAny, initialTo: JSAny): JSAny {
// All continuation points in the optimized filter implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const outputArray = Cast<JSReceiver>(array) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberTo = Cast<Number>(initialTo) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
return ArrayFilterLoopContinuation(
jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK,
numberLength, numberTo);
return ArrayFilterLoopContinuation(
jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK,
numberLength, numberTo);
}
transitioning javascript builtin
ArrayFilterLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny,
length: JSAny, valueK: JSAny, initialTo: JSAny, result: JSAny): JSAny {
// All continuation points in the optimized filter implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const outputArray = Cast<JSReceiver>(array) otherwise unreachable;
let numberK = Cast<Number>(initialK) otherwise unreachable;
let numberTo = Cast<Number>(initialTo) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
// This custom lazy deopt point is right after the callback. filter() needs
// to pick up at the next step, which is setting the callback
// result in the output array. After incrementing k and to, we can glide
// into the loop continuation builtin.
if (ToBoolean(result)) {
FastCreateDataProperty(outputArray, numberTo, valueK);
numberTo = numberTo + 1;
}
transitioning javascript builtin
ArrayFilterLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny,
length: JSAny, valueK: JSAny, initialTo: JSAny, result: JSAny): JSAny {
// All continuation points in the optimized filter implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const outputArray = Cast<JSReceiver>(array) otherwise unreachable;
let numberK = Cast<Number>(initialK) otherwise unreachable;
let numberTo = Cast<Number>(initialTo) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
numberK = numberK + 1;
// This custom lazy deopt point is right after the callback. filter() needs
// to pick up at the next step, which is setting the callback
// result in the output array. After incrementing k and to, we can glide
// into the loop continuation builtin.
if (ToBoolean(result)) {
FastCreateDataProperty(outputArray, numberTo, valueK);
numberTo = numberTo + 1;
}
return ArrayFilterLoopContinuation(
jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK,
numberLength, numberTo);
}
numberK = numberK + 1;
transitioning builtin ArrayFilterLoopContinuation(implicit context: Context)(
_receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny,
array: JSReceiver, o: JSReceiver, initialK: Number, length: Number,
initialTo: Number): JSAny {
let to: Number = initialTo;
// 5. Let k be 0.
// 6. Repeat, while k < len
for (let k: Number = initialK; k < length; k++) {
// 6a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
return ArrayFilterLoopContinuation(
jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK,
numberLength, numberTo);
}
// 6b. Let kPresent be ? HasProperty(O, Pk).
const kPresent: Boolean = HasProperty_Inline(o, k);
transitioning builtin ArrayFilterLoopContinuation(implicit context: Context)(
_receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny,
array: JSReceiver, o: JSReceiver, initialK: Number, length: Number,
initialTo: Number): JSAny {
let to: Number = initialTo;
// 5. Let k be 0.
// 6. Repeat, while k < len
for (let k: Number = initialK; k < length; k++) {
// 6a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
// 6c. If kPresent is true, then
if (kPresent == True) {
// 6c. i. Let kValue be ? Get(O, Pk).
const kValue: JSAny = GetProperty(o, k);
// 6b. Let kPresent be ? HasProperty(O, Pk).
const kPresent: Boolean = HasProperty_Inline(o, k);
// 6c. ii. Perform ? Call(callbackfn, T, <kValue, k, O>).
const result: JSAny = Call(context, callbackfn, thisArg, kValue, k, o);
// 6c. If kPresent is true, then
if (kPresent == True) {
// 6c. i. Let kValue be ? Get(O, Pk).
const kValue: JSAny = GetProperty(o, k);
// 6c. ii. Perform ? Call(callbackfn, T, <kValue, k, O>).
const result: JSAny = Call(context, callbackfn, thisArg, kValue, k, o);
// iii. If selected is true, then...
if (ToBoolean(result)) {
// 1. Perform ? CreateDataPropertyOrThrow(A, ToString(to), kValue).
FastCreateDataProperty(array, to, kValue);
// 2. Increase to by 1.
to = to + 1;
}
}
// 6d. Increase k by 1. (done by the loop).
}
return array;
}
transitioning macro FastArrayFilter(implicit context: Context)(
fastO: FastJSArray, len: Smi, callbackfn: Callable, thisArg: JSAny,
output: FastJSArray) labels Bailout(Number, Number) {
let k: Smi = 0;
let to: Smi = 0;
let fastOW = NewFastJSArrayWitness(fastO);
let fastOutputW = NewFastJSArrayWitness(output);
fastOutputW.EnsureArrayPushable() otherwise goto Bailout(k, to);
// Build a fast loop over the array.
for (; k < len; k++) {
fastOW.Recheck() otherwise goto Bailout(k, to);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k, to);
const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue;
const result: JSAny =
Call(context, callbackfn, thisArg, value, k, fastOW.Get());
// iii. If selected is true, then...
if (ToBoolean(result)) {
try {
// Since the call to {callbackfn} is observable, we can't
// use the Bailout label until we've successfully stored.
// Hence the {SlowStore} label.
fastOutputW.Recheck() otherwise SlowStore;
if (fastOutputW.Get().length != to) goto SlowStore;
fastOutputW.Push(value) otherwise SlowStore;
} label SlowStore {
FastCreateDataProperty(fastOutputW.stable, to, value);
}
// 1. Perform ? CreateDataPropertyOrThrow(A, ToString(to), kValue).
FastCreateDataProperty(array, to, kValue);
// 2. Increase to by 1.
to = to + 1;
}
}
// 6d. Increase k by 1. (done by the loop).
}
return array;
}
// This method creates a 0-length array with the ElementsKind of the
// receiver if possible, otherwise, bails out. It makes sense for the
// caller to know that the slow case needs to be invoked.
macro FastFilterSpeciesCreate(implicit context: Context)(
receiver: JSReceiver): JSReceiver labels Slow {
const len: Smi = 0;
if (IsArraySpeciesProtectorCellInvalid()) goto Slow;
const o = Cast<FastJSArray>(receiver) otherwise Slow;
const newMap: Map =
LoadJSArrayElementsMap(o.map.elements_kind, LoadNativeContext(context));
return AllocateJSArray(ElementsKind::PACKED_SMI_ELEMENTS, newMap, len, len);
}
transitioning macro FastArrayFilter(implicit context: Context)(
fastO: FastJSArray, len: Smi, callbackfn: Callable, thisArg: JSAny,
output: FastJSArray) labels
Bailout(Number, Number) {
let k: Smi = 0;
let to: Smi = 0;
let fastOW = NewFastJSArrayWitness(fastO);
let fastOutputW = NewFastJSArrayWitness(output);
// https://tc39.github.io/ecma262/#sec-array.prototype.filter
transitioning javascript builtin
ArrayFilter(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.filter');
fastOutputW.EnsureArrayPushable() otherwise goto Bailout(k, to);
// 1. Let O be ? ToObject(this value).
const o: JSReceiver = ToObject_Inline(context, receiver);
// Build a fast loop over the array.
for (; k < len; k++) {
fastOW.Recheck() otherwise goto Bailout(k, to);
// 2. Let len be ? ToLength(? Get(O, "length")).
const len: Number = GetLengthProperty(o);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto TypeError;
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise TypeError;
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: JSAny = arguments[1];
let output: JSReceiver;
// Special cases.
let k: Number = 0;
let to: Number = 0;
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k, to);
const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue;
const result: JSAny =
Call(context, callbackfn, thisArg, value, k, fastOW.Get());
if (ToBoolean(result)) {
try {
output = FastFilterSpeciesCreate(o) otherwise SlowSpeciesCreate;
try {
const smiLen: Smi = Cast<Smi>(len) otherwise goto Bailout(k, to);
const fastOutput =
Cast<FastJSArray>(output) otherwise goto Bailout(k, to);
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k, to);
FastArrayFilter(fastO, smiLen, callbackfn, thisArg, fastOutput)
otherwise Bailout;
return output;
} label Bailout(kValue: Number, toValue: Number) deferred {
k = kValue;
to = toValue;
}
} label SlowSpeciesCreate {
output = ArraySpeciesCreate(context, receiver, 0);
// Since the call to {callbackfn} is observable, we can't
// use the Bailout label until we've successfully stored.
// Hence the {SlowStore} label.
fastOutputW.Recheck() otherwise SlowStore;
if (fastOutputW.Get().length != to) goto SlowStore;
fastOutputW.Push(value) otherwise SlowStore;
} label SlowStore {
FastCreateDataProperty(fastOutputW.stable, to, value);
}
return ArrayFilterLoopContinuation(
o, callbackfn, thisArg, output, o, k, len, to);
} label TypeError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
to = to + 1;
}
}
}
// This method creates a 0-length array with the ElementsKind of the
// receiver if possible, otherwise, bails out. It makes sense for the
// caller to know that the slow case needs to be invoked.
macro FastFilterSpeciesCreate(implicit context: Context)(receiver: JSReceiver):
JSReceiver labels Slow {
const len: Smi = 0;
if (IsArraySpeciesProtectorCellInvalid()) goto Slow;
const o = Cast<FastJSArray>(receiver) otherwise Slow;
const newMap: Map =
LoadJSArrayElementsMap(o.map.elements_kind, LoadNativeContext(context));
return AllocateJSArray(ElementsKind::PACKED_SMI_ELEMENTS, newMap, len, len);
}
// https://tc39.github.io/ecma262/#sec-array.prototype.filter
transitioning javascript builtin
ArrayFilter(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.filter');
// 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);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto TypeError;
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise TypeError;
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: JSAny = arguments[1];
let output: JSReceiver;
// Special cases.
let k: Number = 0;
let to: Number = 0;
try {
output = FastFilterSpeciesCreate(o) otherwise SlowSpeciesCreate;
try {
const smiLen: Smi = Cast<Smi>(len) otherwise goto Bailout(k, to);
const fastOutput =
Cast<FastJSArray>(output) otherwise goto Bailout(k, to);
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k, to);
FastArrayFilter(fastO, smiLen, callbackfn, thisArg, fastOutput)
otherwise Bailout;
return output;
} label Bailout(kValue: Number, toValue: Number) deferred {
k = kValue;
to = toValue;
}
} label SlowSpeciesCreate {
output = ArraySpeciesCreate(context, receiver, 0);
}
return ArrayFilterLoopContinuation(
o, callbackfn, thisArg, output, o, k, len, to);
} label TypeError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
}
}
}

View File

@ -3,150 +3,149 @@
// found in the LICENSE file.
namespace array {
transitioning javascript builtin
ArrayFindLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny {
// All continuation points in the optimized find implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
transitioning javascript builtin
ArrayFindLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny {
// All continuation points in the optimized find implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
return ArrayFindLoopContinuation(
jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength);
return ArrayFindLoopContinuation(
jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength);
}
transitioning javascript builtin
ArrayFindLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
_callback: JSAny, _thisArg: JSAny, _initialK: JSAny, _length: JSAny,
_result: JSAny): JSAny {
// This deopt continuation point is never actually called, it just
// exists to make stack traces correct from a ThrowTypeError if the
// callback was found to be non-callable.
unreachable;
}
// Continuation that is called after a lazy deoptimization from TF that
// happens right after the callback and it's returned value must be handled
// before iteration continues.
transitioning javascript builtin
ArrayFindLoopAfterCallbackLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny,
foundValue: JSAny, isFound: JSAny): JSAny {
// All continuation points in the optimized find implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
// This custom lazy deopt point is right after the callback. find() needs
// to pick up at the next step, which is returning the element if the
// callback value is truthy. Otherwise, continue the search by calling the
// continuation.
if (ToBoolean(isFound)) {
return foundValue;
}
transitioning javascript builtin
ArrayFindLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
_callback: JSAny, _thisArg: JSAny, _initialK: JSAny, _length: JSAny,
_result: JSAny): JSAny {
// This deopt continuation point is never actually called, it just
// exists to make stack traces correct from a ThrowTypeError if the
// callback was found to be non-callable.
unreachable;
}
return ArrayFindLoopContinuation(
jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength);
}
// Continuation that is called after a lazy deoptimization from TF that
// happens right after the callback and it's returned value must be handled
// before iteration continues.
transitioning javascript builtin
ArrayFindLoopAfterCallbackLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny,
foundValue: JSAny, isFound: JSAny): JSAny {
// All continuation points in the optimized find implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
transitioning builtin ArrayFindLoopContinuation(implicit context: Context)(
_receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, o: JSReceiver,
initialK: Number, length: Number): JSAny {
// 5. Let k be 0.
// 6. Repeat, while k < len
for (let k: Number = initialK; k < length; k++) {
// 6a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
// This custom lazy deopt point is right after the callback. find() needs
// to pick up at the next step, which is returning the element if the
// callback value is truthy. Otherwise, continue the search by calling the
// continuation.
// 6b. i. Let kValue be ? Get(O, Pk).
const value: JSAny = GetProperty(o, k);
if (ToBoolean(isFound)) {
return foundValue;
// 6c. Let testResult be ToBoolean(? Call(predicate, T, <<kValue, k,
// O>>)).
const testResult: JSAny = Call(context, callbackfn, thisArg, value, k, o);
// 6d. If testResult is true, return kValue.
if (ToBoolean(testResult)) {
return value;
}
return ArrayFindLoopContinuation(
jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength);
// 6e. Increase k by 1. (done by the loop).
}
return Undefined;
}
transitioning builtin ArrayFindLoopContinuation(implicit context: Context)(
_receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny,
o: JSReceiver, initialK: Number, length: Number): JSAny {
// 5. Let k be 0.
// 6. Repeat, while k < len
for (let k: Number = initialK; k < length; k++) {
// 6a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
transitioning macro FastArrayFind(implicit context: Context)(
o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny
labels Bailout(Smi) {
let k: Smi = 0;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(k);
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = NewFastJSArrayWitness(fastO);
// 6b. i. Let kValue be ? Get(O, Pk).
const value: JSAny = GetProperty(o, k);
// Build a fast loop over the smi array.
for (; k < smiLen; k++) {
fastOW.Recheck() otherwise goto Bailout(k);
// 6c. Let testResult be ToBoolean(? Call(predicate, T, <<kValue, k,
// O>>)).
const testResult: JSAny = Call(context, callbackfn, thisArg, value, k, o);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k);
// 6d. If testResult is true, return kValue.
if (ToBoolean(testResult)) {
return value;
}
// 6e. Increase k by 1. (done by the loop).
const value: JSAny = fastOW.LoadElementOrUndefined(k);
const testResult: JSAny =
Call(context, callbackfn, thisArg, value, k, fastOW.Get());
if (ToBoolean(testResult)) {
return value;
}
return Undefined;
}
return Undefined;
}
transitioning macro FastArrayFind(implicit context: Context)(
o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny
labels Bailout(Smi) {
let k: Smi = 0;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(k);
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = NewFastJSArrayWitness(fastO);
// https://tc39.github.io/ecma262/#sec-array.prototype.find
transitioning javascript builtin
ArrayPrototypeFind(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.find');
// Build a fast loop over the smi array.
for (; k < smiLen; k++) {
fastOW.Recheck() otherwise goto Bailout(k);
// 1. Let O be ? ToObject(this value).
const o: JSReceiver = ToObject_Inline(context, receiver);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k);
// 2. Let len be ? ToLength(? Get(O, "length")).
const len: Number = GetLengthProperty(o);
const value: JSAny = fastOW.LoadElementOrUndefined(k);
const testResult: JSAny =
Call(context, callbackfn, thisArg, value, k, fastOW.Get());
if (ToBoolean(testResult)) {
return value;
}
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto NotCallableError;
}
return Undefined;
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallableError;
// https://tc39.github.io/ecma262/#sec-array.prototype.find
transitioning javascript builtin
ArrayPrototypeFind(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): JSAny {
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: JSAny = arguments[1];
// Special cases.
try {
RequireObjectCoercible(receiver, 'Array.prototype.find');
// 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);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto NotCallableError;
}
const callbackfn =
Cast<Callable>(arguments[0]) otherwise NotCallableError;
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: JSAny = arguments[1];
// Special cases.
try {
return FastArrayFind(o, len, callbackfn, thisArg)
otherwise Bailout;
} label Bailout(k: Smi) deferred {
return ArrayFindLoopContinuation(o, callbackfn, thisArg, o, k, len);
}
} label NotCallableError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
return FastArrayFind(o, len, callbackfn, thisArg)
otherwise Bailout;
} label Bailout(k: Smi) deferred {
return ArrayFindLoopContinuation(o, callbackfn, thisArg, o, k, len);
}
} label NotCallableError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
}
}
}

View File

@ -3,152 +3,149 @@
// found in the LICENSE file.
namespace array {
transitioning javascript builtin
ArrayFindIndexLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny {
// All continuation points in the optimized findIndex implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
transitioning javascript builtin
ArrayFindIndexLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny {
// All continuation points in the optimized findIndex implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
return ArrayFindIndexLoopContinuation(
jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength);
return ArrayFindIndexLoopContinuation(
jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength);
}
transitioning javascript builtin
ArrayFindIndexLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
_callback: JSAny, _thisArg: JSAny, _initialK: JSAny, _length: JSAny,
_result: JSAny): JSAny {
// This deopt continuation point is never actually called, it just
// exists to make stack traces correct from a ThrowTypeError if the
// callback was found to be non-callable.
unreachable;
}
// Continuation that is called after a lazy deoptimization from TF that
// happens right after the callback and it's returned value must be handled
// before iteration continues.
transitioning javascript builtin
ArrayFindIndexLoopAfterCallbackLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny,
foundValue: JSAny, isFound: JSAny): JSAny {
// All continuation points in the optimized findIndex implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
// This custom lazy deopt point is right after the callback. find() needs
// to pick up at the next step, which is returning the element if the
// callback value is truthy. Otherwise, continue the search by calling the
// continuation.
if (ToBoolean(isFound)) {
return foundValue;
}
transitioning javascript builtin
ArrayFindIndexLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
_callback: JSAny, _thisArg: JSAny, _initialK: JSAny, _length: JSAny,
_result: JSAny): JSAny {
// This deopt continuation point is never actually called, it just
// exists to make stack traces correct from a ThrowTypeError if the
// callback was found to be non-callable.
unreachable;
}
return ArrayFindIndexLoopContinuation(
jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength);
}
// Continuation that is called after a lazy deoptimization from TF that
// happens right after the callback and it's returned value must be handled
// before iteration continues.
transitioning javascript builtin
ArrayFindIndexLoopAfterCallbackLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny,
foundValue: JSAny, isFound: JSAny): JSAny {
// All continuation points in the optimized findIndex implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
transitioning builtin ArrayFindIndexLoopContinuation(implicit context: Context)(
_receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, o: JSReceiver,
initialK: Number, length: Number): Number {
// 5. Let k be 0.
// 6. Repeat, while k < len
for (let k: Number = initialK; k < length; k++) {
// 6a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
// This custom lazy deopt point is right after the callback. find() needs
// to pick up at the next step, which is returning the element if the
// callback value is truthy. Otherwise, continue the search by calling the
// continuation.
// 6b. i. Let kValue be ? Get(O, Pk).
const value: JSAny = GetProperty(o, k);
if (ToBoolean(isFound)) {
return foundValue;
// 6c. Let testResult be ToBoolean(? Call(predicate, T, <<kValue, k,
// O>>)).
const testResult: JSAny = Call(context, callbackfn, thisArg, value, k, o);
// 6d. If testResult is true, return k.
if (ToBoolean(testResult)) {
return k;
}
return ArrayFindIndexLoopContinuation(
jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength);
// 6e. Increase k by 1. (done by the loop).
}
return Convert<Smi>(-1);
}
transitioning builtin ArrayFindIndexLoopContinuation(implicit context:
Context)(
_receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny,
o: JSReceiver, initialK: Number, length: Number): Number {
// 5. Let k be 0.
// 6. Repeat, while k < len
for (let k: Number = initialK; k < length; k++) {
// 6a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
transitioning macro FastArrayFindIndex(implicit context: Context)(
o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): Number
labels Bailout(Smi) {
let k: Smi = 0;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(k);
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = NewFastJSArrayWitness(fastO);
// 6b. i. Let kValue be ? Get(O, Pk).
const value: JSAny = GetProperty(o, k);
// Build a fast loop over the smi array.
for (; k < smiLen; k++) {
fastOW.Recheck() otherwise goto Bailout(k);
// 6c. Let testResult be ToBoolean(? Call(predicate, T, <<kValue, k,
// O>>)).
const testResult: JSAny = Call(context, callbackfn, thisArg, value, k, o);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k);
// 6d. If testResult is true, return k.
if (ToBoolean(testResult)) {
return k;
}
// 6e. Increase k by 1. (done by the loop).
const value: JSAny = fastOW.LoadElementOrUndefined(k);
const testResult: JSAny =
Call(context, callbackfn, thisArg, value, k, fastOW.Get());
if (ToBoolean(testResult)) {
return k;
}
return Convert<Smi>(-1);
}
return -1;
}
transitioning macro FastArrayFindIndex(implicit context: Context)(
o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): Number
labels Bailout(Smi) {
let k: Smi = 0;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(k);
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = NewFastJSArrayWitness(fastO);
// https://tc39.github.io/ecma262/#sec-array.prototype.findIndex
transitioning javascript builtin
ArrayPrototypeFindIndex(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.findIndex');
// Build a fast loop over the smi array.
for (; k < smiLen; k++) {
fastOW.Recheck() otherwise goto Bailout(k);
// 1. Let O be ? ToObject(this value).
const o: JSReceiver = ToObject_Inline(context, receiver);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k);
// 2. Let len be ? ToLength(? Get(O, "length")).
const len: Number = GetLengthProperty(o);
const value: JSAny = fastOW.LoadElementOrUndefined(k);
const testResult: JSAny =
Call(context, callbackfn, thisArg, value, k, fastOW.Get());
if (ToBoolean(testResult)) {
return k;
}
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto NotCallableError;
}
return -1;
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallableError;
// https://tc39.github.io/ecma262/#sec-array.prototype.findIndex
transitioning javascript builtin
ArrayPrototypeFindIndex(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): JSAny {
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: JSAny = arguments[1];
// Special cases.
try {
RequireObjectCoercible(receiver, 'Array.prototype.findIndex');
// 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);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto NotCallableError;
}
const callbackfn =
Cast<Callable>(arguments[0]) otherwise NotCallableError;
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: JSAny = arguments[1];
// Special cases.
try {
return FastArrayFindIndex(o, len, callbackfn, thisArg)
otherwise Bailout;
} label Bailout(k: Smi) deferred {
return ArrayFindIndexLoopContinuation(
o, callbackfn, thisArg, o, k, len);
}
} label NotCallableError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
return FastArrayFindIndex(o, len, callbackfn, thisArg)
otherwise Bailout;
} label Bailout(k: Smi) deferred {
return ArrayFindIndexLoopContinuation(o, callbackfn, thisArg, o, k, len);
}
} label NotCallableError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
}
}
}

View File

@ -3,127 +3,126 @@
// found in the LICENSE file.
namespace array {
transitioning javascript builtin
ArrayForEachLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny {
// All continuation points in the optimized forEach implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
transitioning javascript builtin
ArrayForEachLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny {
// All continuation points in the optimized forEach implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
return ArrayForEachLoopContinuation(
jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK,
numberLength, Undefined);
}
return ArrayForEachLoopContinuation(
jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK,
numberLength, Undefined);
}
transitioning javascript builtin
ArrayForEachLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny,
_result: JSAny): JSAny {
// All continuation points in the optimized forEach implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
transitioning javascript builtin
ArrayForEachLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny,
_result: JSAny): JSAny {
// All continuation points in the optimized forEach implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
return ArrayForEachLoopContinuation(
jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK,
numberLength, Undefined);
}
return ArrayForEachLoopContinuation(
jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK,
numberLength, Undefined);
}
transitioning builtin ArrayForEachLoopContinuation(implicit context: Context)(
_receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny,
_array: JSAny, o: JSReceiver, initialK: Number, len: Number,
_to: JSAny): JSAny {
// variables {array} and {to} are ignored.
transitioning builtin ArrayForEachLoopContinuation(implicit context: Context)(
_receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, _array: JSAny,
o: JSReceiver, initialK: Number, len: Number, _to: JSAny): JSAny {
// variables {array} and {to} are ignored.
// 5. Let k be 0.
// 6. Repeat, while k < len
for (let k: Number = initialK; k < len; k = k + 1) {
// 6a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
// 5. Let k be 0.
// 6. Repeat, while k < len
for (let k: Number = initialK; k < len; k = k + 1) {
// 6a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
// 6b. Let kPresent be ? HasProperty(O, Pk).
const kPresent: Boolean = HasProperty_Inline(o, k);
// 6b. Let kPresent be ? HasProperty(O, Pk).
const kPresent: Boolean = HasProperty_Inline(o, k);
// 6c. If kPresent is true, then
if (kPresent == True) {
// 6c. i. Let kValue be ? Get(O, Pk).
const kValue: JSAny = GetProperty(o, k);
// 6c. If kPresent is true, then
if (kPresent == True) {
// 6c. i. Let kValue be ? Get(O, Pk).
const kValue: JSAny = GetProperty(o, k);
// 6c. ii. Perform ? Call(callbackfn, T, <kValue, k, O>).
Call(context, callbackfn, thisArg, kValue, k, o);
}
// 6d. Increase k by 1. (done by the loop).
// 6c. ii. Perform ? Call(callbackfn, T, <kValue, k, O>).
Call(context, callbackfn, thisArg, kValue, k, o);
}
return Undefined;
// 6d. Increase k by 1. (done by the loop).
}
return Undefined;
}
transitioning macro FastArrayForEach(implicit context: Context)(
o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny
labels Bailout(Smi) {
let k: Smi = 0;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(k);
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = NewFastJSArrayWitness(fastO);
transitioning macro FastArrayForEach(implicit context: Context)(
o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny
labels Bailout(Smi) {
let k: Smi = 0;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(k);
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = NewFastJSArrayWitness(fastO);
// Build a fast loop over the smi array.
for (; k < smiLen; k++) {
fastOW.Recheck() otherwise goto Bailout(k);
// Build a fast loop over the smi array.
for (; k < smiLen; k++) {
fastOW.Recheck() otherwise goto Bailout(k);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k);
const value: JSAny = fastOW.LoadElementNoHole(k)
otherwise continue;
Call(context, callbackfn, thisArg, value, k, fastOW.Get());
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k);
const value: JSAny = fastOW.LoadElementNoHole(k)
otherwise continue;
Call(context, callbackfn, thisArg, value, k, fastOW.Get());
}
return Undefined;
}
// https://tc39.github.io/ecma262/#sec-array.prototype.foreach
transitioning javascript builtin
ArrayForEach(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.forEach');
// 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);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto TypeError;
}
return Undefined;
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise TypeError;
// https://tc39.github.io/ecma262/#sec-array.prototype.foreach
transitioning javascript builtin
ArrayForEach(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): JSAny {
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: JSAny = arguments[1];
// Special cases.
let k: Number = 0;
try {
RequireObjectCoercible(receiver, 'Array.prototype.forEach');
// 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);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto TypeError;
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise TypeError;
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: JSAny = arguments[1];
// Special cases.
let k: Number = 0;
try {
return FastArrayForEach(o, len, callbackfn, thisArg)
otherwise Bailout;
} label Bailout(kValue: Smi) deferred {
k = kValue;
}
return ArrayForEachLoopContinuation(
o, callbackfn, thisArg, Undefined, o, k, len, Undefined);
} label TypeError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
return FastArrayForEach(o, len, callbackfn, thisArg)
otherwise Bailout;
} label Bailout(kValue: Smi) deferred {
k = kValue;
}
return ArrayForEachLoopContinuation(
o, callbackfn, thisArg, Undefined, o, k, len, Undefined);
} label TypeError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
}
}
}

View File

@ -3,181 +3,181 @@
// found in the LICENSE file.
namespace array {
// Array.from( items [, mapfn [, thisArg ] ] )
// ES #sec-array.from
transitioning javascript builtin
ArrayFrom(js-implicit context: NativeContext, receiver: JSAny)(...arguments):
JSReceiver {
// Use fast path if:
// * |items| is the only argument, and
// * the receiver is the Array function.
if (arguments.length == 1 && receiver == GetArrayFunction()) {
try {
return iterator::FastIterableToList(arguments[0]) otherwise Slow;
} label Slow {
// fall through
}
}
const items = arguments[0];
const mapfn = arguments[1];
const thisArg = arguments[2];
// 1. Let C be the this value.
const c = receiver;
let mapping: bool;
// 2. If mapfn is undefined, let mapping be false.
if (mapfn == Undefined) {
mapping = false;
} else {
// a. If IsCallable(mapfn) is false, throw a TypeError exception.
if (!Is<Callable>(mapfn)) deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, mapfn);
}
// b. Let mapping be true.
mapping = true;
}
// 4. Let usingIterator be ? GetMethod(items, @@iterator).
// 5. If usingIterator is not undefined, then
// Array.from( items [, mapfn [, thisArg ] ] )
// ES #sec-array.from
transitioning javascript builtin
ArrayFrom(js-implicit context: NativeContext, receiver: JSAny)(...arguments):
JSReceiver {
// Use fast path if:
// * |items| is the only argument, and
// * the receiver is the Array function.
if (arguments.length == 1 && receiver == GetArrayFunction()) {
try {
const usingIterator = GetMethod(items, IteratorSymbolConstant())
otherwise IteratorIsUndefined, IteratorNotCallable;
return iterator::FastIterableToList(arguments[0]) otherwise Slow;
} label Slow {
// fall through
}
}
let a: JSReceiver;
// a. If IsConstructor(C) is true, then
typeswitch (c) {
case (c: Constructor): {
// i. Let A be ? Construct(C).
a = Construct(c);
}
case (JSAny): {
// i. Let A be ? ArrayCreate(0).
a = ArrayCreate(0);
}
const items = arguments[0];
const mapfn = arguments[1];
const thisArg = arguments[2];
// 1. Let C be the this value.
const c = receiver;
let mapping: bool;
// 2. If mapfn is undefined, let mapping be false.
if (mapfn == Undefined) {
mapping = false;
} else {
// a. If IsCallable(mapfn) is false, throw a TypeError exception.
if (!Is<Callable>(mapfn)) deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, mapfn);
}
// b. Let mapping be true.
mapping = true;
}
// 4. Let usingIterator be ? GetMethod(items, @@iterator).
// 5. If usingIterator is not undefined, then
try {
const usingIterator = GetMethod(items, IteratorSymbolConstant())
otherwise IteratorIsUndefined, IteratorNotCallable;
let a: JSReceiver;
// a. If IsConstructor(C) is true, then
typeswitch (c) {
case (c: Constructor): {
// i. Let A be ? Construct(C).
a = Construct(c);
}
case (JSAny): {
// i. Let A be ? ArrayCreate(0).
a = ArrayCreate(0);
}
}
// c. Let iteratorRecord be ? GetIterator(items, sync, usingIterator).
const iteratorRecord = iterator::GetIterator(items, usingIterator);
const fastIteratorResultMap = GetIteratorResultMap();
// d. Let k be 0.
let k: Smi = 0;
// e. Repeat,
while (true) {
// i. If k ≥ 2^53-1, then
// 1. Let error be ThrowCompletion(a newly created TypeError object).
// 2. Return ? IteratorClose(iteratorRecord, error).
// The spec requires that we throw an exception if index reaches 2^53-1,
// but an empty loop would take >100 days to do this many iterations. To
// actually run for that long would require an iterator that never set
// done to true and a target array which somehow never ran out of
// memory, e.g. a proxy that discarded the values. Ignoring this case
// just means we would repeatedly call CreateDataProperty with index =
// 2^53
assert(k < kMaxSafeInteger);
// ii. Let Pk be ! ToString(k).
// iii. Let next be ? IteratorStep(iteratorRecord).
let next: JSReceiver;
try {
next = iterator::IteratorStep(iteratorRecord, fastIteratorResultMap)
otherwise NextIsFalse;
}
// iv. If next is false, then
label NextIsFalse {
// 1. Perform ? Set(A, "length", k, true).
array::SetPropertyLength(a, k);
// 2. Return A.
return a;
}
// c. Let iteratorRecord be ? GetIterator(items, sync, usingIterator).
const iteratorRecord = iterator::GetIterator(items, usingIterator);
// v. Let nextValue be ? IteratorValue(next).
const nextValue = iterator::IteratorValue(next, fastIteratorResultMap);
const fastIteratorResultMap = GetIteratorResultMap();
// d. Let k be 0.
let k: Smi = 0;
// e. Repeat,
while (true) {
// i. If k ≥ 2^53-1, then
// 1. Let error be ThrowCompletion(a newly created TypeError object).
// 2. Return ? IteratorClose(iteratorRecord, error).
// The spec requires that we throw an exception if index reaches 2^53-1,
// but an empty loop would take >100 days to do this many iterations. To
// actually run for that long would require an iterator that never set
// done to true and a target array which somehow never ran out of
// memory, e.g. a proxy that discarded the values. Ignoring this case
// just means we would repeatedly call CreateDataProperty with index =
// 2^53
assert(k < kMaxSafeInteger);
// ii. Let Pk be ! ToString(k).
// iii. Let next be ? IteratorStep(iteratorRecord).
let next: JSReceiver;
let mappedValue: JSAny;
// vi. If mapping is true, then
if (mapping) {
// 1. Let mappedValue be Call(mapfn, thisArg, « nextValue, k »).
// 2. If mappedValue is an abrupt completion,
// return ? IteratorClose(iteratorRecord, mappedValue).
// 3. Set mappedValue to mappedValue.[[Value]].
try {
next = iterator::IteratorStep(iteratorRecord, fastIteratorResultMap)
otherwise NextIsFalse;
}
// iv. If next is false, then
label NextIsFalse {
// 1. Perform ? Set(A, "length", k, true).
array::SetPropertyLength(a, k);
// 2. Return A.
return a;
}
// v. Let nextValue be ? IteratorValue(next).
const nextValue = iterator::IteratorValue(next, fastIteratorResultMap);
let mappedValue: JSAny;
// vi. If mapping is true, then
if (mapping) {
// 1. Let mappedValue be Call(mapfn, thisArg, « nextValue, k »).
// 2. If mappedValue is an abrupt completion,
// return ? IteratorClose(iteratorRecord, mappedValue).
// 3. Set mappedValue to mappedValue.[[Value]].
try {
mappedValue = Call(
context, UnsafeCast<Callable>(mapfn), thisArg, nextValue, k);
} catch (e) {
iterator::IteratorCloseOnException(iteratorRecord);
ReThrow(context, e);
}
} else {
mappedValue = nextValue;
}
// viii. Let defineStatus be
// CreateDataPropertyOrThrow(A, Pk, mappedValue).
// ix. If defineStatus is an abrupt completion,
// return ? IteratorClose(iteratorRecord, defineStatus).
try {
FastCreateDataProperty(a, k, mappedValue);
} catch (e) deferred {
mappedValue =
Call(context, UnsafeCast<Callable>(mapfn), thisArg, nextValue, k);
} catch (e) {
iterator::IteratorCloseOnException(iteratorRecord);
ReThrow(context, e);
}
// x. Set k to k + 1.
k += 1;
} else {
mappedValue = nextValue;
}
unreachable;
} label IteratorIsUndefined {
// 6. NOTE: items is not an Iterable so assume it is an array-like object.
// 7. Let arrayLike be ! ToObject(items).
const arrayLike = ToObject_Inline(context, items);
// 8. Let len be ? LengthOfArrayLike(arrayLike).
const len = GetLengthProperty(arrayLike);
let a: JSReceiver;
// 9. If IsConstructor(C) is true, then
typeswitch (c) {
case (c: Constructor): {
// a. Let A be ? Construct(C, « len »).
a = Construct(c, len);
}
case (JSAny): {
// a. Let A be ? ArrayCreate(len).
a = ArrayCreate(len);
}
}
// 11. Let k be 0.
let k: Smi = 0;
// 12. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(arrayLike, Pk).
const kValue = GetProperty(arrayLike, k);
let mappedValue: JSAny;
// c. If mapping is true, then
if (mapping) {
// i. Let mappedValue be ? Call(mapfn, thisArg, « kValue, k »).
mappedValue =
Call(context, UnsafeCast<Callable>(mapfn), thisArg, kValue, k);
} else {
// d. Else, let mappedValue be kValue.
mappedValue = kValue;
}
// e. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).
// viii. Let defineStatus be
// CreateDataPropertyOrThrow(A, Pk, mappedValue).
// ix. If defineStatus is an abrupt completion,
// return ? IteratorClose(iteratorRecord, defineStatus).
try {
FastCreateDataProperty(a, k, mappedValue);
// f. Set k to k + 1.
k += 1;
} catch (e) deferred {
iterator::IteratorCloseOnException(iteratorRecord);
ReThrow(context, e);
}
// 13. Perform ? Set(A, "length", len, true).
array::SetPropertyLength(a, len);
// 14. Return A.
return a;
} label IteratorNotCallable(_value: JSAny) deferred {
ThrowTypeError(MessageTemplate::kIteratorSymbolNonCallable);
// x. Set k to k + 1.
k += 1;
}
unreachable;
} label IteratorIsUndefined {
// 6. NOTE: items is not an Iterable so assume it is an array-like object.
// 7. Let arrayLike be ! ToObject(items).
const arrayLike = ToObject_Inline(context, items);
// 8. Let len be ? LengthOfArrayLike(arrayLike).
const len = GetLengthProperty(arrayLike);
let a: JSReceiver;
// 9. If IsConstructor(C) is true, then
typeswitch (c) {
case (c: Constructor): {
// a. Let A be ? Construct(C, « len »).
a = Construct(c, len);
}
case (JSAny): {
// a. Let A be ? ArrayCreate(len).
a = ArrayCreate(len);
}
}
// 11. Let k be 0.
let k: Smi = 0;
// 12. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(arrayLike, Pk).
const kValue = GetProperty(arrayLike, k);
let mappedValue: JSAny;
// c. If mapping is true, then
if (mapping) {
// i. Let mappedValue be ? Call(mapfn, thisArg, « kValue, k »).
mappedValue =
Call(context, UnsafeCast<Callable>(mapfn), thisArg, kValue, k);
} else {
// d. Else, let mappedValue be kValue.
mappedValue = kValue;
}
// e. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).
FastCreateDataProperty(a, k, mappedValue);
// f. Set k to k + 1.
k += 1;
}
// 13. Perform ? Set(A, "length", len, true).
array::SetPropertyLength(a, len);
// 14. Return A.
return a;
} label IteratorNotCallable(_value: JSAny) deferred {
ThrowTypeError(MessageTemplate::kIteratorSymbolNonCallable);
}
}
}

View File

@ -3,25 +3,25 @@
// found in the LICENSE file.
namespace runtime {
extern runtime ArrayIsArray(implicit context: Context)(JSAny): JSAny;
extern runtime ArrayIsArray(implicit context: Context)(JSAny): JSAny;
} // namespace runtime
namespace array {
// ES #sec-array.isarray
javascript builtin ArrayIsArray(js-implicit context:
NativeContext)(arg: JSAny): JSAny {
// 1. Return ? IsArray(arg).
typeswitch (arg) {
case (JSArray): {
return True;
}
case (JSProxy): {
// TODO(verwaest): Handle proxies in-place
return runtime::ArrayIsArray(arg);
}
case (JSAny): {
return False;
}
// ES #sec-array.isarray
javascript builtin ArrayIsArray(js-implicit context: NativeContext)(arg: JSAny):
JSAny {
// 1. Return ? IsArray(arg).
typeswitch (arg) {
case (JSArray): {
return True;
}
case (JSProxy): {
// TODO(verwaest): Handle proxies in-place
return runtime::ArrayIsArray(arg);
}
case (JSAny): {
return False;
}
}
}
} // namespace array

File diff suppressed because it is too large Load Diff

View File

@ -3,152 +3,151 @@
// found in the LICENSE file.
namespace array {
macro LoadWithHoleCheck<Elements : type extends FixedArrayBase>(
elements: FixedArrayBase, index: Smi): JSAny
labels IfHole;
macro LoadWithHoleCheck<Elements : type extends FixedArrayBase>(
elements: FixedArrayBase, index: Smi): JSAny
labels IfHole;
LoadWithHoleCheck<FixedArray>(implicit context: Context)(
elements: FixedArrayBase, index: Smi): JSAny
labels IfHole {
const elements: FixedArray = UnsafeCast<FixedArray>(elements);
const element: Object = elements.objects[index];
if (element == TheHole) goto IfHole;
return UnsafeCast<JSAny>(element);
LoadWithHoleCheck<FixedArray>(implicit context: Context)(
elements: FixedArrayBase, index: Smi): JSAny
labels IfHole {
const elements: FixedArray = UnsafeCast<FixedArray>(elements);
const element: Object = elements.objects[index];
if (element == TheHole) goto IfHole;
return UnsafeCast<JSAny>(element);
}
LoadWithHoleCheck<FixedDoubleArray>(implicit context: Context)(
elements: FixedArrayBase, index: Smi): JSAny
labels IfHole {
const elements: FixedDoubleArray = UnsafeCast<FixedDoubleArray>(elements);
const element: float64 = elements.floats[index].Value() otherwise IfHole;
return AllocateHeapNumberWithValue(element);
}
macro FastArrayLastIndexOf<Elements : type extends FixedArrayBase>(
context: Context, array: JSArray, from: Smi, searchElement: JSAny): Smi {
const elements: FixedArrayBase = array.elements;
let k: Smi = from;
// Bug(898785): Due to side-effects in the evaluation of `fromIndex`
// the {from} can be out-of-bounds here, so we need to clamp {k} to
// the {elements} length. We might be reading holes / hole NaNs still
// due to that, but those will be ignored below.
if (k >= elements.length) {
k = elements.length - 1;
}
LoadWithHoleCheck<FixedDoubleArray>(implicit context: Context)(
elements: FixedArrayBase, index: Smi): JSAny
labels IfHole {
const elements: FixedDoubleArray = UnsafeCast<FixedDoubleArray>(elements);
const element: float64 = elements.floats[index].Value() otherwise IfHole;
return AllocateHeapNumberWithValue(element);
while (k >= 0) {
try {
const element: JSAny = LoadWithHoleCheck<Elements>(elements, k)
otherwise Hole;
const same: Boolean = StrictEqual(searchElement, element);
if (same == True) {
assert(Is<FastJSArray>(array));
return k;
}
} label Hole {} // Do nothing for holes.
--k;
}
macro FastArrayLastIndexOf<Elements : type extends FixedArrayBase>(
context: Context, array: JSArray, from: Smi, searchElement: JSAny): Smi {
const elements: FixedArrayBase = array.elements;
let k: Smi = from;
assert(Is<FastJSArray>(array));
return -1;
}
// Bug(898785): Due to side-effects in the evaluation of `fromIndex`
// the {from} can be out-of-bounds here, so we need to clamp {k} to
// the {elements} length. We might be reading holes / hole NaNs still
// due to that, but those will be ignored below.
if (k >= elements.length) {
k = elements.length - 1;
}
transitioning macro
GetFromIndex(context: Context, length: Number, arguments: Arguments): Number {
// 4. If fromIndex is present, let n be ? ToInteger(fromIndex);
// else let n be len - 1.
const n: Number =
arguments.length < 2 ? length - 1 : ToInteger_Inline(arguments[1]);
while (k >= 0) {
try {
const element: JSAny = LoadWithHoleCheck<Elements>(elements, k)
otherwise Hole;
const same: Boolean = StrictEqual(searchElement, element);
if (same == True) {
assert(Is<FastJSArray>(array));
return k;
}
} label Hole {} // Do nothing for holes.
--k;
}
assert(Is<FastJSArray>(array));
return -1;
// 5. If n >= 0, then.
let k: Number = SmiConstant(0);
if (n >= 0) {
// a. If n is -0, let k be +0; else let k be min(n, len - 1).
// If n was -0 it got truncated to 0.0, so taking the minimum is fine.
k = Min(n, length - 1);
} else {
// a. Let k be len + n.
k = length + n;
}
return k;
}
transitioning macro
GetFromIndex(context: Context, length: Number, arguments: Arguments): Number {
// 4. If fromIndex is present, let n be ? ToInteger(fromIndex);
// else let n be len - 1.
const n: Number =
arguments.length < 2 ? length - 1 : ToInteger_Inline(arguments[1]);
macro TryFastArrayLastIndexOf(
context: Context, receiver: JSReceiver, searchElement: JSAny,
from: Number): JSAny
labels Slow {
const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
const length: Smi = array.length;
if (length == 0) return SmiConstant(-1);
// 5. If n >= 0, then.
let k: Number = SmiConstant(0);
if (n >= 0) {
// a. If n is -0, let k be +0; else let k be min(n, len - 1).
// If n was -0 it got truncated to 0.0, so taking the minimum is fine.
k = Min(n, length - 1);
} else {
// a. Let k be len + n.
k = length + n;
}
return k;
}
macro TryFastArrayLastIndexOf(
context: Context, receiver: JSReceiver, searchElement: JSAny,
from: Number): JSAny
labels Slow {
const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
const length: Smi = array.length;
if (length == 0) return SmiConstant(-1);
const fromSmi: Smi = Cast<Smi>(from) otherwise Slow;
const kind: ElementsKind = array.map.elements_kind;
if (IsFastSmiOrTaggedElementsKind(kind)) {
return FastArrayLastIndexOf<FixedArray>(
context, array, fromSmi, searchElement);
}
assert(IsDoubleElementsKind(kind));
return FastArrayLastIndexOf<FixedDoubleArray>(
const fromSmi: Smi = Cast<Smi>(from) otherwise Slow;
const kind: ElementsKind = array.map.elements_kind;
if (IsFastSmiOrTaggedElementsKind(kind)) {
return FastArrayLastIndexOf<FixedArray>(
context, array, fromSmi, searchElement);
}
assert(IsDoubleElementsKind(kind));
return FastArrayLastIndexOf<FixedDoubleArray>(
context, array, fromSmi, searchElement);
}
transitioning macro GenericArrayLastIndexOf(
context: Context, object: JSReceiver, searchElement: JSAny,
from: Number): JSAny {
let k: Number = from;
transitioning macro GenericArrayLastIndexOf(
context: Context, object: JSReceiver, searchElement: JSAny,
from: Number): JSAny {
let k: Number = from;
// 7. Repeat, while k >= 0.
while (k >= 0) {
// a. Let kPresent be ? HasProperty(O, ! ToString(k)).
const kPresent: Boolean = HasProperty(object, k);
// 7. Repeat, while k >= 0.
while (k >= 0) {
// a. Let kPresent be ? HasProperty(O, ! ToString(k)).
const kPresent: Boolean = HasProperty(object, k);
// b. If kPresent is true, then.
if (kPresent == True) {
// i. Let elementK be ? Get(O, ! ToString(k)).
const element: JSAny = GetProperty(object, k);
// b. If kPresent is true, then.
if (kPresent == True) {
// i. Let elementK be ? Get(O, ! ToString(k)).
const element: JSAny = GetProperty(object, k);
// ii. Let same be the result of performing Strict Equality Comparison
// searchElement === elementK.
const same: Boolean = StrictEqual(searchElement, element);
// ii. Let same be the result of performing Strict Equality Comparison
// searchElement === elementK.
const same: Boolean = StrictEqual(searchElement, element);
// iii. If same is true, return k.
if (same == True) return k;
}
// c. Decrease k by 1.
--k;
// iii. If same is true, return k.
if (same == True) return k;
}
// 8. Return -1.
return SmiConstant(-1);
// c. Decrease k by 1.
--k;
}
// https://tc39.github.io/ecma262/#sec-array.prototype.lastIndexOf
transitioning javascript builtin ArrayPrototypeLastIndexOf(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSAny {
// 1. Let O be ? ToObject(this value).
const object: JSReceiver = ToObject_Inline(context, receiver);
// 8. Return -1.
return SmiConstant(-1);
}
// 2. Let len be ? ToLength(? Get(O, "length")).
const length: Number = GetLengthProperty(object);
// https://tc39.github.io/ecma262/#sec-array.prototype.lastIndexOf
transitioning javascript builtin ArrayPrototypeLastIndexOf(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// 1. Let O be ? ToObject(this value).
const object: JSReceiver = ToObject_Inline(context, receiver);
// 3. If len is 0, return -1.
if (length == SmiConstant(0)) return SmiConstant(-1);
// 2. Let len be ? ToLength(? Get(O, "length")).
const length: Number = GetLengthProperty(object);
// Step 4 - 6.
const from: Number = GetFromIndex(context, length, arguments);
// 3. If len is 0, return -1.
if (length == SmiConstant(0)) return SmiConstant(-1);
const searchElement: JSAny = arguments[0];
// Step 4 - 6.
const from: Number = GetFromIndex(context, length, arguments);
try {
return TryFastArrayLastIndexOf(context, object, searchElement, from)
otherwise Baseline;
} label Baseline {
return GenericArrayLastIndexOf(context, object, searchElement, from);
}
const searchElement: JSAny = arguments[0];
try {
return TryFastArrayLastIndexOf(context, object, searchElement, from)
otherwise Baseline;
} label Baseline {
return GenericArrayLastIndexOf(context, object, searchElement, from);
}
}
}

View File

@ -3,266 +3,265 @@
// found in the LICENSE file.
namespace array {
transitioning javascript builtin
ArrayMapLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny,
length: JSAny): JSAny {
// All continuation points in the optimized filter implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const outputArray = Cast<JSReceiver>(array) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
transitioning javascript builtin
ArrayMapLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny,
length: JSAny): JSAny {
// All continuation points in the optimized filter implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const outputArray = Cast<JSReceiver>(array) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
return ArrayMapLoopContinuation(
jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK,
numberLength);
}
return ArrayMapLoopContinuation(
jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK,
numberLength);
}
transitioning javascript builtin
ArrayMapLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny,
length: JSAny, result: JSAny): JSAny {
// All continuation points in the optimized filter implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const outputArray = Cast<JSReceiver>(array) otherwise unreachable;
let numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
transitioning javascript builtin
ArrayMapLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny,
length: JSAny, result: JSAny): JSAny {
// All continuation points in the optimized filter implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const outputArray = Cast<JSReceiver>(array) otherwise unreachable;
let numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
// This custom lazy deopt point is right after the callback. map() needs
// to pick up at the next step, which is setting the callback result in
// the output array. After incrementing k, we can glide into the loop
// continuation builtin.
// This custom lazy deopt point is right after the callback. map() needs
// to pick up at the next step, which is setting the callback result in
// the output array. After incrementing k, we can glide into the loop
// continuation builtin.
// iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).
FastCreateDataProperty(outputArray, numberK, result);
// iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).
FastCreateDataProperty(outputArray, numberK, result);
// 7d. Increase k by 1.
numberK = numberK + 1;
// 7d. Increase k by 1.
numberK = numberK + 1;
return ArrayMapLoopContinuation(
jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK,
numberLength);
}
return ArrayMapLoopContinuation(
jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK,
numberLength);
}
transitioning builtin ArrayMapLoopContinuation(implicit context: Context)(
_receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny,
array: JSReceiver, o: JSReceiver, initialK: Number,
length: Number): JSAny {
// 6. Let k be 0.
// 7. Repeat, while k < len
for (let k: Number = initialK; k < length; k++) {
// 7a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
transitioning builtin ArrayMapLoopContinuation(implicit context: Context)(
_receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny,
array: JSReceiver, o: JSReceiver, initialK: Number, length: Number): JSAny {
// 6. Let k be 0.
// 7. Repeat, while k < len
for (let k: Number = initialK; k < length; k++) {
// 7a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
// 7b. Let kPresent be ? HasProperty(O, Pk).
const kPresent: Boolean = HasProperty_Inline(o, k);
// 7b. Let kPresent be ? HasProperty(O, Pk).
const kPresent: Boolean = HasProperty_Inline(o, k);
// 7c. If kPresent is true, then:
if (kPresent == True) {
// i. Let kValue be ? Get(O, Pk).
const kValue: JSAny = GetProperty(o, k);
// 7c. If kPresent is true, then:
if (kPresent == True) {
// i. Let kValue be ? Get(O, Pk).
const kValue: JSAny = GetProperty(o, k);
// ii. Let mapped_value be ? Call(callbackfn, T, kValue, k, O).
const mappedValue: JSAny =
Call(context, callbackfn, thisArg, kValue, k, o);
// ii. Let mapped_value be ? Call(callbackfn, T, kValue, k, O).
const mappedValue: JSAny =
Call(context, callbackfn, thisArg, kValue, k, o);
// iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value).
FastCreateDataProperty(array, k, mappedValue);
}
// 7d. Increase k by 1. (done by the loop).
// iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value).
FastCreateDataProperty(array, k, mappedValue);
}
// 8. Return A.
return array;
// 7d. Increase k by 1. (done by the loop).
}
struct Vector {
macro ReportSkippedElement() {
this.skippedElements = true;
// 8. Return A.
return array;
}
struct Vector {
macro ReportSkippedElement() {
this.skippedElements = true;
}
macro CreateJSArray(implicit context: Context)(validLength: Smi): JSArray {
const length: Smi = this.fixedArray.length;
assert(validLength <= length);
let kind: ElementsKind = ElementsKind::PACKED_SMI_ELEMENTS;
if (!this.onlySmis) {
if (this.onlyNumbers) {
kind = ElementsKind::PACKED_DOUBLE_ELEMENTS;
} else {
kind = ElementsKind::PACKED_ELEMENTS;
}
}
macro CreateJSArray(implicit context: Context)(validLength: Smi): JSArray {
const length: Smi = this.fixedArray.length;
assert(validLength <= length);
let kind: ElementsKind = ElementsKind::PACKED_SMI_ELEMENTS;
if (!this.onlySmis) {
if (this.onlyNumbers) {
kind = ElementsKind::PACKED_DOUBLE_ELEMENTS;
} else {
kind = ElementsKind::PACKED_ELEMENTS;
}
}
if (this.skippedElements || validLength < length) {
// We also need to create a holey output array if we are
// bailing out of the fast path partway through the array.
// This is indicated by {validLength} < {length}.
// Who knows if the bailout condition will continue to fill in
// every element?
kind = FastHoleyElementsKind(kind);
}
if (this.skippedElements || validLength < length) {
// We also need to create a holey output array if we are
// bailing out of the fast path partway through the array.
// This is indicated by {validLength} < {length}.
// Who knows if the bailout condition will continue to fill in
// every element?
kind = FastHoleyElementsKind(kind);
}
const map: Map = LoadJSArrayElementsMap(kind, LoadNativeContext(context));
let a: JSArray;
const map: Map = LoadJSArrayElementsMap(kind, LoadNativeContext(context));
let a: JSArray;
if (IsDoubleElementsKind(kind)) {
// We need to allocate and copy.
// First, initialize the elements field before allocation to prevent
// heap corruption.
const elements: FixedDoubleArray = AllocateFixedDoubleArrayWithHoles(
SmiUntag(length), AllocationFlag::kAllowLargeObjectAllocation);
a = NewJSArray(map, this.fixedArray);
for (let i: Smi = 0; i < validLength; i++) {
typeswitch (
UnsafeCast<(Number | TheHole)>(this.fixedArray.objects[i])) {
case (n: Number): {
elements.floats[i] = Convert<float64_or_hole>(n);
}
case (TheHole): {
}
if (IsDoubleElementsKind(kind)) {
// We need to allocate and copy.
// First, initialize the elements field before allocation to prevent
// heap corruption.
const elements: FixedDoubleArray = AllocateFixedDoubleArrayWithHoles(
SmiUntag(length), AllocationFlag::kAllowLargeObjectAllocation);
a = NewJSArray(map, this.fixedArray);
for (let i: Smi = 0; i < validLength; i++) {
typeswitch (
UnsafeCast<(Number | TheHole)>(this.fixedArray.objects[i])) {
case (n: Number): {
elements.floats[i] = Convert<float64_or_hole>(n);
}
case (TheHole): {
}
}
a.elements = elements;
} else {
// Simply install the given fixedArray in {vector}.
a = NewJSArray(map, this.fixedArray);
}
// Paranoia. the FixedArray now "belongs" to JSArray {a}.
this.fixedArray = kEmptyFixedArray;
return a;
a.elements = elements;
} else {
// Simply install the given fixedArray in {vector}.
a = NewJSArray(map, this.fixedArray);
}
macro StoreResult(implicit context: Context)(index: Smi, result: JSAny) {
typeswitch (result) {
case (s: Smi): {
this.fixedArray.objects[index] = s;
}
case (s: HeapNumber): {
this.onlySmis = false;
this.fixedArray.objects[index] = s;
}
case (s: JSAnyNotNumber): {
this.onlySmis = false;
this.onlyNumbers = false;
this.fixedArray.objects[index] = s;
}
// Paranoia. the FixedArray now "belongs" to JSArray {a}.
this.fixedArray = kEmptyFixedArray;
return a;
}
macro StoreResult(implicit context: Context)(index: Smi, result: JSAny) {
typeswitch (result) {
case (s: Smi): {
this.fixedArray.objects[index] = s;
}
case (s: HeapNumber): {
this.onlySmis = false;
this.fixedArray.objects[index] = s;
}
case (s: JSAnyNotNumber): {
this.onlySmis = false;
this.onlyNumbers = false;
this.fixedArray.objects[index] = s;
}
}
fixedArray: FixedArray;
onlySmis: bool; // initially true.
onlyNumbers: bool; // initially true.
skippedElements: bool; // initially false.
}
macro NewVector(implicit context: Context)(length: Smi): Vector {
const fixedArray = length > 0 ?
AllocateFixedArrayWithHoles(
SmiUntag(length), AllocationFlag::kAllowLargeObjectAllocation) :
kEmptyFixedArray;
return Vector{
fixedArray,
onlySmis: true,
onlyNumbers: true,
skippedElements: false
};
}
fixedArray: FixedArray;
onlySmis: bool; // initially true.
onlyNumbers: bool; // initially true.
skippedElements: bool; // initially false.
}
transitioning macro FastArrayMap(implicit context: Context)(
fastO: FastJSArrayForRead, len: Smi, callbackfn: Callable,
thisArg: JSAny): JSArray
labels Bailout(JSArray, Smi) {
let k: Smi = 0;
let fastOW = NewFastJSArrayForReadWitness(fastO);
let vector = NewVector(len);
macro NewVector(implicit context: Context)(length: Smi): Vector {
const fixedArray = length > 0 ?
AllocateFixedArrayWithHoles(
SmiUntag(length), AllocationFlag::kAllowLargeObjectAllocation) :
kEmptyFixedArray;
return Vector{
fixedArray,
onlySmis: true,
onlyNumbers: true,
skippedElements: false
};
}
// Build a fast loop over the smi array.
// 7. Repeat, while k < len.
try {
for (; k < len; k++) {
fastOW.Recheck() otherwise goto PrepareBailout(k);
transitioning macro FastArrayMap(implicit context: Context)(
fastO: FastJSArrayForRead, len: Smi, callbackfn: Callable,
thisArg: JSAny): JSArray
labels Bailout(JSArray, Smi) {
let k: Smi = 0;
let fastOW = NewFastJSArrayForReadWitness(fastO);
let vector = NewVector(len);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto PrepareBailout(k);
// Build a fast loop over the smi array.
// 7. Repeat, while k < len.
try {
for (; k < len; k++) {
fastOW.Recheck() otherwise goto PrepareBailout(k);
try {
const value: JSAny = fastOW.LoadElementNoHole(k)
otherwise FoundHole;
const result: JSAny =
Call(context, callbackfn, thisArg, value, k, fastOW.Get());
vector.StoreResult(k, result);
} label FoundHole {
// Our output array must necessarily be holey because of holes in
// the input array.
vector.ReportSkippedElement();
}
}
} label PrepareBailout(k: Smi) deferred {
// Transform {vector} into a JSArray and bail out.
goto Bailout(vector.CreateJSArray(k), k);
}
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto PrepareBailout(k);
return vector.CreateJSArray(len);
}
// https://tc39.github.io/ecma262/#sec-array.prototype.map
transitioning javascript builtin
ArrayMap(js-implicit context: NativeContext, receiver: JSAny)(...arguments):
JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.map');
// 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);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) goto TypeError;
const callbackfn = Cast<Callable>(arguments[0]) otherwise TypeError;
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: JSAny = arguments[1];
let array: JSReceiver;
let k: Number = 0;
try {
// 5. Let A be ? ArraySpeciesCreate(O, len).
if (IsArraySpeciesProtectorCellInvalid()) goto SlowSpeciesCreate;
const o: FastJSArrayForRead = Cast<FastJSArrayForRead>(receiver)
otherwise SlowSpeciesCreate;
const smiLength: Smi = Cast<Smi>(len)
otherwise SlowSpeciesCreate;
return FastArrayMap(o, smiLength, callbackfn, thisArg)
otherwise Bailout;
} label SlowSpeciesCreate {
array = ArraySpeciesCreate(context, receiver, len);
} label Bailout(output: JSArray, kValue: Smi) deferred {
array = output;
k = kValue;
const value: JSAny = fastOW.LoadElementNoHole(k)
otherwise FoundHole;
const result: JSAny =
Call(context, callbackfn, thisArg, value, k, fastOW.Get());
vector.StoreResult(k, result);
} label FoundHole {
// Our output array must necessarily be holey because of holes in
// the input array.
vector.ReportSkippedElement();
}
return ArrayMapLoopContinuation(o, callbackfn, thisArg, array, o, k, len);
} label TypeError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
}
} label PrepareBailout(k: Smi) deferred {
// Transform {vector} into a JSArray and bail out.
goto Bailout(vector.CreateJSArray(k), k);
}
return vector.CreateJSArray(len);
}
// https://tc39.github.io/ecma262/#sec-array.prototype.map
transitioning javascript builtin
ArrayMap(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.map');
// 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);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) goto TypeError;
const callbackfn = Cast<Callable>(arguments[0]) otherwise TypeError;
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: JSAny = arguments[1];
let array: JSReceiver;
let k: Number = 0;
try {
// 5. Let A be ? ArraySpeciesCreate(O, len).
if (IsArraySpeciesProtectorCellInvalid()) goto SlowSpeciesCreate;
const o: FastJSArrayForRead = Cast<FastJSArrayForRead>(receiver)
otherwise SlowSpeciesCreate;
const smiLength: Smi = Cast<Smi>(len)
otherwise SlowSpeciesCreate;
return FastArrayMap(o, smiLength, callbackfn, thisArg)
otherwise Bailout;
} label SlowSpeciesCreate {
array = ArraySpeciesCreate(context, receiver, len);
} label Bailout(output: JSArray, kValue: Smi) deferred {
array = output;
k = kValue;
}
return ArrayMapLoopContinuation(o, callbackfn, thisArg, array, o, k, len);
} label TypeError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
}
}
}

View File

@ -3,53 +3,53 @@
// found in the LICENSE file.
namespace array {
// https://tc39.github.io/ecma262/#sec-array.of
transitioning javascript builtin
ArrayOf(js-implicit context: NativeContext, receiver: JSAny)(...arguments):
JSAny {
// 1. Let len be the actual number of arguments passed to this function.
const len: Smi = Convert<Smi>(arguments.length);
// https://tc39.github.io/ecma262/#sec-array.of
transitioning javascript builtin
ArrayOf(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// 1. Let len be the actual number of arguments passed to this function.
const len: Smi = Convert<Smi>(arguments.length);
// 2. Let items be the List of arguments passed to this function.
const items: Arguments = arguments;
// 2. Let items be the List of arguments passed to this function.
const items: Arguments = arguments;
// 3. Let C be the this value.
const c: JSAny = receiver;
// 3. Let C be the this value.
const c: JSAny = receiver;
let a: JSReceiver;
let a: JSReceiver;
// 4. If IsConstructor(C) is true, then
typeswitch (c) {
case (c: Constructor): {
// a. Let A be ? Construct(C, « len »).
a = Construct(c, len);
}
case (JSAny): {
// a. Let A be ? ArrayCreate(len).
a = ArrayCreate(len);
}
// 4. If IsConstructor(C) is true, then
typeswitch (c) {
case (c: Constructor): {
// a. Let A be ? Construct(C, « len »).
a = Construct(c, len);
}
// 6. Let k be 0.
let k: Smi = 0;
// 7. Repeat, while k < len
while (k < len) {
// a. Let kValue be items[k].
const kValue: JSAny = items[Convert<intptr>(k)];
// b. Let Pk be ! ToString(k).
// c. Perform ? CreateDataPropertyOrThrow(A, Pk, kValue).
FastCreateDataProperty(a, k, kValue);
// d. Increase k by 1.
k++;
case (JSAny): {
// a. Let A be ? ArrayCreate(len).
a = ArrayCreate(len);
}
// 8. Perform ? Set(A, "length", len, true).
array::SetPropertyLength(a, len);
// 9. Return A.
return a;
}
// 6. Let k be 0.
let k: Smi = 0;
// 7. Repeat, while k < len
while (k < len) {
// a. Let kValue be items[k].
const kValue: JSAny = items[Convert<intptr>(k)];
// b. Let Pk be ! ToString(k).
// c. Perform ? CreateDataPropertyOrThrow(A, Pk, kValue).
FastCreateDataProperty(a, k, kValue);
// d. Increase k by 1.
k++;
}
// 8. Perform ? Set(A, "length", len, true).
array::SetPropertyLength(a, len);
// 9. Return A.
return a;
}
}

View File

@ -3,196 +3,195 @@
// found in the LICENSE file.
namespace array {
transitioning javascript builtin
ArrayReduceRightPreLoopEagerDeoptContinuation(
js-implicit context: NativeContext,
receiver: JSAny)(callback: JSAny, length: JSAny): JSAny {
// All continuation points in the optimized every implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
const initialK = numberLength - 1;
transitioning javascript builtin
ArrayReduceRightPreLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, length: JSAny): JSAny {
// All continuation points in the optimized every implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
const initialK = numberLength - 1;
// Simulate starting the loop at {length - 1}, but ensuring that the
// accumulator is the hole. The continuation stub will search for the
// last non-hole element, rightly throwing an exception if not found.
return ArrayReduceRightLoopContinuation(
jsreceiver, callbackfn, TheHole, jsreceiver, initialK, numberLength);
}
// Simulate starting the loop at {length - 1}, but ensuring that the
// accumulator is the hole. The continuation stub will search for the
// last non-hole element, rightly throwing an exception if not found.
return ArrayReduceRightLoopContinuation(
jsreceiver, callbackfn, TheHole, jsreceiver, initialK, numberLength);
}
transitioning javascript builtin
ArrayReduceRightLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, initialK: JSAny, length: JSAny,
accumulator: JSAny): JSAny {
// All continuation points in the optimized every implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
transitioning javascript builtin
ArrayReduceRightLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, initialK: JSAny, length: JSAny,
accumulator: JSAny): JSAny {
// All continuation points in the optimized every implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
return ArrayReduceRightLoopContinuation(
jsreceiver, callbackfn, accumulator, jsreceiver, numberK, numberLength);
}
return ArrayReduceRightLoopContinuation(
jsreceiver, callbackfn, accumulator, jsreceiver, numberK, numberLength);
}
transitioning javascript builtin
ArrayReduceRightLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, initialK: JSAny, length: JSAny, result: JSAny): JSAny {
// All continuation points in the optimized every implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
transitioning javascript builtin
ArrayReduceRightLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, initialK: JSAny, length: JSAny, result: JSAny): JSAny {
// All continuation points in the optimized every implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
// The accumulator is the result from the callback call which just occured.
const r = ArrayReduceRightLoopContinuation(
jsreceiver, callbackfn, result, jsreceiver, numberK, numberLength);
return r;
}
// The accumulator is the result from the callback call which just occured.
const r = ArrayReduceRightLoopContinuation(
jsreceiver, callbackfn, result, jsreceiver, numberK, numberLength);
return r;
}
transitioning builtin ArrayReduceRightLoopContinuation(implicit context:
Context)(
_receiver: JSReceiver, callbackfn: Callable,
initialAccumulator: JSAny|TheHole, o: JSReceiver, initialK: Number,
_length: Number): JSAny {
let accumulator = initialAccumulator;
transitioning builtin ArrayReduceRightLoopContinuation(
implicit context: Context)(
_receiver: JSReceiver, callbackfn: Callable,
initialAccumulator: JSAny|TheHole, o: JSReceiver, initialK: Number,
_length: Number): JSAny {
let accumulator = initialAccumulator;
// 8b and 9. Repeat, while k >= 0
for (let k: Number = initialK; k >= 0; k--) {
// 8b i and 9a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
// 8b and 9. Repeat, while k >= 0
for (let k: Number = initialK; k >= 0; k--) {
// 8b i and 9a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
// 8b ii and 9b. Set kPresent to ? HasProperty(O, Pk).
const present: Boolean = HasProperty_Inline(o, k);
// 8b ii and 9b. Set kPresent to ? HasProperty(O, Pk).
const present: Boolean = HasProperty_Inline(o, k);
// 8b iii and 9c. If kPresent is true, then
if (present == True) {
// 8b iii and 9c i. Let kValue be ? Get(O, Pk).
const value: JSAny = GetProperty(o, k);
// 8b iii and 9c. If kPresent is true, then
if (present == True) {
// 8b iii and 9c i. Let kValue be ? Get(O, Pk).
const value: JSAny = GetProperty(o, k);
typeswitch (accumulator) {
case (TheHole): {
// 8b iii 1.
accumulator = value;
}
case (accumulatorNotHole: JSAny): {
// 9c. ii. Set accumulator to ? Call(callbackfn, undefined,
// <accumulator, kValue, k, O>).
accumulator = Call(
context, callbackfn, Undefined, accumulatorNotHole, value, k,
o);
}
}
}
// 8b iv and 9d. Decrease k by 1. (done by the loop).
}
// 8c. if kPresent is false, throw a TypeError exception.
// If the accumulator is discovered with the sentinel hole value,
// this means kPresent is false.
typeswitch (accumulator) {
case (TheHole): {
ThrowTypeError(
MessageTemplate::kReduceNoInitial, 'Array.prototype.reduceRight');
}
case (accumulator: JSAny): {
return accumulator;
}
}
}
transitioning macro FastArrayReduceRight(implicit context: Context)(
o: JSReceiver, len: Number, callbackfn: Callable,
initialAccumulator: JSAny|TheHole): JSAny
labels Bailout(Number, JSAny|TheHole) {
let accumulator = initialAccumulator;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(len - 1, accumulator);
const fastO = Cast<FastJSArrayForRead>(o)
otherwise goto Bailout(len - 1, accumulator);
let fastOW = NewFastJSArrayForReadWitness(fastO);
// Build a fast loop over the array.
for (let k: Smi = smiLen - 1; k >= 0; k--) {
fastOW.Recheck() otherwise goto Bailout(k, accumulator);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k, accumulator);
const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue;
typeswitch (accumulator) {
case (TheHole): {
// 8b iii 1.
accumulator = value;
}
case (accumulatorNotHole: JSAny): {
// 9c. ii. Set accumulator to ? Call(callbackfn, undefined,
// <accumulator, kValue, k, O>).
accumulator = Call(
context, callbackfn, Undefined, accumulatorNotHole, value, k,
fastOW.Get());
context, callbackfn, Undefined, accumulatorNotHole, value, k, o);
}
}
}
typeswitch (accumulator) {
case (TheHole): {
ThrowTypeError(
MessageTemplate::kReduceNoInitial, 'Array.prototype.reduceRight');
}
case (accumulator: JSAny): {
return accumulator;
}
}
// 8b iv and 9d. Decrease k by 1. (done by the loop).
}
// https://tc39.github.io/ecma262/#sec-array.prototype.reduceRight
transitioning javascript builtin
ArrayReduceRight(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.reduceRight');
// 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);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto NoCallableError;
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise NoCallableError;
// 4. If len is 0 and initialValue is not present, throw a TypeError
// exception. (This case is handled at the end of
// ArrayReduceRightLoopContinuation).
const initialValue: JSAny|TheHole =
arguments.length > 1 ? arguments[1] : TheHole;
try {
return FastArrayReduceRight(o, len, callbackfn, initialValue)
otherwise Bailout;
} label Bailout(value: Number, accumulator: JSAny|TheHole) {
return ArrayReduceRightLoopContinuation(
o, callbackfn, accumulator, o, value, len);
}
} label NoCallableError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
// 8c. if kPresent is false, throw a TypeError exception.
// If the accumulator is discovered with the sentinel hole value,
// this means kPresent is false.
typeswitch (accumulator) {
case (TheHole): {
ThrowTypeError(
MessageTemplate::kReduceNoInitial, 'Array.prototype.reduceRight');
}
case (accumulator: JSAny): {
return accumulator;
}
}
}
transitioning macro FastArrayReduceRight(implicit context: Context)(
o: JSReceiver, len: Number, callbackfn: Callable,
initialAccumulator: JSAny|TheHole): JSAny
labels Bailout(Number, JSAny | TheHole) {
let accumulator = initialAccumulator;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(len - 1, accumulator);
const fastO = Cast<FastJSArrayForRead>(o)
otherwise goto Bailout(len - 1, accumulator);
let fastOW = NewFastJSArrayForReadWitness(fastO);
// Build a fast loop over the array.
for (let k: Smi = smiLen - 1; k >= 0; k--) {
fastOW.Recheck() otherwise goto Bailout(k, accumulator);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k, accumulator);
const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue;
typeswitch (accumulator) {
case (TheHole): {
accumulator = value;
}
case (accumulatorNotHole: JSAny): {
accumulator = Call(
context, callbackfn, Undefined, accumulatorNotHole, value, k,
fastOW.Get());
}
}
}
typeswitch (accumulator) {
case (TheHole): {
ThrowTypeError(
MessageTemplate::kReduceNoInitial, 'Array.prototype.reduceRight');
}
case (accumulator: JSAny): {
return accumulator;
}
}
}
// https://tc39.github.io/ecma262/#sec-array.prototype.reduceRight
transitioning javascript builtin
ArrayReduceRight(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.reduceRight');
// 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);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto NoCallableError;
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise NoCallableError;
// 4. If len is 0 and initialValue is not present, throw a TypeError
// exception. (This case is handled at the end of
// ArrayReduceRightLoopContinuation).
const initialValue: JSAny|TheHole =
arguments.length > 1 ? arguments[1] : TheHole;
try {
return FastArrayReduceRight(o, len, callbackfn, initialValue)
otherwise Bailout;
} label Bailout(value: Number, accumulator: JSAny|TheHole) {
return ArrayReduceRightLoopContinuation(
o, callbackfn, accumulator, o, value, len);
}
} label NoCallableError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
}
}
}

View File

@ -3,195 +3,194 @@
// found in the LICENSE file.
namespace array {
transitioning javascript builtin
ArrayReducePreLoopEagerDeoptContinuation(
js-implicit context: NativeContext,
receiver: JSAny)(callback: JSAny, length: JSAny): JSAny {
// All continuation points in the optimized every implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
transitioning javascript builtin
ArrayReducePreLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, length: JSAny): JSAny {
// All continuation points in the optimized every implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
// Simulate starting the loop at 0, but ensuring that the accumulator is
// the hole. The continuation stub will search for the initial non-hole
// element, rightly throwing an exception if not found.
return ArrayReduceLoopContinuation(
jsreceiver, callbackfn, TheHole, jsreceiver, 0, numberLength);
}
// Simulate starting the loop at 0, but ensuring that the accumulator is
// the hole. The continuation stub will search for the initial non-hole
// element, rightly throwing an exception if not found.
return ArrayReduceLoopContinuation(
jsreceiver, callbackfn, TheHole, jsreceiver, 0, numberLength);
}
transitioning javascript builtin
ArrayReduceLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, initialK: JSAny, length: JSAny,
accumulator: JSAny): JSAny {
// All continuation points in the optimized every implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
transitioning javascript builtin
ArrayReduceLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, initialK: JSAny, length: JSAny,
accumulator: JSAny): JSAny {
// All continuation points in the optimized every implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
return ArrayReduceLoopContinuation(
jsreceiver, callbackfn, accumulator, jsreceiver, numberK, numberLength);
}
return ArrayReduceLoopContinuation(
jsreceiver, callbackfn, accumulator, jsreceiver, numberK, numberLength);
}
transitioning javascript builtin
ArrayReduceLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, initialK: JSAny, length: JSAny, result: JSAny): JSAny {
// All continuation points in the optimized every implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
transitioning javascript builtin
ArrayReduceLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, initialK: JSAny, length: JSAny, result: JSAny): JSAny {
// All continuation points in the optimized every implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
// The accumulator is the result from the callback call which just occured.
const r = ArrayReduceLoopContinuation(
jsreceiver, callbackfn, result, jsreceiver, numberK, numberLength);
return r;
}
// The accumulator is the result from the callback call which just occured.
const r = ArrayReduceLoopContinuation(
jsreceiver, callbackfn, result, jsreceiver, numberK, numberLength);
return r;
}
transitioning builtin ArrayReduceLoopContinuation(implicit context: Context)(
_receiver: JSReceiver, callbackfn: Callable,
initialAccumulator: JSAny|TheHole, o: JSReceiver, initialK: Number,
length: Number): JSAny {
let accumulator = initialAccumulator;
transitioning builtin ArrayReduceLoopContinuation(implicit context: Context)(
_receiver: JSReceiver, callbackfn: Callable,
initialAccumulator: JSAny|TheHole, o: JSReceiver, initialK: Number,
length: Number): JSAny {
let accumulator = initialAccumulator;
// 8b and 9. Repeat, while k < len
for (let k: Number = initialK; k < length; k++) {
// 8b i and 9a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
// 8b and 9. Repeat, while k < len
for (let k: Number = initialK; k < length; k++) {
// 8b i and 9a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
// 8b ii and 9b. Set kPresent to ? HasProperty(O, Pk).
const present: Boolean = HasProperty_Inline(o, k);
// 8b ii and 9b. Set kPresent to ? HasProperty(O, Pk).
const present: Boolean = HasProperty_Inline(o, k);
// 6c. If kPresent is true, then
if (present == True) {
// 6c. i. Let kValue be ? Get(O, Pk).
const value: JSAny = GetProperty(o, k);
// 6c. If kPresent is true, then
if (present == True) {
// 6c. i. Let kValue be ? Get(O, Pk).
const value: JSAny = GetProperty(o, k);
typeswitch (accumulator) {
case (TheHole): {
// 8b.
accumulator = value;
}
case (accumulatorNotHole: JSAny): {
// 9c. ii. Set accumulator to ? Call(callbackfn, undefined,
// <accumulator, kValue, k, O>).
accumulator = Call(
context, callbackfn, Undefined, accumulatorNotHole, value, k,
o);
}
}
}
// 8b iv and 9d. Increase k by 1. (done by the loop).
}
// 8c. if kPresent is false, throw a TypeError exception.
// If the accumulator is discovered with the sentinel hole value,
// this means kPresent is false.
typeswitch (accumulator) {
case (TheHole): {
ThrowTypeError(
MessageTemplate::kReduceNoInitial, 'Array.prototype.reduce');
}
case (accumulator: JSAny): {
return accumulator;
}
}
}
transitioning macro FastArrayReduce(implicit context: Context)(
o: JSReceiver, len: Number, callbackfn: Callable,
initialAccumulator: JSAny|TheHole): JSAny
labels Bailout(Number, JSAny|TheHole) {
const k = 0;
let accumulator = initialAccumulator;
Cast<Smi>(len) otherwise goto Bailout(k, accumulator);
const fastO =
Cast<FastJSArrayForRead>(o) otherwise goto Bailout(k, accumulator);
let fastOW = NewFastJSArrayForReadWitness(fastO);
// Build a fast loop over the array.
for (let k: Smi = 0; k < len; k++) {
fastOW.Recheck() otherwise goto Bailout(k, accumulator);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k, accumulator);
const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue;
typeswitch (accumulator) {
case (TheHole): {
// 8b.
accumulator = value;
}
case (accumulatorNotHole: JSAny): {
// 9c. ii. Set accumulator to ? Call(callbackfn, undefined,
// <accumulator, kValue, k, O>).
accumulator = Call(
context, callbackfn, Undefined, accumulatorNotHole, value, k,
fastOW.Get());
context, callbackfn, Undefined, accumulatorNotHole, value, k, o);
}
}
}
typeswitch (accumulator) {
case (TheHole): {
ThrowTypeError(
MessageTemplate::kReduceNoInitial, 'Array.prototype.reduce');
}
case (accumulator: JSAny): {
return accumulator;
}
}
// 8b iv and 9d. Increase k by 1. (done by the loop).
}
// https://tc39.github.io/ecma262/#sec-array.prototype.reduce
transitioning javascript builtin
ArrayReduce(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.reduce');
// 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);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto NoCallableError;
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise NoCallableError;
// 4. If len is 0 and initialValue is not present, throw a TypeError
// exception. (This case is handled at the end of
// ArrayReduceLoopContinuation).
const initialValue: JSAny|TheHole =
arguments.length > 1 ? arguments[1] : TheHole;
try {
return FastArrayReduce(o, len, callbackfn, initialValue)
otherwise Bailout;
} label Bailout(value: Number, accumulator: JSAny|TheHole) {
return ArrayReduceLoopContinuation(
o, callbackfn, accumulator, o, value, len);
}
} label NoCallableError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
// 8c. if kPresent is false, throw a TypeError exception.
// If the accumulator is discovered with the sentinel hole value,
// this means kPresent is false.
typeswitch (accumulator) {
case (TheHole): {
ThrowTypeError(
MessageTemplate::kReduceNoInitial, 'Array.prototype.reduce');
}
case (accumulator: JSAny): {
return accumulator;
}
}
}
transitioning macro FastArrayReduce(implicit context: Context)(
o: JSReceiver, len: Number, callbackfn: Callable,
initialAccumulator: JSAny|TheHole): JSAny
labels Bailout(Number, JSAny | TheHole) {
const k = 0;
let accumulator = initialAccumulator;
Cast<Smi>(len) otherwise goto Bailout(k, accumulator);
const fastO =
Cast<FastJSArrayForRead>(o) otherwise goto Bailout(k, accumulator);
let fastOW = NewFastJSArrayForReadWitness(fastO);
// Build a fast loop over the array.
for (let k: Smi = 0; k < len; k++) {
fastOW.Recheck() otherwise goto Bailout(k, accumulator);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k, accumulator);
const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue;
typeswitch (accumulator) {
case (TheHole): {
accumulator = value;
}
case (accumulatorNotHole: JSAny): {
accumulator = Call(
context, callbackfn, Undefined, accumulatorNotHole, value, k,
fastOW.Get());
}
}
}
typeswitch (accumulator) {
case (TheHole): {
ThrowTypeError(
MessageTemplate::kReduceNoInitial, 'Array.prototype.reduce');
}
case (accumulator: JSAny): {
return accumulator;
}
}
}
// https://tc39.github.io/ecma262/#sec-array.prototype.reduce
transitioning javascript builtin
ArrayReduce(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.reduce');
// 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);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto NoCallableError;
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise NoCallableError;
// 4. If len is 0 and initialValue is not present, throw a TypeError
// exception. (This case is handled at the end of
// ArrayReduceLoopContinuation).
const initialValue: JSAny|TheHole =
arguments.length > 1 ? arguments[1] : TheHole;
try {
return FastArrayReduce(o, len, callbackfn, initialValue)
otherwise Bailout;
} label Bailout(value: Number, accumulator: JSAny|TheHole) {
return ArrayReduceLoopContinuation(
o, callbackfn, accumulator, o, value, len);
}
} label NoCallableError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
}
}
}

View File

@ -3,174 +3,170 @@
// found in the LICENSE file.
namespace array {
macro LoadElement<ElementsAccessor : type extends ElementsKind, T: type>(
elements: FixedArrayBase, index: Smi): T;
macro LoadElement<ElementsAccessor : type extends ElementsKind, T: type>(
elements: FixedArrayBase, index: Smi): T;
LoadElement<array::FastPackedSmiElements, Smi>(implicit context: Context)(
elements: FixedArrayBase, index: Smi): Smi {
const elements: FixedArray = UnsafeCast<FixedArray>(elements);
return UnsafeCast<Smi>(elements.objects[index]);
}
LoadElement<array::FastPackedSmiElements, Smi>(implicit context: Context)(
elements: FixedArrayBase, index: Smi): Smi {
const elements: FixedArray = UnsafeCast<FixedArray>(elements);
return UnsafeCast<Smi>(elements.objects[index]);
}
LoadElement<array::FastPackedObjectElements, JSAny>(
implicit context: Context)(elements: FixedArrayBase, index: Smi): JSAny {
const elements: FixedArray = UnsafeCast<FixedArray>(elements);
return UnsafeCast<JSAny>(elements.objects[index]);
}
LoadElement<array::FastPackedObjectElements, JSAny>(implicit context: Context)(
elements: FixedArrayBase, index: Smi): JSAny {
const elements: FixedArray = UnsafeCast<FixedArray>(elements);
return UnsafeCast<JSAny>(elements.objects[index]);
}
LoadElement<array::FastPackedDoubleElements, float64>(
implicit context: Context)(elements: FixedArrayBase, index: Smi):
float64 {
const elements: FixedDoubleArray = UnsafeCast<FixedDoubleArray>(elements);
// This macro is only used for PACKED_DOUBLE, loading the hole should
// be impossible.
return elements.floats[index].Value() otherwise unreachable;
}
LoadElement<array::FastPackedDoubleElements, float64>(
implicit context: Context)(elements: FixedArrayBase, index: Smi): float64 {
const elements: FixedDoubleArray = UnsafeCast<FixedDoubleArray>(elements);
// This macro is only used for PACKED_DOUBLE, loading the hole should
// be impossible.
return elements.floats[index].Value() otherwise unreachable;
}
macro StoreElement<ElementsAccessor : type extends ElementsKind, T: type>(
implicit context:
Context)(elements: FixedArrayBase, index: Smi, value: T);
macro StoreElement<ElementsAccessor : type extends ElementsKind, T: type>(
implicit context: Context)(elements: FixedArrayBase, index: Smi, value: T);
StoreElement<array::FastPackedSmiElements, Smi>(implicit context: Context)(
elements: FixedArrayBase, index: Smi, value: Smi) {
const elems: FixedArray = UnsafeCast<FixedArray>(elements);
StoreFixedArrayElement(elems, index, value, SKIP_WRITE_BARRIER);
}
StoreElement<array::FastPackedSmiElements, Smi>(implicit context: Context)(
elements: FixedArrayBase, index: Smi, value: Smi) {
const elems: FixedArray = UnsafeCast<FixedArray>(elements);
StoreFixedArrayElement(elems, index, value, SKIP_WRITE_BARRIER);
}
StoreElement<array::FastPackedObjectElements, JSAny>(
implicit context:
Context)(elements: FixedArrayBase, index: Smi, value: JSAny) {
const elements: FixedArray = UnsafeCast<FixedArray>(elements);
elements.objects[index] = value;
}
StoreElement<array::FastPackedObjectElements, JSAny>(implicit context: Context)(
elements: FixedArrayBase, index: Smi, value: JSAny) {
const elements: FixedArray = UnsafeCast<FixedArray>(elements);
elements.objects[index] = value;
}
StoreElement<array::FastPackedDoubleElements, float64>(
implicit context:
Context)(elements: FixedArrayBase, index: Smi, value: float64) {
const elems: FixedDoubleArray = UnsafeCast<FixedDoubleArray>(elements);
StoreFixedDoubleArrayElementSmi(elems, index, value);
}
StoreElement<array::FastPackedDoubleElements, float64>(
implicit context: Context)(
elements: FixedArrayBase, index: Smi, value: float64) {
const elems: FixedDoubleArray = UnsafeCast<FixedDoubleArray>(elements);
StoreFixedDoubleArrayElementSmi(elems, index, value);
}
// Fast-path for all PACKED_* elements kinds. These do not need to check
// whether a property is present, so we can simply swap them using fast
// FixedArray loads/stores.
macro FastPackedArrayReverse<Accessor: type, T: type>(
implicit context: Context)(elements: FixedArrayBase, length: Smi) {
let lower: Smi = 0;
let upper: Smi = length - 1;
// Fast-path for all PACKED_* elements kinds. These do not need to check
// whether a property is present, so we can simply swap them using fast
// FixedArray loads/stores.
macro FastPackedArrayReverse<Accessor: type, T: type>(
implicit context: Context)(elements: FixedArrayBase, length: Smi) {
let lower: Smi = 0;
let upper: Smi = length - 1;
while (lower < upper) {
const lowerValue: T = LoadElement<Accessor, T>(elements, lower);
const upperValue: T = LoadElement<Accessor, T>(elements, upper);
StoreElement<Accessor>(elements, lower, upperValue);
StoreElement<Accessor>(elements, upper, lowerValue);
++lower;
--upper;
}
}
transitioning macro GenericArrayReverse(context: Context, receiver: JSAny):
JSAny {
// 1. Let O be ? ToObject(this value).
const object: JSReceiver = ToObject_Inline(context, receiver);
// 2. Let len be ? ToLength(? Get(O, "length")).
const length: Number = GetLengthProperty(object);
// 3. Let middle be floor(len / 2).
// 4. Let lower be 0.
// 5. Repeat, while lower != middle.
// a. Let upper be len - lower - 1.
// Instead of calculating the middle value, we simply initialize upper
// with len - 1 and decrement it after each iteration.
let lower: Number = 0;
let upper: Number = length - 1;
while (lower < upper) {
let lowerValue: JSAny = Undefined;
let upperValue: JSAny = Undefined;
// b. Let upperP be ! ToString(upper).
// c. Let lowerP be ! ToString(lower).
// d. Let lowerExists be ? HasProperty(O, lowerP).
const lowerExists: Boolean = HasProperty(object, lower);
// e. If lowerExists is true, then.
if (lowerExists == True) {
// i. Let lowerValue be ? Get(O, lowerP).
lowerValue = GetProperty(object, lower);
}
// f. Let upperExists be ? HasProperty(O, upperP).
const upperExists: Boolean = HasProperty(object, upper);
// g. If upperExists is true, then.
if (upperExists == True) {
// i. Let upperValue be ? Get(O, upperP).
upperValue = GetProperty(object, upper);
}
// h. If lowerExists is true and upperExists is true, then
if (lowerExists == True && upperExists == True) {
// i. Perform ? Set(O, lowerP, upperValue, true).
SetProperty(object, lower, upperValue);
// ii. Perform ? Set(O, upperP, lowerValue, true).
SetProperty(object, upper, lowerValue);
} else if (lowerExists == False && upperExists == True) {
// i. Perform ? Set(O, lowerP, upperValue, true).
SetProperty(object, lower, upperValue);
// ii. Perform ? DeletePropertyOrThrow(O, upperP).
DeleteProperty(object, upper, LanguageMode::kStrict);
} else if (lowerExists == True && upperExists == False) {
// i. Perform ? DeletePropertyOrThrow(O, lowerP).
DeleteProperty(object, lower, LanguageMode::kStrict);
// ii. Perform ? Set(O, upperP, lowerValue, true).
SetProperty(object, upper, lowerValue);
}
// l. Increase lower by 1.
++lower;
--upper;
}
// 6. Return O.
return object;
}
macro TryFastPackedArrayReverse(implicit context: Context)(receiver: JSAny)
labels Slow {
const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
const kind: ElementsKind = array.map.elements_kind;
if (kind == ElementsKind::PACKED_SMI_ELEMENTS) {
array::EnsureWriteableFastElements(array);
FastPackedArrayReverse<array::FastPackedSmiElements, Smi>(
array.elements, array.length);
} else if (kind == ElementsKind::PACKED_ELEMENTS) {
array::EnsureWriteableFastElements(array);
FastPackedArrayReverse<array::FastPackedObjectElements, JSAny>(
array.elements, array.length);
} else if (kind == ElementsKind::PACKED_DOUBLE_ELEMENTS) {
FastPackedArrayReverse<array::FastPackedDoubleElements, float64>(
array.elements, array.length);
} else {
goto Slow;
}
}
// https://tc39.github.io/ecma262/#sec-array.prototype.reverse
transitioning javascript builtin ArrayPrototypeReverse(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSAny {
try {
TryFastPackedArrayReverse(receiver) otherwise Baseline;
return receiver;
} label Baseline {
return GenericArrayReverse(context, receiver);
}
while (lower < upper) {
const lowerValue: T = LoadElement<Accessor, T>(elements, lower);
const upperValue: T = LoadElement<Accessor, T>(elements, upper);
StoreElement<Accessor>(elements, lower, upperValue);
StoreElement<Accessor>(elements, upper, lowerValue);
++lower;
--upper;
}
}
transitioning macro GenericArrayReverse(
context: Context, receiver: JSAny): JSAny {
// 1. Let O be ? ToObject(this value).
const object: JSReceiver = ToObject_Inline(context, receiver);
// 2. Let len be ? ToLength(? Get(O, "length")).
const length: Number = GetLengthProperty(object);
// 3. Let middle be floor(len / 2).
// 4. Let lower be 0.
// 5. Repeat, while lower != middle.
// a. Let upper be len - lower - 1.
// Instead of calculating the middle value, we simply initialize upper
// with len - 1 and decrement it after each iteration.
let lower: Number = 0;
let upper: Number = length - 1;
while (lower < upper) {
let lowerValue: JSAny = Undefined;
let upperValue: JSAny = Undefined;
// b. Let upperP be ! ToString(upper).
// c. Let lowerP be ! ToString(lower).
// d. Let lowerExists be ? HasProperty(O, lowerP).
const lowerExists: Boolean = HasProperty(object, lower);
// e. If lowerExists is true, then.
if (lowerExists == True) {
// i. Let lowerValue be ? Get(O, lowerP).
lowerValue = GetProperty(object, lower);
}
// f. Let upperExists be ? HasProperty(O, upperP).
const upperExists: Boolean = HasProperty(object, upper);
// g. If upperExists is true, then.
if (upperExists == True) {
// i. Let upperValue be ? Get(O, upperP).
upperValue = GetProperty(object, upper);
}
// h. If lowerExists is true and upperExists is true, then
if (lowerExists == True && upperExists == True) {
// i. Perform ? Set(O, lowerP, upperValue, true).
SetProperty(object, lower, upperValue);
// ii. Perform ? Set(O, upperP, lowerValue, true).
SetProperty(object, upper, lowerValue);
} else if (lowerExists == False && upperExists == True) {
// i. Perform ? Set(O, lowerP, upperValue, true).
SetProperty(object, lower, upperValue);
// ii. Perform ? DeletePropertyOrThrow(O, upperP).
DeleteProperty(object, upper, LanguageMode::kStrict);
} else if (lowerExists == True && upperExists == False) {
// i. Perform ? DeletePropertyOrThrow(O, lowerP).
DeleteProperty(object, lower, LanguageMode::kStrict);
// ii. Perform ? Set(O, upperP, lowerValue, true).
SetProperty(object, upper, lowerValue);
}
// l. Increase lower by 1.
++lower;
--upper;
}
// 6. Return O.
return object;
}
macro TryFastPackedArrayReverse(implicit context: Context)(receiver: JSAny)
labels Slow {
const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
const kind: ElementsKind = array.map.elements_kind;
if (kind == ElementsKind::PACKED_SMI_ELEMENTS) {
array::EnsureWriteableFastElements(array);
FastPackedArrayReverse<array::FastPackedSmiElements, Smi>(
array.elements, array.length);
} else if (kind == ElementsKind::PACKED_ELEMENTS) {
array::EnsureWriteableFastElements(array);
FastPackedArrayReverse<array::FastPackedObjectElements, JSAny>(
array.elements, array.length);
} else if (kind == ElementsKind::PACKED_DOUBLE_ELEMENTS) {
FastPackedArrayReverse<array::FastPackedDoubleElements, float64>(
array.elements, array.length);
} else {
goto Slow;
}
}
// https://tc39.github.io/ecma262/#sec-array.prototype.reverse
transitioning javascript builtin ArrayPrototypeReverse(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
try {
TryFastPackedArrayReverse(receiver) otherwise Baseline;
return receiver;
} label Baseline {
return GenericArrayReverse(context, receiver);
}
}
}

View File

@ -3,108 +3,107 @@
// found in the LICENSE file.
namespace array {
extern builtin ArrayShift(Context, JSFunction, JSAny, int32): JSAny;
extern builtin ArrayShift(Context, JSFunction, JSAny, int32): JSAny;
macro TryFastArrayShift(implicit context: Context)(receiver: JSAny): JSAny
labels Slow, Runtime {
const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
let witness = NewFastJSArrayWitness(array);
macro TryFastArrayShift(implicit context: Context)(receiver: JSAny): JSAny
labels Slow, Runtime {
const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
let witness = NewFastJSArrayWitness(array);
witness.EnsureArrayPushable() otherwise Slow;
witness.EnsureArrayPushable() otherwise Slow;
if (array.length == 0) {
return Undefined;
}
const newLength = array.length - 1;
// Check that we're not supposed to right-trim the backing store, as
// implemented in elements.cc:ElementsAccessorBase::SetLengthImpl.
if ((newLength + newLength + kMinAddedElementsCapacity) <
array.elements.length) {
goto Runtime;
}
// Check that we're not supposed to left-trim the backing store, as
// implemented in elements.cc:FastElementsAccessor::MoveElements.
if (newLength > kMaxCopyElements) goto Runtime;
const result = witness.LoadElementOrUndefined(0);
witness.ChangeLength(newLength);
witness.MoveElements(0, 1, Convert<intptr>(newLength));
witness.StoreHole(newLength);
return result;
if (array.length == 0) {
return Undefined;
}
transitioning macro GenericArrayShift(implicit context:
Context)(receiver: JSAny): JSAny {
// 1. Let O be ? ToObject(this value).
const object: JSReceiver = ToObject_Inline(context, receiver);
const newLength = array.length - 1;
// 2. Let len be ? ToLength(? Get(O, "length")).
const length: Number = GetLengthProperty(object);
// 3. If len is zero, then
if (length == 0) {
// a. Perform ? Set(O, "length", 0, true).
SetProperty(object, kLengthString, Convert<Smi>(0));
// b. Return undefined.
return Undefined;
}
// 4. Let first be ? Get(O, "0").
const first = GetProperty(object, Convert<Smi>(0));
// 5. Let k be 1.
let k: Number = 1;
// 6. Repeat, while k < len
while (k < length) {
// a. Let from be ! ToString(k).
const from: Number = k;
// b. Let to be ! ToString(k - 1).
const to: Number = k - 1;
// c. Let fromPresent be ? HasProperty(O, from).
const fromPresent: Boolean = HasProperty(object, from);
// d. If fromPresent is true, then
if (fromPresent == True) {
// i. Let fromVal be ? Get(O, from).
const fromValue: JSAny = GetProperty(object, from);
// ii. Perform ? Set(O, to, fromValue, true).
SetProperty(object, to, fromValue);
} else {
// i. Perform ? DeletePropertyOrThrow(O, to).
DeleteProperty(object, to, LanguageMode::kStrict);
}
// f. Increase k by 1.
k++;
}
// 7. Perform ? DeletePropertyOrThrow(O, ! ToString(len - 1)).
DeleteProperty(object, length - 1, LanguageMode::kStrict);
// 8. Perform ? Set(O, "length", len - 1, true).
SetProperty(object, kLengthString, length - 1);
// 9. Return first.
return first;
// Check that we're not supposed to right-trim the backing store, as
// implemented in elements.cc:ElementsAccessorBase::SetLengthImpl.
if ((newLength + newLength + kMinAddedElementsCapacity) <
array.elements.length) {
goto Runtime;
}
// https://tc39.github.io/ecma262/#sec-array.prototype.shift
transitioning javascript builtin ArrayPrototypeShift(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSAny {
try {
return TryFastArrayShift(receiver) otherwise Slow, Runtime;
} label Slow {
return GenericArrayShift(receiver);
} label Runtime {
tail ArrayShift(
context, LoadTargetFromFrame(), Undefined,
Convert<int32>(arguments.length));
// Check that we're not supposed to left-trim the backing store, as
// implemented in elements.cc:FastElementsAccessor::MoveElements.
if (newLength > kMaxCopyElements) goto Runtime;
const result = witness.LoadElementOrUndefined(0);
witness.ChangeLength(newLength);
witness.MoveElements(0, 1, Convert<intptr>(newLength));
witness.StoreHole(newLength);
return result;
}
transitioning macro GenericArrayShift(implicit context: Context)(
receiver: JSAny): JSAny {
// 1. Let O be ? ToObject(this value).
const object: JSReceiver = ToObject_Inline(context, receiver);
// 2. Let len be ? ToLength(? Get(O, "length")).
const length: Number = GetLengthProperty(object);
// 3. If len is zero, then
if (length == 0) {
// a. Perform ? Set(O, "length", 0, true).
SetProperty(object, kLengthString, Convert<Smi>(0));
// b. Return undefined.
return Undefined;
}
// 4. Let first be ? Get(O, "0").
const first = GetProperty(object, Convert<Smi>(0));
// 5. Let k be 1.
let k: Number = 1;
// 6. Repeat, while k < len
while (k < length) {
// a. Let from be ! ToString(k).
const from: Number = k;
// b. Let to be ! ToString(k - 1).
const to: Number = k - 1;
// c. Let fromPresent be ? HasProperty(O, from).
const fromPresent: Boolean = HasProperty(object, from);
// d. If fromPresent is true, then
if (fromPresent == True) {
// i. Let fromVal be ? Get(O, from).
const fromValue: JSAny = GetProperty(object, from);
// ii. Perform ? Set(O, to, fromValue, true).
SetProperty(object, to, fromValue);
} else {
// i. Perform ? DeletePropertyOrThrow(O, to).
DeleteProperty(object, to, LanguageMode::kStrict);
}
// f. Increase k by 1.
k++;
}
// 7. Perform ? DeletePropertyOrThrow(O, ! ToString(len - 1)).
DeleteProperty(object, length - 1, LanguageMode::kStrict);
// 8. Perform ? Set(O, "length", len - 1, true).
SetProperty(object, kLengthString, length - 1);
// 9. Return first.
return first;
}
// https://tc39.github.io/ecma262/#sec-array.prototype.shift
transitioning javascript builtin ArrayPrototypeShift(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
try {
return TryFastArrayShift(receiver) otherwise Slow, Runtime;
} label Slow {
return GenericArrayShift(receiver);
} label Runtime {
tail ArrayShift(
context, LoadTargetFromFrame(), Undefined,
Convert<int32>(arguments.length));
}
}
}

View File

@ -3,223 +3,222 @@
// found in the LICENSE file.
namespace array {
macro HandleSimpleArgumentsSlice(
context: NativeContext, args: JSArgumentsObjectWithLength, start: Smi,
count: Smi): JSArray
labels Bailout {
// If the resulting array doesn't fit in new space, use the slow path.
if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout;
macro HandleSimpleArgumentsSlice(
context: NativeContext, args: JSArgumentsObjectWithLength, start: Smi,
count: Smi): JSArray
labels Bailout {
// If the resulting array doesn't fit in new space, use the slow path.
if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout;
const end: Smi = start + count;
const sourceElements: FixedArray =
Cast<FixedArray>(args.elements) otherwise Bailout;
if (SmiAbove(end, sourceElements.length)) goto Bailout;
const end: Smi = start + count;
const sourceElements: FixedArray =
Cast<FixedArray>(args.elements) otherwise Bailout;
if (SmiAbove(end, sourceElements.length)) goto Bailout;
const arrayMap: Map =
LoadJSArrayElementsMap(ElementsKind::HOLEY_ELEMENTS, context);
const result: JSArray =
AllocateJSArray(ElementsKind::HOLEY_ELEMENTS, arrayMap, count, count);
const newElements: FixedArray =
Cast<FixedArray>(result.elements) otherwise Bailout;
CopyElements(
ElementsKind::PACKED_ELEMENTS, newElements, 0, sourceElements,
Convert<intptr>(start), Convert<intptr>(count));
return result;
const arrayMap: Map =
LoadJSArrayElementsMap(ElementsKind::HOLEY_ELEMENTS, context);
const result: JSArray =
AllocateJSArray(ElementsKind::HOLEY_ELEMENTS, arrayMap, count, count);
const newElements: FixedArray =
Cast<FixedArray>(result.elements) otherwise Bailout;
CopyElements(
ElementsKind::PACKED_ELEMENTS, newElements, 0, sourceElements,
Convert<intptr>(start), Convert<intptr>(count));
return result;
}
macro HandleFastAliasedSloppyArgumentsSlice(
context: NativeContext, args: JSArgumentsObjectWithLength, start: Smi,
count: Smi): JSArray
labels Bailout {
// If the resulting array doesn't fit in new space, use the slow path.
if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout;
const sloppyElements: SloppyArgumentsElements =
Cast<SloppyArgumentsElements>(args.elements) otherwise Bailout;
const sloppyElementsLength: Smi = sloppyElements.length;
const parameterMapLength: Smi =
sloppyElementsLength - kSloppyArgumentsParameterMapStart;
// Check to make sure that the extraction will not access outside the
// defined arguments
const end: Smi = start + count;
const unmappedElements: FixedArray =
Cast<FixedArray>(sloppyElements.objects[kSloppyArgumentsArgumentsIndex])
otherwise Bailout;
const unmappedElementsLength: Smi = unmappedElements.length;
if (SmiAbove(end, unmappedElementsLength)) goto Bailout;
const argumentsContext: Context =
UnsafeCast<Context>(sloppyElements.objects[kSloppyArgumentsContextIndex]);
const arrayMap: Map =
LoadJSArrayElementsMap(ElementsKind::HOLEY_ELEMENTS, context);
const result: JSArray =
AllocateJSArray(ElementsKind::HOLEY_ELEMENTS, arrayMap, count, count);
let indexOut: Smi = 0;
const resultElements: FixedArray = UnsafeCast<FixedArray>(result.elements);
const to: Smi = SmiMin(parameterMapLength, end);
// Fill in the part of the result that map to context-mapped parameters.
for (let current: Smi = start; current < to; ++current) {
const e: Object =
sloppyElements.objects[current + kSloppyArgumentsParameterMapStart];
const newElement = UnsafeCast<(JSAny | TheHole)>(
e != TheHole ? argumentsContext[UnsafeCast<Smi>(e)] :
unmappedElements.objects[current]);
// It is safe to skip the write barrier here because resultElements was
// allocated together with result in a folded allocation.
// TODO(tebbi): The verification of this fails at the moment due to
// missing load elimination.
StoreFixedArrayElement(
resultElements, indexOut++, newElement, UNSAFE_SKIP_WRITE_BARRIER);
}
macro HandleFastAliasedSloppyArgumentsSlice(
context: NativeContext, args: JSArgumentsObjectWithLength, start: Smi,
count: Smi): JSArray
labels Bailout {
// If the resulting array doesn't fit in new space, use the slow path.
if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout;
// Fill in the rest of the result that contains the unmapped parameters
// above the formal parameters.
const unmappedFrom: Smi = SmiMin(SmiMax(parameterMapLength, start), end);
const restCount: Smi = end - unmappedFrom;
CopyElements(
ElementsKind::PACKED_ELEMENTS, resultElements, Convert<intptr>(indexOut),
unmappedElements, Convert<intptr>(unmappedFrom),
Convert<intptr>(restCount));
return result;
}
const sloppyElements: SloppyArgumentsElements =
Cast<SloppyArgumentsElements>(args.elements) otherwise Bailout;
const sloppyElementsLength: Smi = sloppyElements.length;
const parameterMapLength: Smi =
sloppyElementsLength - kSloppyArgumentsParameterMapStart;
macro HandleFastSlice(
context: NativeContext, o: JSAny, startNumber: Number,
countNumber: Number): JSArray
labels Bailout {
const start: Smi = Cast<Smi>(startNumber) otherwise Bailout;
const count: Smi = Cast<Smi>(countNumber) otherwise Bailout;
assert(start >= 0);
// Check to make sure that the extraction will not access outside the
// defined arguments
const end: Smi = start + count;
const unmappedElements: FixedArray =
Cast<FixedArray>(sloppyElements.objects[kSloppyArgumentsArgumentsIndex])
otherwise Bailout;
const unmappedElementsLength: Smi = unmappedElements.length;
if (SmiAbove(end, unmappedElementsLength)) goto Bailout;
const argumentsContext: Context = UnsafeCast<Context>(
sloppyElements.objects[kSloppyArgumentsContextIndex]);
const arrayMap: Map =
LoadJSArrayElementsMap(ElementsKind::HOLEY_ELEMENTS, context);
const result: JSArray =
AllocateJSArray(ElementsKind::HOLEY_ELEMENTS, arrayMap, count, count);
let indexOut: Smi = 0;
const resultElements: FixedArray = UnsafeCast<FixedArray>(result.elements);
const to: Smi = SmiMin(parameterMapLength, end);
// Fill in the part of the result that map to context-mapped parameters.
for (let current: Smi = start; current < to; ++current) {
const e: Object =
sloppyElements.objects[current + kSloppyArgumentsParameterMapStart];
const newElement = UnsafeCast<(JSAny | TheHole)>(
e != TheHole ? argumentsContext[UnsafeCast<Smi>(e)] :
unmappedElements.objects[current]);
// It is safe to skip the write barrier here because resultElements was
// allocated together with result in a folded allocation.
// TODO(tebbi): The verification of this fails at the moment due to
// missing load elimination.
StoreFixedArrayElement(
resultElements, indexOut++, newElement, UNSAFE_SKIP_WRITE_BARRIER);
}
// Fill in the rest of the result that contains the unmapped parameters
// above the formal parameters.
const unmappedFrom: Smi = SmiMin(SmiMax(parameterMapLength, start), end);
const restCount: Smi = end - unmappedFrom;
CopyElements(
ElementsKind::PACKED_ELEMENTS, resultElements,
Convert<intptr>(indexOut), unmappedElements,
Convert<intptr>(unmappedFrom), Convert<intptr>(restCount));
return result;
}
macro HandleFastSlice(
context: NativeContext, o: JSAny, startNumber: Number,
countNumber: Number): JSArray
labels Bailout {
const start: Smi = Cast<Smi>(startNumber) otherwise Bailout;
const count: Smi = Cast<Smi>(countNumber) otherwise Bailout;
assert(start >= 0);
try {
typeswitch (o) {
case (a: FastJSArrayForCopy): {
// It's possible to modify the array length from a valueOf
// callback between the original array length read and this
// point. That can change the length of the array backing store,
// in the worst case, making it smaller than the region that needs
// to be copied out. Therefore, re-check the length before calling
// the appropriate fast path. See regress-785804.js
if (SmiAbove(start + count, a.length)) goto Bailout;
return ExtractFastJSArray(context, a, start, count);
}
case (a: JSStrictArgumentsObject): {
try {
typeswitch (o) {
case (a: FastJSArrayForCopy): {
// It's possible to modify the array length from a valueOf
// callback between the original array length read and this
// point. That can change the length of the array backing store,
// in the worst case, making it smaller than the region that needs
// to be copied out. Therefore, re-check the length before calling
// the appropriate fast path. See regress-785804.js
if (SmiAbove(start + count, a.length)) goto Bailout;
return ExtractFastJSArray(context, a, start, count);
}
case (a: JSStrictArgumentsObject): {
goto HandleSimpleArgumentsSlice(a);
}
case (a: JSSloppyArgumentsObject): {
const map: Map = a.map;
if (IsFastAliasedArgumentsMap(map)) {
return HandleFastAliasedSloppyArgumentsSlice(context, a, start, count)
otherwise Bailout;
} else if (IsSloppyArgumentsMap(map)) {
goto HandleSimpleArgumentsSlice(a);
}
case (a: JSSloppyArgumentsObject): {
const map: Map = a.map;
if (IsFastAliasedArgumentsMap(map)) {
return HandleFastAliasedSloppyArgumentsSlice(
context, a, start, count)
otherwise Bailout;
} else if (IsSloppyArgumentsMap(map)) {
goto HandleSimpleArgumentsSlice(a);
}
goto Bailout;
}
case (JSAny): {
goto Bailout;
}
goto Bailout;
}
} label HandleSimpleArgumentsSlice(a: JSArgumentsObjectWithLength) {
return HandleSimpleArgumentsSlice(context, a, start, count)
otherwise Bailout;
}
}
// https://tc39.github.io/ecma262/#sec-array.prototype.slice
transitioning javascript builtin
ArrayPrototypeSlice(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): JSAny {
// Handle array cloning case if the receiver is a fast array.
if (arguments.length == 0) {
typeswitch (receiver) {
case (a: FastJSArrayForCopy): {
return CloneFastJSArray(context, a);
}
case (JSAny): {
}
case (JSAny): {
goto Bailout;
}
}
// 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);
// 3. Let relativeStart be ? ToInteger(start).
const start: JSAny = arguments[0];
const relativeStart: Number = ToInteger_Inline(start);
// 4. If relativeStart < 0, let k be max((len + relativeStart), 0);
// else let k be min(relativeStart, len).
let k: Number = relativeStart < 0 ? Max((len + relativeStart), 0) :
Min(relativeStart, len);
// 5. If end is undefined, let relativeEnd be len;
// else let relativeEnd be ? ToInteger(end).
const end: JSAny = arguments[1];
const relativeEnd: Number = end == Undefined ? len : ToInteger_Inline(end);
// 6. If relativeEnd < 0, let final be max((len + relativeEnd), 0);
// else let final be min(relativeEnd, len).
const final: Number =
relativeEnd < 0 ? Max((len + relativeEnd), 0) : Min(relativeEnd, len);
// 7. Let count be max(final - k, 0).
const count: Number = Max(final - k, 0);
assert(0 <= k);
assert(k <= len);
assert(0 <= final);
assert(final <= len);
assert(0 <= count);
assert(count <= len);
try {
return HandleFastSlice(context, o, k, count)
otherwise Slow;
} label Slow {}
// 8. Let A be ? ArraySpeciesCreate(O, count).
const a: JSReceiver = ArraySpeciesCreate(context, o, count);
// 9. Let n be 0.
let n: Number = 0;
// 10. Repeat, while k < final
while (k < final) {
// a. Let Pk be ! ToString(k).
const pK: Number = k;
// b. Let kPresent be ? HasProperty(O, Pk).
const fromPresent: Boolean = HasProperty(o, pK);
// c. If kPresent is true, then
if (fromPresent == True) {
// i. Let kValue be ? Get(O, Pk).
const kValue: JSAny = GetProperty(o, pK);
// ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(n), kValue).
FastCreateDataProperty(a, n, kValue);
}
// d. Increase k by 1.
k++;
// e. Increase n by 1.
n++;
}
// 11. Perform ? Set(A, "length", n, true).
SetProperty(a, kLengthString, n);
// 12. Return A.
return a;
} label HandleSimpleArgumentsSlice(a: JSArgumentsObjectWithLength) {
return HandleSimpleArgumentsSlice(context, a, start, count)
otherwise Bailout;
}
}
// https://tc39.github.io/ecma262/#sec-array.prototype.slice
transitioning javascript builtin
ArrayPrototypeSlice(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// Handle array cloning case if the receiver is a fast array.
if (arguments.length == 0) {
typeswitch (receiver) {
case (a: FastJSArrayForCopy): {
return CloneFastJSArray(context, a);
}
case (JSAny): {
}
}
}
// 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);
// 3. Let relativeStart be ? ToInteger(start).
const start: JSAny = arguments[0];
const relativeStart: Number = ToInteger_Inline(start);
// 4. If relativeStart < 0, let k be max((len + relativeStart), 0);
// else let k be min(relativeStart, len).
let k: Number = relativeStart < 0 ? Max((len + relativeStart), 0) :
Min(relativeStart, len);
// 5. If end is undefined, let relativeEnd be len;
// else let relativeEnd be ? ToInteger(end).
const end: JSAny = arguments[1];
const relativeEnd: Number = end == Undefined ? len : ToInteger_Inline(end);
// 6. If relativeEnd < 0, let final be max((len + relativeEnd), 0);
// else let final be min(relativeEnd, len).
const final: Number =
relativeEnd < 0 ? Max((len + relativeEnd), 0) : Min(relativeEnd, len);
// 7. Let count be max(final - k, 0).
const count: Number = Max(final - k, 0);
assert(0 <= k);
assert(k <= len);
assert(0 <= final);
assert(final <= len);
assert(0 <= count);
assert(count <= len);
try {
return HandleFastSlice(context, o, k, count)
otherwise Slow;
} label Slow {}
// 8. Let A be ? ArraySpeciesCreate(O, count).
const a: JSReceiver = ArraySpeciesCreate(context, o, count);
// 9. Let n be 0.
let n: Number = 0;
// 10. Repeat, while k < final
while (k < final) {
// a. Let Pk be ! ToString(k).
const pK: Number = k;
// b. Let kPresent be ? HasProperty(O, Pk).
const fromPresent: Boolean = HasProperty(o, pK);
// c. If kPresent is true, then
if (fromPresent == True) {
// i. Let kValue be ? Get(O, Pk).
const kValue: JSAny = GetProperty(o, pK);
// ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(n), kValue).
FastCreateDataProperty(a, n, kValue);
}
// d. Increase k by 1.
k++;
// e. Increase n by 1.
n++;
}
// 11. Perform ? Set(A, "length", n, true).
SetProperty(a, kLengthString, n);
// 12. Return A.
return a;
}
}

View File

@ -3,143 +3,142 @@
// found in the LICENSE file.
namespace array {
transitioning javascript builtin
ArraySomeLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny {
// All continuation points in the optimized some implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
transitioning javascript builtin
ArraySomeLoopEagerDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny {
// All continuation points in the optimized some implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires JSAny type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
return ArraySomeLoopContinuation(
jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK,
numberLength, Undefined);
return ArraySomeLoopContinuation(
jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK,
numberLength, Undefined);
}
transitioning javascript builtin
ArraySomeLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny,
result: JSAny): JSAny {
// All continuation points in the optimized some implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
let numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
// This custom lazy deopt point is right after the callback. some() needs
// to pick up at the next step: if the result is true, then return,
// otherwise, keep going through the array starting from k + 1.
if (ToBoolean(result)) {
return True;
}
transitioning javascript builtin
ArraySomeLoopLazyDeoptContinuation(
js-implicit context: NativeContext, receiver: JSAny)(
callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny,
result: JSAny): JSAny {
// All continuation points in the optimized some implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
let numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
numberK = numberK + 1;
// This custom lazy deopt point is right after the callback. some() needs
// to pick up at the next step: if the result is true, then return,
// otherwise, keep going through the array starting from k + 1.
if (ToBoolean(result)) {
return True;
}
return ArraySomeLoopContinuation(
jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK,
numberLength, Undefined);
}
numberK = numberK + 1;
transitioning builtin ArraySomeLoopContinuation(implicit context: Context)(
_receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, _array: JSAny,
o: JSReceiver, initialK: Number, length: Number, _initialTo: JSAny): JSAny {
// 5. Let k be 0.
// 6. Repeat, while k < len
for (let k: Number = initialK; k < length; k++) {
// 6a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
return ArraySomeLoopContinuation(
jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK,
numberLength, Undefined);
}
// 6b. Let kPresent be ? HasProperty(O, Pk).
const kPresent: Boolean = HasProperty_Inline(o, k);
transitioning builtin ArraySomeLoopContinuation(implicit context: Context)(
_receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny,
_array: JSAny, o: JSReceiver, initialK: Number, length: Number,
_initialTo: JSAny): JSAny {
// 5. Let k be 0.
// 6. Repeat, while k < len
for (let k: Number = initialK; k < length; k++) {
// 6a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
// 6c. If kPresent is true, then
if (kPresent == True) {
// 6c. i. Let kValue be ? Get(O, Pk).
const kValue: JSAny = GetProperty(o, k);
// 6b. Let kPresent be ? HasProperty(O, Pk).
const kPresent: Boolean = HasProperty_Inline(o, k);
// 6c. ii. Perform ? Call(callbackfn, T, <kValue, k, O>).
const result: JSAny = Call(context, callbackfn, thisArg, kValue, k, o);
// 6c. If kPresent is true, then
if (kPresent == True) {
// 6c. i. Let kValue be ? Get(O, Pk).
const kValue: JSAny = GetProperty(o, k);
// 6c. ii. Perform ? Call(callbackfn, T, <kValue, k, O>).
const result: JSAny = Call(context, callbackfn, thisArg, kValue, k, o);
// iii. If selected is true, then...
if (ToBoolean(result)) {
return True;
}
}
// 6d. Increase k by 1. (done by the loop).
}
return False;
}
transitioning macro FastArraySome(implicit context: Context)(
o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny
labels Bailout(Smi) {
let k: Smi = 0;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(k);
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = NewFastJSArrayWitness(fastO);
// Build a fast loop over the smi array.
for (; k < smiLen; k++) {
fastOW.Recheck() otherwise goto Bailout(k);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k);
const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue;
const result: JSAny =
Call(context, callbackfn, thisArg, value, k, fastOW.Get());
// iii. If selected is true, then...
if (ToBoolean(result)) {
return True;
}
}
return False;
// 6d. Increase k by 1. (done by the loop).
}
return False;
}
// https://tc39.github.io/ecma262/#sec-array.prototype.some
transitioning javascript builtin
ArraySome(js-implicit context: NativeContext, receiver: JSAny)(...arguments):
JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.some');
transitioning macro FastArraySome(implicit context: Context)(
o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny
labels Bailout(Smi) {
let k: Smi = 0;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(k);
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = NewFastJSArrayWitness(fastO);
// 1. Let O be ? ToObject(this value).
const o: JSReceiver = ToObject_Inline(context, receiver);
// Build a fast loop over the smi array.
for (; k < smiLen; k++) {
fastOW.Recheck() otherwise goto Bailout(k);
// 2. Let len be ? ToLength(? Get(O, "length")).
const len: Number = GetLengthProperty(o);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto TypeError;
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise TypeError;
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: JSAny = arguments[1];
// Special cases.
try {
return FastArraySome(o, len, callbackfn, thisArg)
otherwise Bailout;
} label Bailout(kValue: Smi) deferred {
return ArraySomeLoopContinuation(
o, callbackfn, thisArg, Undefined, o, kValue, len, Undefined);
}
} label TypeError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k);
const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue;
const result: JSAny =
Call(context, callbackfn, thisArg, value, k, fastOW.Get());
if (ToBoolean(result)) {
return True;
}
}
return False;
}
// https://tc39.github.io/ecma262/#sec-array.prototype.some
transitioning javascript builtin
ArraySome(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.some');
// 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);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto TypeError;
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise TypeError;
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: JSAny = arguments[1];
// Special cases.
try {
return FastArraySome(o, len, callbackfn, thisArg)
otherwise Bailout;
} label Bailout(kValue: Smi) deferred {
return ArraySomeLoopContinuation(
o, callbackfn, thisArg, Undefined, o, kValue, len, Undefined);
}
} label TypeError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
}
}
}

View File

@ -3,422 +3,418 @@
// found in the LICENSE file.
namespace array {
// Given {source}, we want to create a non-zero length array of type
// FixedArrayType with the specified {result_capacity}. Starting from
// {startIndex}, {count} number of elements are copied to the newly
// created result array. Most of this behavior is outsourced to
// ExtractFixedArray(). We handle the case where the {source} is
// EmptyFixedArray but result is expected to be a FixedDoubleArray.
macro Extract(implicit context: Context)(
source: FixedArray, startIndex: Smi, count: Smi,
resultCapacity: Smi): FixedArray {
return ExtractFixedArray(
source, Convert<intptr>(startIndex), Convert<intptr>(count),
Convert<intptr>(resultCapacity));
}
// Given {source}, we want to create a non-zero length array of type
// FixedArrayType with the specified {result_capacity}. Starting from
// {startIndex}, {count} number of elements are copied to the newly
// created result array. Most of this behavior is outsourced to
// ExtractFixedArray(). We handle the case where the {source} is
// EmptyFixedArray but result is expected to be a FixedDoubleArray.
macro Extract(implicit context: Context)(
source: FixedArray, startIndex: Smi, count: Smi,
resultCapacity: Smi): FixedArray {
return ExtractFixedArray(
source, Convert<intptr>(startIndex), Convert<intptr>(count),
Convert<intptr>(resultCapacity));
}
macro Extract(implicit context: Context)(
source: FixedDoubleArray|EmptyFixedArray, startIndex: Smi, count: Smi,
resultCapacity: Smi): FixedDoubleArray|EmptyFixedArray {
typeswitch (source) {
case (EmptyFixedArray): {
// ExtractFixedDoubleArray expects {source} to be a FixedDoubleArray.
// Handle the case where {source} is empty here.
return AllocateFixedDoubleArrayWithHoles(
Convert<intptr>(resultCapacity),
AllocationFlag::kAllowLargeObjectAllocation);
}
case (source: FixedDoubleArray): {
return ExtractFixedDoubleArray(
source, Convert<intptr>(startIndex), Convert<intptr>(count),
Convert<intptr>(resultCapacity));
}
macro Extract(implicit context: Context)(
source: FixedDoubleArray|EmptyFixedArray, startIndex: Smi, count: Smi,
resultCapacity: Smi): FixedDoubleArray|EmptyFixedArray {
typeswitch (source) {
case (EmptyFixedArray): {
// ExtractFixedDoubleArray expects {source} to be a FixedDoubleArray.
// Handle the case where {source} is empty here.
return AllocateFixedDoubleArrayWithHoles(
Convert<intptr>(resultCapacity),
AllocationFlag::kAllowLargeObjectAllocation);
}
case (source: FixedDoubleArray): {
return ExtractFixedDoubleArray(
source, Convert<intptr>(startIndex), Convert<intptr>(count),
Convert<intptr>(resultCapacity));
}
}
}
macro DoMoveElements<FixedArrayType : type extends FixedArrayBase>(
elements: FixedArrayType, dstIndex: Smi, srcIndex: Smi,
count: Smi): void {
TorqueMoveElements(
elements, Convert<intptr>(dstIndex), Convert<intptr>(srcIndex),
Convert<intptr>(count));
macro DoMoveElements<FixedArrayType : type extends FixedArrayBase>(
elements: FixedArrayType, dstIndex: Smi, srcIndex: Smi, count: Smi): void {
TorqueMoveElements(
elements, Convert<intptr>(dstIndex), Convert<intptr>(srcIndex),
Convert<intptr>(count));
}
macro StoreHoles<FixedArrayType : type extends FixedArrayBase>(
elements: FixedArrayType, holeStartIndex: Smi, holeEndIndex: Smi): void {
for (let i: Smi = holeStartIndex; i < holeEndIndex; i++) {
array::StoreArrayHole(elements, i);
}
}
macro StoreHoles<FixedArrayType : type extends FixedArrayBase>(
elements: FixedArrayType, holeStartIndex: Smi, holeEndIndex: Smi): void {
for (let i: Smi = holeStartIndex; i < holeEndIndex; i++) {
array::StoreArrayHole(elements, i);
}
}
macro DoCopyElements<FixedArrayType : type extends FixedArrayBase>(
dstElements: FixedArrayType, dstIndex: Smi, srcElements: FixedArrayType,
srcIndex: Smi, count: Smi): void {
TorqueCopyElements(
dstElements, Convert<intptr>(dstIndex), srcElements,
Convert<intptr>(srcIndex), Convert<intptr>(count));
}
macro DoCopyElements<FixedArrayType : type extends FixedArrayBase>(
dstElements: FixedArrayType, dstIndex: Smi, srcElements: FixedArrayType,
srcIndex: Smi, count: Smi): void {
TorqueCopyElements(
dstElements, Convert<intptr>(dstIndex), srcElements,
Convert<intptr>(srcIndex), Convert<intptr>(count));
}
macro
FastSplice<FixedArrayType : type extends FixedArrayBase, ElementType: type>(
implicit context: Context)(
args: Arguments, a: JSArray, length: Smi, newLength: Smi, actualStart: Smi,
insertCount: Smi, actualDeleteCount: Smi): void {
// Make sure elements are writable.
array::EnsureWriteableFastElements(a);
macro
FastSplice<FixedArrayType : type extends FixedArrayBase, ElementType: type>(
implicit context: Context)(
args: Arguments, a: JSArray, length: Smi, newLength: Smi,
actualStart: Smi, insertCount: Smi, actualDeleteCount: Smi): void {
// Make sure elements are writable.
array::EnsureWriteableFastElements(a);
if (insertCount != actualDeleteCount) {
const elements =
UnsafeCast<(FixedArrayType | EmptyFixedArray)>(a.elements);
const dstIndex: Smi = actualStart + insertCount;
const srcIndex: Smi = actualStart + actualDeleteCount;
const count: Smi = length - actualDeleteCount - actualStart;
if (insertCount < actualDeleteCount) {
// Shrink.
if (insertCount != actualDeleteCount) {
const elements = UnsafeCast<(FixedArrayType | EmptyFixedArray)>(a.elements);
const dstIndex: Smi = actualStart + insertCount;
const srcIndex: Smi = actualStart + actualDeleteCount;
const count: Smi = length - actualDeleteCount - actualStart;
if (insertCount < actualDeleteCount) {
// Shrink.
DoMoveElements(
UnsafeCast<FixedArrayType>(elements), dstIndex, srcIndex, count);
StoreHoles(UnsafeCast<FixedArrayType>(elements), newLength, length);
} else if (insertCount > actualDeleteCount) {
// If the backing store is big enough, then moving elements is enough.
if (newLength <= elements.length) {
DoMoveElements(
UnsafeCast<FixedArrayType>(elements), dstIndex, srcIndex, count);
StoreHoles(UnsafeCast<FixedArrayType>(elements), newLength, length);
} else if (insertCount > actualDeleteCount) {
// If the backing store is big enough, then moving elements is enough.
if (newLength <= elements.length) {
DoMoveElements(
UnsafeCast<FixedArrayType>(elements), dstIndex, srcIndex, count);
} else {
// Grow.
const capacity: Smi = CalculateNewElementsCapacity(newLength);
const newElements: FixedArrayType = UnsafeCast<FixedArrayType>(
Extract(elements, 0, actualStart, capacity));
a.elements = newElements;
if (elements.length > 0) {
DoCopyElements(
newElements, dstIndex, UnsafeCast<FixedArrayType>(elements),
srcIndex, count);
}
} else {
// Grow.
const capacity: Smi = CalculateNewElementsCapacity(newLength);
const newElements: FixedArrayType = UnsafeCast<FixedArrayType>(
Extract(elements, 0, actualStart, capacity));
a.elements = newElements;
if (elements.length > 0) {
DoCopyElements(
newElements, dstIndex, UnsafeCast<FixedArrayType>(elements),
srcIndex, count);
}
}
}
// Copy arguments.
let k: Smi = actualStart;
if (insertCount > 0) {
const typedNewElements: FixedArrayType =
UnsafeCast<FixedArrayType>(a.elements);
for (let i: intptr = 2; i < args.length; ++i) {
const e: JSAny = args[i];
// The argument elements were already validated to be an appropriate
// {ElementType} to store in {FixedArrayType}.
typedNewElements[k++] = UnsafeCast<ElementType>(e);
}
}
// Update the array's length after all the FixedArray shuffling is done.
a.length = newLength;
}
transitioning macro FastArraySplice(
context: Context, args: Arguments, o: JSReceiver,
originalLengthNumber: Number, actualStartNumber: Number, insertCount: Smi,
actualDeleteCountNumber: Number): JSAny
labels Bailout {
const originalLength: Smi =
Cast<Smi>(originalLengthNumber) otherwise Bailout;
const actualStart: Smi = Cast<Smi>(actualStartNumber) otherwise Bailout;
const actualDeleteCount: Smi =
Cast<Smi>(actualDeleteCountNumber) otherwise Bailout;
const lengthDelta: Smi = insertCount - actualDeleteCount;
const newLength: Smi = originalLength + lengthDelta;
const a: JSArray = Cast<JSArray>(o) otherwise Bailout;
const map: Map = a.map;
if (!IsPrototypeInitialArrayPrototype(map)) goto Bailout;
if (IsNoElementsProtectorCellInvalid()) goto Bailout;
if (IsArraySpeciesProtectorCellInvalid()) goto Bailout;
// Fast path only works on fast elements kind and with writable length.
let elementsKind: ElementsKind = EnsureArrayPushable(map) otherwise Bailout;
if (!IsFastElementsKind(elementsKind)) goto Bailout;
const oldElementsKind: ElementsKind = elementsKind;
// Copy arguments.
let k: Smi = actualStart;
if (insertCount > 0) {
const typedNewElements: FixedArrayType =
UnsafeCast<FixedArrayType>(a.elements);
for (let i: intptr = 2; i < args.length; ++i) {
const e: JSAny = args[i];
if (IsFastSmiElementsKind(elementsKind)) {
if (TaggedIsNotSmi(e)) {
const heapObject: HeapObject = UnsafeCast<HeapObject>(e);
elementsKind = IsHeapNumber(heapObject) ?
AllowDoubleElements(elementsKind) :
AllowNonNumberElements(elementsKind);
}
} else if (IsDoubleElementsKind(elementsKind)) {
if (!IsNumber(e)) {
elementsKind = AllowNonNumberElements(elementsKind);
}
// The argument elements were already validated to be an appropriate
// {ElementType} to store in {FixedArrayType}.
typedNewElements[k++] = UnsafeCast<ElementType>(e);
}
}
// Update the array's length after all the FixedArray shuffling is done.
a.length = newLength;
}
transitioning macro FastArraySplice(
context: Context, args: Arguments, o: JSReceiver,
originalLengthNumber: Number, actualStartNumber: Number, insertCount: Smi,
actualDeleteCountNumber: Number): JSAny
labels Bailout {
const originalLength: Smi = Cast<Smi>(originalLengthNumber) otherwise Bailout;
const actualStart: Smi = Cast<Smi>(actualStartNumber) otherwise Bailout;
const actualDeleteCount: Smi =
Cast<Smi>(actualDeleteCountNumber) otherwise Bailout;
const lengthDelta: Smi = insertCount - actualDeleteCount;
const newLength: Smi = originalLength + lengthDelta;
const a: JSArray = Cast<JSArray>(o) otherwise Bailout;
const map: Map = a.map;
if (!IsPrototypeInitialArrayPrototype(map)) goto Bailout;
if (IsNoElementsProtectorCellInvalid()) goto Bailout;
if (IsArraySpeciesProtectorCellInvalid()) goto Bailout;
// Fast path only works on fast elements kind and with writable length.
let elementsKind: ElementsKind = EnsureArrayPushable(map) otherwise Bailout;
if (!IsFastElementsKind(elementsKind)) goto Bailout;
const oldElementsKind: ElementsKind = elementsKind;
for (let i: intptr = 2; i < args.length; ++i) {
const e: JSAny = args[i];
if (IsFastSmiElementsKind(elementsKind)) {
if (TaggedIsNotSmi(e)) {
const heapObject: HeapObject = UnsafeCast<HeapObject>(e);
elementsKind = IsHeapNumber(heapObject) ?
AllowDoubleElements(elementsKind) :
AllowNonNumberElements(elementsKind);
}
} else if (IsDoubleElementsKind(elementsKind)) {
if (!IsNumber(e)) {
elementsKind = AllowNonNumberElements(elementsKind);
}
}
}
if (elementsKind != oldElementsKind) {
const smiElementsKind: Smi = Convert<Smi>(Convert<int32>(elementsKind));
TransitionElementsKindWithKind(context, a, smiElementsKind);
}
if (elementsKind != oldElementsKind) {
const smiElementsKind: Smi = Convert<Smi>(Convert<int32>(elementsKind));
TransitionElementsKindWithKind(context, a, smiElementsKind);
}
// Make sure that the length hasn't been changed by side-effect.
const length: Smi = Cast<Smi>(a.length) otherwise Bailout;
if (originalLength != length) goto Bailout;
// Make sure that the length hasn't been changed by side-effect.
const length: Smi = Cast<Smi>(a.length) otherwise Bailout;
if (originalLength != length) goto Bailout;
const deletedResult: JSArray =
ExtractFastJSArray(context, a, actualStart, actualDeleteCount);
if (newLength == 0) {
a.elements = kEmptyFixedArray;
a.length = 0;
return deletedResult;
}
if (IsFastSmiOrTaggedElementsKind(elementsKind)) {
FastSplice<FixedArray, JSAny>(
args, a, length, newLength, actualStart, insertCount,
actualDeleteCount);
} else {
FastSplice<FixedDoubleArray, Number>(
args, a, length, newLength, actualStart, insertCount,
actualDeleteCount);
}
const deletedResult: JSArray =
ExtractFastJSArray(context, a, actualStart, actualDeleteCount);
if (newLength == 0) {
a.elements = kEmptyFixedArray;
a.length = 0;
return deletedResult;
}
transitioning macro FillDeletedElementsArray(
context: Context, o: JSReceiver, actualStart: Number,
actualDeleteCount: Number, a: JSReceiver): JSAny {
// 10. Let k be 0.
let k: Number = 0;
// 11. Repeat, while k < actualDeleteCount
while (k < actualDeleteCount) {
// a. Let from be ! ToString(actualStart + k).
const from: Number = actualStart + k;
// b. Let fromPresent be ? HasProperty(O, from).
const fromPresent: Boolean = HasProperty(o, from);
// c. If fromPresent is true, then
if (fromPresent == True) {
// i. Let fromValue be ? Get(O, from).
const fromValue: JSAny = GetProperty(o, from);
// ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(k), fromValue).
FastCreateDataProperty(a, k, fromValue);
}
// d. Increment k by 1.
k++;
}
// 12. Perform ? Set(A, "length", actualDeleteCount, true).
SetProperty(a, kLengthString, actualDeleteCount);
return a;
}
// HandleForwardCase implements step 15. "If itemCount < actualDeleteCount,
// then...""
transitioning macro HandleForwardCase(
context: Context, o: JSReceiver, len: Number, itemCount: Number,
actualStart: Number, actualDeleteCount: Number): void {
// 15. If itemCount < actualDeleteCount, then
// a. Let k be actualStart.
let k: Number = actualStart;
// b. Repeat, while k < (len - actualDeleteCount)
while (k < (len - actualDeleteCount)) {
// i. Let from be ! ToString(k + actualDeleteCount).
const from: Number = k + actualDeleteCount;
// ii. Let to be ! ToString(k + itemCount).
const to: Number = k + itemCount;
// iii. Let fromPresent be ? HasProperty(O, from).
const fromPresent: Boolean = HasProperty(o, from);
// iv. If fromPresent is true, then
if (fromPresent == True) {
// 1. Let fromValue be ? Get(O, from).
const fromValue: JSAny = GetProperty(o, from);
// 2. Perform ? Set(O, to, fromValue, true).
SetProperty(o, to, fromValue);
// v. Else fromPresent is false,
} else {
// 1. Perform ? DeletePropertyOrThrow(O, to).
DeleteProperty(o, to, LanguageMode::kStrict);
}
// vi. Increase k by 1.
k++;
}
// c. Let k be len.
k = len;
// d. Repeat, while k > (len - actualDeleteCount + itemCount)
while (k > (len - actualDeleteCount + itemCount)) {
// i. Perform ? DeletePropertyOrThrow(O, ! ToString(k - 1)).
DeleteProperty(o, k - 1, LanguageMode::kStrict);
// ii. Decrease k by 1.
k--;
}
}
// HandleBackwardCase implements step 16. "Else if itemCount >
// actualDeleteCount, then..."
transitioning macro HandleBackwardCase(
context: Context, o: JSReceiver, len: Number, itemCount: Number,
actualStart: Number, actualDeleteCount: Number): void {
// 16. Else if itemCount > actualDeleteCount, then
// a. Let k be (len - actualDeleteCount).
let k: Number = len - actualDeleteCount;
// b. Repeat, while k > actualStart
while (k > actualStart) {
// i. Let from be ! ToString(k + actualDeleteCount - 1).
const from: Number = k + actualDeleteCount - 1;
// ii. Let to be ! ToString(k + itemCount - 1).
const to: Number = k + itemCount - 1;
// iii. Let fromPresent be ? HasProperty(O, from).
const fromPresent: Boolean = HasProperty(o, from);
// iv. If fromPresent is true, then
if (fromPresent == True) {
// 1. Let fromValue be ? Get(O, from).
const fromValue: JSAny = GetProperty(o, from);
// 2. Perform ? Set(O, to, fromValue, true).
SetProperty(o, to, fromValue);
// v. Else fromPresent is false,
} else {
// 1. Perform ? DeletePropertyOrThrow(O, to).
DeleteProperty(o, to, LanguageMode::kStrict);
}
// vi. Decrease k by 1.
k--;
}
}
transitioning macro SlowSplice(
context: Context, arguments: Arguments, o: JSReceiver, len: Number,
actualStart: Number, insertCount: Smi, actualDeleteCount: Number): JSAny {
// 9. Let A be ? ArraySpeciesCreate(O, actualDeleteCount).
const a: JSReceiver = ArraySpeciesCreate(context, o, actualDeleteCount);
const itemCount: Number = insertCount;
// Steps 9 through 12: creating the array of deleted elements.
FillDeletedElementsArray(context, o, actualStart, actualDeleteCount, a);
// 13. Let items be a List whose elements are, in left-to-right order,
// the portion of the actual argument list starting with the third
// argument. The list is empty if fewer than three arguments were
// passed.
// 14. Let itemCount be the Number of elements in items.
// (done above).
// 15. If itemCount < actualDeleteCount, then
if (itemCount < actualDeleteCount) {
HandleForwardCase(
context, o, len, itemCount, actualStart, actualDeleteCount);
// 16. Else if itemCount > actualDeleteCount, then
} else if (itemCount > actualDeleteCount) {
HandleBackwardCase(
context, o, len, itemCount, actualStart, actualDeleteCount);
}
// 17. Let k be actualStart.
let k: Number = actualStart;
// 18. Repeat, while items is not empty
// a. Remove the first element from items and let E be the value of that
// element.
if (arguments.length > 2) {
for (let i: intptr = 2; i < arguments.length; ++i) {
const e: JSAny = arguments[i];
// b. Perform ? Set(O, ! ToString(k), E, true).
SetProperty(o, k, e);
// c. Increase k by 1.
k = k + 1;
}
}
// 19. Perform ? Set(O, "length", len - actualDeleteCount + itemCount,
// true).
SetProperty(o, kLengthString, len - actualDeleteCount + itemCount);
return a;
}
// https://tc39.github.io/ecma262/#sec-array.prototype.splice
transitioning javascript builtin
ArrayPrototypeSplice(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): JSAny {
// 1. Let O be ? ToObject(this value).
const o: JSReceiver = ToObject(context, receiver);
// 2. Let len be ? ToLength(? Get(O, "length")).
const len: Number = GetLengthProperty(o);
// 3. Let relativeStart be ? ToInteger(start).
const start: JSAny = arguments[0];
const relativeStart: Number = ToInteger_Inline(start);
// 4. If relativeStart < 0, let actualStart be max((len + relativeStart),
// 0);
// else let actualStart be min(relativeStart, len).
const actualStart: Number = relativeStart < 0 ?
Max((len + relativeStart), 0) :
Min(relativeStart, len);
let insertCount: Smi;
let actualDeleteCount: Number;
// 5. If the Number of actual arguments is 0, then
if (arguments.length == 0) {
// a. Let insertCount be 0.
insertCount = 0;
// b. Let actualDeleteCount be 0.
actualDeleteCount = 0;
// 6. Else if the Number of actual arguments is 1, then
} else if (arguments.length == 1) {
// a. Let insertCount be 0.
insertCount = 0;
// b. Let actualDeleteCount be len - actualStart.
actualDeleteCount = len - actualStart;
// 7. Else,
} else {
// a. Let insertCount be the Number of actual arguments minus 2.
insertCount = Convert<Smi>(arguments.length) - 2;
// b. Let dc be ? ToInteger(deleteCount).
const deleteCount: JSAny = arguments[1];
const dc: Number = ToInteger_Inline(deleteCount);
// c. Let actualDeleteCount be min(max(dc, 0), len - actualStart).
actualDeleteCount = Min(Max(dc, 0), len - actualStart);
}
// 8. If len + insertCount - actualDeleteCount > 2^53-1, throw a
// Bailout exception.
const newLength: Number = len + insertCount - actualDeleteCount;
if (newLength > kMaxSafeInteger) {
ThrowTypeError(MessageTemplate::kInvalidArrayLength, start);
}
try {
return FastArraySplice(
context, arguments, o, len, actualStart, insertCount,
actualDeleteCount) otherwise Bailout;
} label Bailout {}
// If the fast case fails, just continue with the slow, correct,
// spec-compliant case.
return SlowSplice(
context, arguments, o, len, actualStart, insertCount,
if (IsFastSmiOrTaggedElementsKind(elementsKind)) {
FastSplice<FixedArray, JSAny>(
args, a, length, newLength, actualStart, insertCount,
actualDeleteCount);
} else {
FastSplice<FixedDoubleArray, Number>(
args, a, length, newLength, actualStart, insertCount,
actualDeleteCount);
}
return deletedResult;
}
transitioning macro FillDeletedElementsArray(
context: Context, o: JSReceiver, actualStart: Number,
actualDeleteCount: Number, a: JSReceiver): JSAny {
// 10. Let k be 0.
let k: Number = 0;
// 11. Repeat, while k < actualDeleteCount
while (k < actualDeleteCount) {
// a. Let from be ! ToString(actualStart + k).
const from: Number = actualStart + k;
// b. Let fromPresent be ? HasProperty(O, from).
const fromPresent: Boolean = HasProperty(o, from);
// c. If fromPresent is true, then
if (fromPresent == True) {
// i. Let fromValue be ? Get(O, from).
const fromValue: JSAny = GetProperty(o, from);
// ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(k), fromValue).
FastCreateDataProperty(a, k, fromValue);
}
// d. Increment k by 1.
k++;
}
// 12. Perform ? Set(A, "length", actualDeleteCount, true).
SetProperty(a, kLengthString, actualDeleteCount);
return a;
}
// HandleForwardCase implements step 15. "If itemCount < actualDeleteCount,
// then...""
transitioning macro HandleForwardCase(
context: Context, o: JSReceiver, len: Number, itemCount: Number,
actualStart: Number, actualDeleteCount: Number): void {
// 15. If itemCount < actualDeleteCount, then
// a. Let k be actualStart.
let k: Number = actualStart;
// b. Repeat, while k < (len - actualDeleteCount)
while (k < (len - actualDeleteCount)) {
// i. Let from be ! ToString(k + actualDeleteCount).
const from: Number = k + actualDeleteCount;
// ii. Let to be ! ToString(k + itemCount).
const to: Number = k + itemCount;
// iii. Let fromPresent be ? HasProperty(O, from).
const fromPresent: Boolean = HasProperty(o, from);
// iv. If fromPresent is true, then
if (fromPresent == True) {
// 1. Let fromValue be ? Get(O, from).
const fromValue: JSAny = GetProperty(o, from);
// 2. Perform ? Set(O, to, fromValue, true).
SetProperty(o, to, fromValue);
// v. Else fromPresent is false,
} else {
// 1. Perform ? DeletePropertyOrThrow(O, to).
DeleteProperty(o, to, LanguageMode::kStrict);
}
// vi. Increase k by 1.
k++;
}
// c. Let k be len.
k = len;
// d. Repeat, while k > (len - actualDeleteCount + itemCount)
while (k > (len - actualDeleteCount + itemCount)) {
// i. Perform ? DeletePropertyOrThrow(O, ! ToString(k - 1)).
DeleteProperty(o, k - 1, LanguageMode::kStrict);
// ii. Decrease k by 1.
k--;
}
}
// HandleBackwardCase implements step 16. "Else if itemCount >
// actualDeleteCount, then..."
transitioning macro HandleBackwardCase(
context: Context, o: JSReceiver, len: Number, itemCount: Number,
actualStart: Number, actualDeleteCount: Number): void {
// 16. Else if itemCount > actualDeleteCount, then
// a. Let k be (len - actualDeleteCount).
let k: Number = len - actualDeleteCount;
// b. Repeat, while k > actualStart
while (k > actualStart) {
// i. Let from be ! ToString(k + actualDeleteCount - 1).
const from: Number = k + actualDeleteCount - 1;
// ii. Let to be ! ToString(k + itemCount - 1).
const to: Number = k + itemCount - 1;
// iii. Let fromPresent be ? HasProperty(O, from).
const fromPresent: Boolean = HasProperty(o, from);
// iv. If fromPresent is true, then
if (fromPresent == True) {
// 1. Let fromValue be ? Get(O, from).
const fromValue: JSAny = GetProperty(o, from);
// 2. Perform ? Set(O, to, fromValue, true).
SetProperty(o, to, fromValue);
// v. Else fromPresent is false,
} else {
// 1. Perform ? DeletePropertyOrThrow(O, to).
DeleteProperty(o, to, LanguageMode::kStrict);
}
// vi. Decrease k by 1.
k--;
}
}
transitioning macro SlowSplice(
context: Context, arguments: Arguments, o: JSReceiver, len: Number,
actualStart: Number, insertCount: Smi, actualDeleteCount: Number): JSAny {
// 9. Let A be ? ArraySpeciesCreate(O, actualDeleteCount).
const a: JSReceiver = ArraySpeciesCreate(context, o, actualDeleteCount);
const itemCount: Number = insertCount;
// Steps 9 through 12: creating the array of deleted elements.
FillDeletedElementsArray(context, o, actualStart, actualDeleteCount, a);
// 13. Let items be a List whose elements are, in left-to-right order,
// the portion of the actual argument list starting with the third
// argument. The list is empty if fewer than three arguments were
// passed.
// 14. Let itemCount be the Number of elements in items.
// (done above).
// 15. If itemCount < actualDeleteCount, then
if (itemCount < actualDeleteCount) {
HandleForwardCase(
context, o, len, itemCount, actualStart, actualDeleteCount);
// 16. Else if itemCount > actualDeleteCount, then
} else if (itemCount > actualDeleteCount) {
HandleBackwardCase(
context, o, len, itemCount, actualStart, actualDeleteCount);
}
// 17. Let k be actualStart.
let k: Number = actualStart;
// 18. Repeat, while items is not empty
// a. Remove the first element from items and let E be the value of that
// element.
if (arguments.length > 2) {
for (let i: intptr = 2; i < arguments.length; ++i) {
const e: JSAny = arguments[i];
// b. Perform ? Set(O, ! ToString(k), E, true).
SetProperty(o, k, e);
// c. Increase k by 1.
k = k + 1;
}
}
// 19. Perform ? Set(O, "length", len - actualDeleteCount + itemCount,
// true).
SetProperty(o, kLengthString, len - actualDeleteCount + itemCount);
return a;
}
// https://tc39.github.io/ecma262/#sec-array.prototype.splice
transitioning javascript builtin
ArrayPrototypeSplice(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// 1. Let O be ? ToObject(this value).
const o: JSReceiver = ToObject(context, receiver);
// 2. Let len be ? ToLength(? Get(O, "length")).
const len: Number = GetLengthProperty(o);
// 3. Let relativeStart be ? ToInteger(start).
const start: JSAny = arguments[0];
const relativeStart: Number = ToInteger_Inline(start);
// 4. If relativeStart < 0, let actualStart be max((len + relativeStart),
// 0);
// else let actualStart be min(relativeStart, len).
const actualStart: Number = relativeStart < 0 ?
Max((len + relativeStart), 0) :
Min(relativeStart, len);
let insertCount: Smi;
let actualDeleteCount: Number;
// 5. If the Number of actual arguments is 0, then
if (arguments.length == 0) {
// a. Let insertCount be 0.
insertCount = 0;
// b. Let actualDeleteCount be 0.
actualDeleteCount = 0;
// 6. Else if the Number of actual arguments is 1, then
} else if (arguments.length == 1) {
// a. Let insertCount be 0.
insertCount = 0;
// b. Let actualDeleteCount be len - actualStart.
actualDeleteCount = len - actualStart;
// 7. Else,
} else {
// a. Let insertCount be the Number of actual arguments minus 2.
insertCount = Convert<Smi>(arguments.length) - 2;
// b. Let dc be ? ToInteger(deleteCount).
const deleteCount: JSAny = arguments[1];
const dc: Number = ToInteger_Inline(deleteCount);
// c. Let actualDeleteCount be min(max(dc, 0), len - actualStart).
actualDeleteCount = Min(Max(dc, 0), len - actualStart);
}
// 8. If len + insertCount - actualDeleteCount > 2^53-1, throw a
// Bailout exception.
const newLength: Number = len + insertCount - actualDeleteCount;
if (newLength > kMaxSafeInteger) {
ThrowTypeError(MessageTemplate::kInvalidArrayLength, start);
}
try {
return FastArraySplice(
context, arguments, o, len, actualStart, insertCount, actualDeleteCount)
otherwise Bailout;
} label Bailout {}
// If the fast case fails, just continue with the slow, correct,
// spec-compliant case.
return SlowSplice(
context, arguments, o, len, actualStart, insertCount, actualDeleteCount);
}
}

View File

@ -3,96 +3,95 @@
// found in the LICENSE file.
namespace array {
extern builtin ArrayUnshift(Context, JSFunction, JSAny, int32): JSAny;
extern builtin ArrayUnshift(Context, JSFunction, JSAny, int32): JSAny;
transitioning macro GenericArrayUnshift(
context: Context, receiver: JSAny, arguments: Arguments): Number {
// 1. Let O be ? ToObject(this value).
const object: JSReceiver = ToObject_Inline(context, receiver);
transitioning macro GenericArrayUnshift(
context: Context, receiver: JSAny, arguments: Arguments): Number {
// 1. Let O be ? ToObject(this value).
const object: JSReceiver = ToObject_Inline(context, receiver);
// 2. Let len be ? ToLength(? Get(O, "length")).
const length: Number = GetLengthProperty(object);
// 2. Let len be ? ToLength(? Get(O, "length")).
const length: Number = GetLengthProperty(object);
// 3. Let argCount be the number of actual arguments.
const argCount: Smi = Convert<Smi>(arguments.length);
// 3. Let argCount be the number of actual arguments.
const argCount: Smi = Convert<Smi>(arguments.length);
// 4. If argCount > 0, then.
if (argCount > 0) {
// a. If len + argCount > 2**53 - 1, throw a TypeError exception.
if (length + argCount > kMaxSafeInteger) {
ThrowTypeError(MessageTemplate::kInvalidArrayLength);
}
// b. Let k be len.
let k: Number = length;
// c. Repeat, while k > 0.
while (k > 0) {
// i. Let from be ! ToString(k - 1).
const from: Number = k - 1;
// ii. Let to be ! ToString(k + argCount - 1).
const to: Number = k + argCount - 1;
// iii. Let fromPresent be ? HasProperty(O, from).
const fromPresent: Boolean = HasProperty(object, from);
// iv. If fromPresent is true, then
if (fromPresent == True) {
// 1. Let fromValue be ? Get(O, from).
const fromValue: JSAny = GetProperty(object, from);
// 2. Perform ? Set(O, to, fromValue, true).
SetProperty(object, to, fromValue);
} else {
// 1. Perform ? DeletePropertyOrThrow(O, to).
DeleteProperty(object, to, LanguageMode::kStrict);
}
// vi. Decrease k by 1.
--k;
}
// d. Let j be 0.
let j: Smi = 0;
// e. Let items be a List whose elements are, in left to right order,
// the arguments that were passed to this function invocation.
// f. Repeat, while items is not empty
while (j < argCount) {
// ii .Perform ? Set(O, ! ToString(j), E, true).
SetProperty(object, j, arguments[Convert<intptr>(j)]);
// iii. Increase j by 1.
++j;
}
// 4. If argCount > 0, then.
if (argCount > 0) {
// a. If len + argCount > 2**53 - 1, throw a TypeError exception.
if (length + argCount > kMaxSafeInteger) {
ThrowTypeError(MessageTemplate::kInvalidArrayLength);
}
// 5. Perform ? Set(O, "length", len + argCount, true).
const newLength: Number = length + argCount;
SetProperty(object, kLengthString, newLength);
// b. Let k be len.
let k: Number = length;
// 6. Return length + argCount.
return newLength;
// c. Repeat, while k > 0.
while (k > 0) {
// i. Let from be ! ToString(k - 1).
const from: Number = k - 1;
// ii. Let to be ! ToString(k + argCount - 1).
const to: Number = k + argCount - 1;
// iii. Let fromPresent be ? HasProperty(O, from).
const fromPresent: Boolean = HasProperty(object, from);
// iv. If fromPresent is true, then
if (fromPresent == True) {
// 1. Let fromValue be ? Get(O, from).
const fromValue: JSAny = GetProperty(object, from);
// 2. Perform ? Set(O, to, fromValue, true).
SetProperty(object, to, fromValue);
} else {
// 1. Perform ? DeletePropertyOrThrow(O, to).
DeleteProperty(object, to, LanguageMode::kStrict);
}
// vi. Decrease k by 1.
--k;
}
// d. Let j be 0.
let j: Smi = 0;
// e. Let items be a List whose elements are, in left to right order,
// the arguments that were passed to this function invocation.
// f. Repeat, while items is not empty
while (j < argCount) {
// ii .Perform ? Set(O, ! ToString(j), E, true).
SetProperty(object, j, arguments[Convert<intptr>(j)]);
// iii. Increase j by 1.
++j;
}
}
// https://tc39.github.io/ecma262/#sec-array.prototype.unshift
transitioning javascript builtin ArrayPrototypeUnshift(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSAny {
try {
const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
array::EnsureWriteableFastElements(array);
// 5. Perform ? Set(O, "length", len + argCount, true).
const newLength: Number = length + argCount;
SetProperty(object, kLengthString, newLength);
const map: Map = array.map;
if (!IsExtensibleMap(map)) goto Slow;
EnsureArrayLengthWritable(map) otherwise Slow;
// 6. Return length + argCount.
return newLength;
}
tail ArrayUnshift(
context, LoadTargetFromFrame(), Undefined,
Convert<int32>(arguments.length));
} label Slow {
return GenericArrayUnshift(context, receiver, arguments);
}
// https://tc39.github.io/ecma262/#sec-array.prototype.unshift
transitioning javascript builtin ArrayPrototypeUnshift(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
try {
const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
array::EnsureWriteableFastElements(array);
const map: Map = array.map;
if (!IsExtensibleMap(map)) goto Slow;
EnsureArrayLengthWritable(map) otherwise Slow;
tail ArrayUnshift(
context, LoadTargetFromFrame(), Undefined,
Convert<int32>(arguments.length));
} label Slow {
return GenericArrayUnshift(context, receiver, arguments);
}
}
}

View File

@ -5,87 +5,85 @@
#include 'src/builtins/builtins-array-gen.h'
namespace array {
// Naming convention from elements.cc. We have a similar intent but implement
// fastpaths using generics instead of using a class hierarchy for elements
// kinds specific implementations.
type GenericElementsAccessor extends ElementsKind;
type FastPackedSmiElements extends ElementsKind;
type FastPackedObjectElements extends ElementsKind;
type FastPackedDoubleElements extends ElementsKind;
type FastSmiOrObjectElements extends ElementsKind;
type FastDoubleElements extends ElementsKind;
type DictionaryElements extends ElementsKind;
// Naming convention from elements.cc. We have a similar intent but implement
// fastpaths using generics instead of using a class hierarchy for elements
// kinds specific implementations.
type GenericElementsAccessor extends ElementsKind;
type FastPackedSmiElements extends ElementsKind;
type FastPackedObjectElements extends ElementsKind;
type FastPackedDoubleElements extends ElementsKind;
type FastSmiOrObjectElements extends ElementsKind;
type FastDoubleElements extends ElementsKind;
type DictionaryElements extends ElementsKind;
macro EnsureWriteableFastElements(implicit context: Context)(array: JSArray) {
assert(IsFastElementsKind(array.map.elements_kind));
macro EnsureWriteableFastElements(implicit context: Context)(array: JSArray) {
assert(IsFastElementsKind(array.map.elements_kind));
const elements: FixedArrayBase = array.elements;
if (elements.map != kCOWMap) return;
const elements: FixedArrayBase = array.elements;
if (elements.map != kCOWMap) return;
// There are no COW *_DOUBLE_ELEMENTS arrays, so we are allowed to always
// extract FixedArrays and don't have to worry about FixedDoubleArrays.
assert(IsFastSmiOrTaggedElementsKind(array.map.elements_kind));
const length =
Convert<intptr>(Cast<Smi>(array.length) otherwise unreachable);
array.elements =
ExtractFixedArray(UnsafeCast<FixedArray>(elements), 0, length, length);
assert(array.elements.map != kCOWMap);
}
macro LoadElementOrUndefined(implicit context:
Context)(a: FixedArray, i: Smi): JSAny {
const e = UnsafeCast<(JSAny | TheHole)>(a.objects[i]);
return ReplaceTheHoleWithUndefined(e);
}
macro LoadElementOrUndefined(a: FixedDoubleArray, i: Smi): NumberOrUndefined {
const f: float64 = a.floats[i].Value() otherwise return Undefined;
return AllocateHeapNumberWithValue(f);
}
macro StoreArrayHole(elements: FixedDoubleArray, k: Smi): void {
elements.floats[k] = kDoubleHole;
}
macro StoreArrayHole(elements: FixedArray, k: Smi): void {
elements.objects[k] = TheHole;
}
extern macro SetPropertyLength(implicit context: Context)(JSAny, Number);
const kLengthDescriptorIndex:
constexpr int31 generates 'JSArray::kLengthDescriptorIndex';
const kAttributesReadOnlyMask: constexpr int31
generates 'PropertyDetails::kAttributesReadOnlyMask';
@export
macro EnsureArrayLengthWritable(implicit context: Context)(map: Map):
void labels Bailout {
// Don't support arrays in dictionary named property mode.
if (IsDictionaryMap(map)) {
goto Bailout;
}
// Check whether the length property is writable. The length property is the
// only default named property on arrays. It's nonconfigurable, hence is
// guaranteed to stay the first property.
const descriptors: DescriptorArray = map.instance_descriptors;
const descriptor:&DescriptorEntry =
& descriptors.descriptors[kLengthDescriptorIndex];
assert(TaggedEqual(descriptor->key, LengthStringConstant()));
const details: Smi = UnsafeCast<Smi>(descriptor->details);
if ((details & kAttributesReadOnlyMask) != 0) {
goto Bailout;
}
}
macro CreateJSArrayWithElements(implicit context: Context)(array: FixedArray):
JSArray {
const nativeContext: NativeContext = LoadNativeContext(context);
const map: Map =
LoadJSArrayElementsMap(ElementsKind::PACKED_ELEMENTS, nativeContext);
return AllocateJSArray(map, array, array.length);
}
// There are no COW *_DOUBLE_ELEMENTS arrays, so we are allowed to always
// extract FixedArrays and don't have to worry about FixedDoubleArrays.
assert(IsFastSmiOrTaggedElementsKind(array.map.elements_kind));
const length = Convert<intptr>(Cast<Smi>(array.length) otherwise unreachable);
array.elements =
ExtractFixedArray(UnsafeCast<FixedArray>(elements), 0, length, length);
assert(array.elements.map != kCOWMap);
}
macro LoadElementOrUndefined(implicit context: Context)(
a: FixedArray, i: Smi): JSAny {
const e = UnsafeCast<(JSAny | TheHole)>(a.objects[i]);
return ReplaceTheHoleWithUndefined(e);
}
macro LoadElementOrUndefined(a: FixedDoubleArray, i: Smi): NumberOrUndefined {
const f: float64 = a.floats[i].Value() otherwise return Undefined;
return AllocateHeapNumberWithValue(f);
}
macro StoreArrayHole(elements: FixedDoubleArray, k: Smi): void {
elements.floats[k] = kDoubleHole;
}
macro StoreArrayHole(elements: FixedArray, k: Smi): void {
elements.objects[k] = TheHole;
}
extern macro SetPropertyLength(implicit context: Context)(JSAny, Number);
const kLengthDescriptorIndex:
constexpr int31 generates 'JSArray::kLengthDescriptorIndex';
const kAttributesReadOnlyMask: constexpr int31
generates 'PropertyDetails::kAttributesReadOnlyMask';
@export
macro EnsureArrayLengthWritable(implicit context: Context)(map: Map):
void labels Bailout {
// Don't support arrays in dictionary named property mode.
if (IsDictionaryMap(map)) {
goto Bailout;
}
// Check whether the length property is writable. The length property is the
// only default named property on arrays. It's nonconfigurable, hence is
// guaranteed to stay the first property.
const descriptors: DescriptorArray = map.instance_descriptors;
const descriptor:&DescriptorEntry =
& descriptors.descriptors[kLengthDescriptorIndex];
assert(TaggedEqual(descriptor->key, LengthStringConstant()));
const details: Smi = UnsafeCast<Smi>(descriptor->details);
if ((details & kAttributesReadOnlyMask) != 0) {
goto Bailout;
}
}
macro CreateJSArrayWithElements(implicit context: Context)(array: FixedArray):
JSArray {
const nativeContext: NativeContext = LoadNativeContext(context);
const map: Map =
LoadJSArrayElementsMap(ElementsKind::PACKED_ELEMENTS, nativeContext);
return AllocateJSArray(map, array, array.length);
}
}

View File

@ -1513,8 +1513,8 @@ extern transitioning runtime
CreateDataProperty(implicit context: Context)(JSReceiver, JSAny, JSAny);
namespace runtime {
extern runtime
GetDerivedMap(Context, JSFunction, JSReceiver): Map;
extern runtime
GetDerivedMap(Context, JSFunction, JSReceiver): Map;
}
transitioning builtin FastCreateDataProperty(implicit context: Context)(

View File

@ -23,218 +23,218 @@ Convert<BigInt, MutableBigInt>(i: MutableBigInt): BigInt {
namespace bigint {
const kPositiveSign: uint32 = 0;
const kNegativeSign: uint32 = 1;
const kPositiveSign: uint32 = 0;
const kNegativeSign: uint32 = 1;
extern macro BigIntBuiltinsAssembler::CppAbsoluteAddAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppAbsoluteSubAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppAbsoluteCompare(
BigIntBase, BigIntBase): int32;
extern macro BigIntBuiltinsAssembler::CppAbsoluteAddAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppAbsoluteSubAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppAbsoluteCompare(
BigIntBase, BigIntBase): int32;
extern macro BigIntBuiltinsAssembler::ReadBigIntSign(BigIntBase): uint32;
extern macro BigIntBuiltinsAssembler::ReadBigIntLength(BigIntBase): intptr;
extern macro BigIntBuiltinsAssembler::WriteBigIntSignAndLength(
MutableBigInt, uint32, intptr): void;
extern macro BigIntBuiltinsAssembler::ReadBigIntSign(BigIntBase): uint32;
extern macro BigIntBuiltinsAssembler::ReadBigIntLength(BigIntBase): intptr;
extern macro BigIntBuiltinsAssembler::WriteBigIntSignAndLength(
MutableBigInt, uint32, intptr): void;
extern macro CodeStubAssembler::AllocateBigInt(intptr): MutableBigInt;
extern macro CodeStubAssembler::StoreBigIntDigit(
MutableBigInt, intptr, uintptr): void;
extern macro CodeStubAssembler::LoadBigIntDigit(BigIntBase, intptr): uintptr;
extern macro CodeStubAssembler::AllocateBigInt(intptr): MutableBigInt;
extern macro CodeStubAssembler::StoreBigIntDigit(
MutableBigInt, intptr, uintptr): void;
extern macro CodeStubAssembler::LoadBigIntDigit(BigIntBase, intptr): uintptr;
macro IsCanonicalized(bigint: BigIntBase): bool {
const length = ReadBigIntLength(bigint);
macro IsCanonicalized(bigint: BigIntBase): bool {
const length = ReadBigIntLength(bigint);
if (length == 0) {
return ReadBigIntSign(bigint) == kPositiveSign;
}
return LoadBigIntDigit(bigint, length - 1) != 0;
if (length == 0) {
return ReadBigIntSign(bigint) == kPositiveSign;
}
macro InvertSign(sign: uint32): uint32 {
return sign == kPositiveSign ? kNegativeSign : kPositiveSign;
return LoadBigIntDigit(bigint, length - 1) != 0;
}
macro InvertSign(sign: uint32): uint32 {
return sign == kPositiveSign ? kNegativeSign : kPositiveSign;
}
macro AllocateEmptyBigIntNoThrow(implicit context: Context)(
sign: uint32, length: intptr): MutableBigInt labels BigIntTooBig {
if (length > kBigIntMaxLength) {
goto BigIntTooBig;
}
const result: MutableBigInt = AllocateBigInt(length);
WriteBigIntSignAndLength(result, sign, length);
return result;
}
macro AllocateEmptyBigInt(implicit context: Context)(
sign: uint32, length: intptr): MutableBigInt {
try {
return AllocateEmptyBigIntNoThrow(sign, length) otherwise BigIntTooBig;
} label BigIntTooBig {
ThrowRangeError(MessageTemplate::kBigIntTooBig);
}
}
macro MutableBigIntAbsoluteCompare(x: BigIntBase, y: BigIntBase): int32 {
return CppAbsoluteCompare(x, y);
}
macro MutableBigIntAbsoluteSub(implicit context: Context)(
x: BigInt, y: BigInt, resultSign: uint32): BigInt {
const xlength = ReadBigIntLength(x);
const ylength = ReadBigIntLength(y);
const xsign = ReadBigIntSign(x);
assert(MutableBigIntAbsoluteCompare(x, y) >= 0);
if (xlength == 0) {
assert(ylength == 0);
return x;
}
macro AllocateEmptyBigIntNoThrow(implicit context: Context)(
sign: uint32, length: intptr): MutableBigInt labels BigIntTooBig {
if (length > kBigIntMaxLength) {
goto BigIntTooBig;
}
const result: MutableBigInt = AllocateBigInt(length);
WriteBigIntSignAndLength(result, sign, length);
return result;
if (ylength == 0) {
return resultSign == xsign ? x : BigIntUnaryMinus(x);
}
macro AllocateEmptyBigInt(implicit context: Context)(
sign: uint32, length: intptr): MutableBigInt {
try {
return AllocateEmptyBigIntNoThrow(sign, length) otherwise BigIntTooBig;
} label BigIntTooBig {
ThrowRangeError(MessageTemplate::kBigIntTooBig);
}
const result = AllocateEmptyBigInt(resultSign, xlength);
CppAbsoluteSubAndCanonicalize(result, x, y);
return Convert<BigInt>(result);
}
macro MutableBigIntAbsoluteAdd(implicit context: Context)(
xBigint: BigInt, yBigint: BigInt,
resultSign: uint32): BigInt labels BigIntTooBig {
let xlength = ReadBigIntLength(xBigint);
let ylength = ReadBigIntLength(yBigint);
let x = xBigint;
let y = yBigint;
if (xlength < ylength) {
// Swap x and y so that x is longer.
x = yBigint;
y = xBigint;
const tempLength = xlength;
xlength = ylength;
ylength = tempLength;
}
macro MutableBigIntAbsoluteCompare(x: BigIntBase, y: BigIntBase): int32 {
return CppAbsoluteCompare(x, y);
// case: 0n + 0n
if (xlength == 0) {
assert(ylength == 0);
return x;
}
macro MutableBigIntAbsoluteSub(implicit context: Context)(
x: BigInt, y: BigInt, resultSign: uint32): BigInt {
const xlength = ReadBigIntLength(x);
const ylength = ReadBigIntLength(y);
const xsign = ReadBigIntSign(x);
assert(MutableBigIntAbsoluteCompare(x, y) >= 0);
if (xlength == 0) {
assert(ylength == 0);
return x;
}
if (ylength == 0) {
return resultSign == xsign ? x : BigIntUnaryMinus(x);
}
const result = AllocateEmptyBigInt(resultSign, xlength);
CppAbsoluteSubAndCanonicalize(result, x, y);
return Convert<BigInt>(result);
// case: x + 0n
if (ylength == 0) {
return resultSign == ReadBigIntSign(x) ? x : BigIntUnaryMinus(x);
}
macro MutableBigIntAbsoluteAdd(implicit context: Context)(
xBigint: BigInt, yBigint: BigInt,
resultSign: uint32): BigInt labels BigIntTooBig {
let xlength = ReadBigIntLength(xBigint);
let ylength = ReadBigIntLength(yBigint);
// case: x + y
const result = AllocateEmptyBigIntNoThrow(resultSign, xlength + 1)
otherwise BigIntTooBig;
CppAbsoluteAddAndCanonicalize(result, x, y);
return Convert<BigInt>(result);
}
let x = xBigint;
let y = yBigint;
if (xlength < ylength) {
// Swap x and y so that x is longer.
x = yBigint;
y = xBigint;
const tempLength = xlength;
xlength = ylength;
ylength = tempLength;
}
// case: 0n + 0n
if (xlength == 0) {
assert(ylength == 0);
return x;
}
// case: x + 0n
if (ylength == 0) {
return resultSign == ReadBigIntSign(x) ? x : BigIntUnaryMinus(x);
}
// case: x + y
const result = AllocateEmptyBigIntNoThrow(resultSign, xlength + 1)
otherwise BigIntTooBig;
CppAbsoluteAddAndCanonicalize(result, x, y);
return Convert<BigInt>(result);
macro BigIntAddImpl(implicit context: Context)(x: BigInt, y: BigInt): BigInt
labels BigIntTooBig {
const xsign = ReadBigIntSign(x);
const ysign = ReadBigIntSign(y);
if (xsign == ysign) {
// x + y == x + y
// -x + -y == -(x + y)
return MutableBigIntAbsoluteAdd(x, y, xsign) otherwise BigIntTooBig;
}
macro BigIntAddImpl(implicit context: Context)(x: BigInt, y: BigInt): BigInt
labels BigIntTooBig {
const xsign = ReadBigIntSign(x);
const ysign = ReadBigIntSign(y);
if (xsign == ysign) {
// x + y == x + y
// -x + -y == -(x + y)
return MutableBigIntAbsoluteAdd(x, y, xsign) otherwise BigIntTooBig;
}
// x + -y == x - y == -(y - x)
// -x + y == y - x == -(x - y)
if (MutableBigIntAbsoluteCompare(x, y) >= 0) {
return MutableBigIntAbsoluteSub(x, y, xsign);
}
return MutableBigIntAbsoluteSub(y, x, InvertSign(xsign));
}
// x + -y == x - y == -(y - x)
// -x + y == y - x == -(x - y)
if (MutableBigIntAbsoluteCompare(x, y) >= 0) {
return MutableBigIntAbsoluteSub(x, y, xsign);
}
return MutableBigIntAbsoluteSub(y, x, InvertSign(xsign));
builtin BigIntAddNoThrow(implicit context: Context)(
x: BigInt, y: BigInt): Numeric {
try {
return BigIntAddImpl(x, y) otherwise BigIntTooBig;
} label BigIntTooBig {
// Smi sentinal is used to signal BigIntTooBig exception.
return Convert<Smi>(0);
}
}
builtin BigIntAdd(implicit context: Context)(
xNum: Numeric, yNum: Numeric): BigInt {
try {
const x = Cast<BigInt>(xNum) otherwise MixedTypes;
const y = Cast<BigInt>(yNum) otherwise MixedTypes;
return BigIntAddImpl(x, y) otherwise BigIntTooBig;
} label MixedTypes {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
} label BigIntTooBig {
ThrowRangeError(MessageTemplate::kBigIntTooBig);
}
}
macro BigIntSubtractImpl(implicit context: Context)(
x: BigInt, y: BigInt): BigInt labels BigIntTooBig {
const xsign = ReadBigIntSign(x);
const ysign = ReadBigIntSign(y);
if (xsign != ysign) {
// x - (-y) == x + y
// (-x) - y == -(x + y)
return MutableBigIntAbsoluteAdd(x, y, xsign) otherwise BigIntTooBig;
}
builtin BigIntAddNoThrow(implicit context: Context)(x: BigInt, y: BigInt):
Numeric {
try {
return BigIntAddImpl(x, y) otherwise BigIntTooBig;
} label BigIntTooBig {
// Smi sentinal is used to signal BigIntTooBig exception.
return Convert<Smi>(0);
}
// x - y == -(y - x)
// (-x) - (-y) == y - x == -(x - y)
if (MutableBigIntAbsoluteCompare(x, y) >= 0) {
return MutableBigIntAbsoluteSub(x, y, xsign);
}
return MutableBigIntAbsoluteSub(y, x, InvertSign(xsign));
}
builtin BigIntSubtractNoThrow(implicit context: Context)(
x: BigInt, y: BigInt): Numeric {
try {
return BigIntSubtractImpl(x, y) otherwise BigIntTooBig;
} label BigIntTooBig {
// Smi sentinal is used to signal BigIntTooBig exception.
return Convert<Smi>(0);
}
}
builtin BigIntSubtract(implicit context: Context)(
xNum: Numeric, yNum: Numeric): BigInt {
try {
const x = Cast<BigInt>(xNum) otherwise MixedTypes;
const y = Cast<BigInt>(yNum) otherwise MixedTypes;
return BigIntSubtractImpl(x, y) otherwise BigIntTooBig;
} label MixedTypes {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
} label BigIntTooBig {
ThrowRangeError(MessageTemplate::kBigIntTooBig);
}
}
builtin BigIntUnaryMinus(implicit context: Context)(bigint: BigInt): BigInt {
const length = ReadBigIntLength(bigint);
// There is no -0n.
if (length == 0) {
return bigint;
}
builtin BigIntAdd(implicit context: Context)(xNum: Numeric, yNum: Numeric):
BigInt {
try {
const x = Cast<BigInt>(xNum) otherwise MixedTypes;
const y = Cast<BigInt>(yNum) otherwise MixedTypes;
return BigIntAddImpl(x, y) otherwise BigIntTooBig;
} label MixedTypes {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
} label BigIntTooBig {
ThrowRangeError(MessageTemplate::kBigIntTooBig);
}
}
macro BigIntSubtractImpl(implicit context: Context)(x: BigInt, y: BigInt):
BigInt labels BigIntTooBig {
const xsign = ReadBigIntSign(x);
const ysign = ReadBigIntSign(y);
if (xsign != ysign) {
// x - (-y) == x + y
// (-x) - y == -(x + y)
return MutableBigIntAbsoluteAdd(x, y, xsign) otherwise BigIntTooBig;
}
// x - y == -(y - x)
// (-x) - (-y) == y - x == -(x - y)
if (MutableBigIntAbsoluteCompare(x, y) >= 0) {
return MutableBigIntAbsoluteSub(x, y, xsign);
}
return MutableBigIntAbsoluteSub(y, x, InvertSign(xsign));
}
builtin BigIntSubtractNoThrow(implicit context:
Context)(x: BigInt, y: BigInt): Numeric {
try {
return BigIntSubtractImpl(x, y) otherwise BigIntTooBig;
} label BigIntTooBig {
// Smi sentinal is used to signal BigIntTooBig exception.
return Convert<Smi>(0);
}
}
builtin BigIntSubtract(implicit context:
Context)(xNum: Numeric, yNum: Numeric): BigInt {
try {
const x = Cast<BigInt>(xNum) otherwise MixedTypes;
const y = Cast<BigInt>(yNum) otherwise MixedTypes;
return BigIntSubtractImpl(x, y) otherwise BigIntTooBig;
} label MixedTypes {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
} label BigIntTooBig {
ThrowRangeError(MessageTemplate::kBigIntTooBig);
}
}
builtin BigIntUnaryMinus(implicit context: Context)(bigint: BigInt): BigInt {
const length = ReadBigIntLength(bigint);
// There is no -0n.
if (length == 0) {
return bigint;
}
const result =
AllocateEmptyBigInt(InvertSign(ReadBigIntSign(bigint)), length);
for (let i: intptr = 0; i < length; ++i) {
StoreBigIntDigit(result, i, LoadBigIntDigit(bigint, i));
}
return Convert<BigInt>(result);
const result =
AllocateEmptyBigInt(InvertSign(ReadBigIntSign(bigint)), length);
for (let i: intptr = 0; i < length; ++i) {
StoreBigIntDigit(result, i, LoadBigIntDigit(bigint, i));
}
return Convert<BigInt>(result);
}
} // namespace bigint

View File

@ -3,43 +3,43 @@
// found in the LICENSE file.
namespace boolean {
transitioning macro ThisBooleanValue(implicit context: Context)(
receiver: JSAny, method: constexpr string): Boolean {
return UnsafeCast<Boolean>(
ToThisValue(receiver, PrimitiveType::kBoolean, method));
}
javascript builtin
BooleanConstructor(
js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny,
target: JSFunction)(...arguments): JSAny {
const value = SelectBooleanConstant(ToBoolean(arguments[0]));
if (newTarget == Undefined) {
return value;
}
const map = GetDerivedMap(target, UnsafeCast<JSReceiver>(newTarget));
const obj =
UnsafeCast<JSPrimitiveWrapper>(AllocateFastOrSlowJSObjectFromMap(map));
obj.value = value;
return obj;
}
// ES #sec-boolean.prototype.tostring
transitioning javascript builtin BooleanPrototypeToString(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
// 1. Let b be ? thisBooleanValue(this value).
const b = ThisBooleanValue(receiver, 'Boolean.prototype.toString');
// 2. If b is true, return "true"; else return "false".
return b.to_string;
}
// ES #sec-boolean.prototype.valueof
transitioning javascript builtin BooleanPrototypeValueOf(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
// 1. Return ? thisBooleanValue(this value).
return ThisBooleanValue(receiver, 'Boolean.prototype.valueOf');
}
transitioning macro ThisBooleanValue(implicit context: Context)(
receiver: JSAny, method: constexpr string): Boolean {
return UnsafeCast<Boolean>(
ToThisValue(receiver, PrimitiveType::kBoolean, method));
}
javascript builtin
BooleanConstructor(
js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny,
target: JSFunction)(...arguments): JSAny {
const value = SelectBooleanConstant(ToBoolean(arguments[0]));
if (newTarget == Undefined) {
return value;
}
const map = GetDerivedMap(target, UnsafeCast<JSReceiver>(newTarget));
const obj =
UnsafeCast<JSPrimitiveWrapper>(AllocateFastOrSlowJSObjectFromMap(map));
obj.value = value;
return obj;
}
// ES #sec-boolean.prototype.tostring
transitioning javascript builtin BooleanPrototypeToString(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
// 1. Let b be ? thisBooleanValue(this value).
const b = ThisBooleanValue(receiver, 'Boolean.prototype.toString');
// 2. If b is true, return "true"; else return "false".
return b.to_string;
}
// ES #sec-boolean.prototype.valueof
transitioning javascript builtin BooleanPrototypeValueOf(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
// 1. Return ? thisBooleanValue(this value).
return ThisBooleanValue(receiver, 'Boolean.prototype.valueOf');
}
}

View File

@ -5,216 +5,215 @@
#include 'src/builtins/builtins-string-gen.h'
namespace string {
extern macro StringBuiltinsAssembler::SubString(String, uintptr, uintptr):
String;
extern macro StringBuiltinsAssembler::SubString(
String, uintptr, uintptr): String;
// ES6 #sec-string.prototype.tostring
transitioning javascript builtin
StringPrototypeToString(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
return ToThisValue(
receiver, PrimitiveType::kString, 'String.prototype.toString');
// ES6 #sec-string.prototype.tostring
transitioning javascript builtin
StringPrototypeToString(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
return ToThisValue(
receiver, PrimitiveType::kString, 'String.prototype.toString');
}
// ES6 #sec-string.prototype.valueof
transitioning javascript builtin
StringPrototypeValueOf(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
return ToThisValue(
receiver, PrimitiveType::kString, 'String.prototype.valueOf');
}
extern macro StringBuiltinsAssembler::LoadSurrogatePairAt(
String, intptr, intptr, constexpr UnicodeEncoding): int32;
extern macro StringBuiltinsAssembler::StringFromSingleUTF16EncodedCodePoint(
int32): String;
// This function assumes StringPrimitiveWithNoCustomIteration is true.
transitioning builtin StringToList(implicit context: Context)(string: String):
JSArray {
const kind = ElementsKind::PACKED_ELEMENTS;
const stringLength: intptr = string.length_intptr;
const nativeContext = LoadNativeContext(context);
const map: Map = LoadJSArrayElementsMap(kind, nativeContext);
const array: JSArray = AllocateJSArray(
kind, map, stringLength, SmiTag(stringLength),
AllocationFlag::kAllowLargeObjectAllocation);
const elements = UnsafeCast<FixedArray>(array.elements);
const encoding = UnicodeEncoding::UTF16;
let arrayLength: Smi = 0;
let i: intptr = 0;
while (i < stringLength) {
const ch: int32 = LoadSurrogatePairAt(string, stringLength, i, encoding);
const value: String = StringFromSingleUTF16EncodedCodePoint(ch);
elements[arrayLength] = value;
// Increment and continue the loop.
i = i + value.length_intptr;
arrayLength++;
}
assert(arrayLength >= 0);
assert(SmiTag(stringLength) >= arrayLength);
array.length = arrayLength;
// ES6 #sec-string.prototype.valueof
transitioning javascript builtin
StringPrototypeValueOf(js-implicit context: NativeContext, receiver: JSAny)():
JSAny {
return ToThisValue(
receiver, PrimitiveType::kString, 'String.prototype.valueOf');
}
return array;
}
extern macro StringBuiltinsAssembler::LoadSurrogatePairAt(
String, intptr, intptr, constexpr UnicodeEncoding): int32;
extern macro StringBuiltinsAssembler::StringFromSingleUTF16EncodedCodePoint(
int32): String;
transitioning macro GenerateStringAt(implicit context: Context)(
receiver: JSAny, position: JSAny,
methodName: constexpr string): never labels
IfInBounds(String, uintptr, uintptr), IfOutOfBounds {
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
const string: String = ToThisString(receiver, methodName);
// This function assumes StringPrimitiveWithNoCustomIteration is true.
transitioning builtin StringToList(implicit context: Context)(string: String):
JSArray {
const kind = ElementsKind::PACKED_ELEMENTS;
const stringLength: intptr = string.length_intptr;
// 3. Let position be ? ToInteger(pos).
const indexNumber: Number = ToInteger_Inline(position);
const nativeContext = LoadNativeContext(context);
const map: Map = LoadJSArrayElementsMap(kind, nativeContext);
const array: JSArray = AllocateJSArray(
kind, map, stringLength, SmiTag(stringLength),
AllocationFlag::kAllowLargeObjectAllocation);
const elements = UnsafeCast<FixedArray>(array.elements);
const encoding = UnicodeEncoding::UTF16;
let arrayLength: Smi = 0;
let i: intptr = 0;
while (i < stringLength) {
const ch: int32 = LoadSurrogatePairAt(string, stringLength, i, encoding);
const value: String = StringFromSingleUTF16EncodedCodePoint(ch);
elements[arrayLength] = value;
// Increment and continue the loop.
i = i + value.length_intptr;
arrayLength++;
// Convert the {position} to a uintptr and check that it's in bounds of
// the {string}.
typeswitch (indexNumber) {
case (indexSmi: Smi): {
const length: uintptr = string.length_uintptr;
const index: uintptr = Unsigned(Convert<intptr>(indexSmi));
// Max string length fits Smi range, so we can do an unsigned bounds
// check.
const kMaxStringLengthFitsSmi: constexpr bool =
kStringMaxLengthUintptr < kSmiMaxValue;
StaticAssert(kMaxStringLengthFitsSmi);
if (index >= length) goto IfOutOfBounds;
goto IfInBounds(string, index, length);
}
assert(arrayLength >= 0);
assert(SmiTag(stringLength) >= arrayLength);
array.length = arrayLength;
return array;
}
transitioning macro GenerateStringAt(implicit context: Context)(
receiver: JSAny, position: JSAny,
methodName: constexpr string): never labels
IfInBounds(String, uintptr, uintptr), IfOutOfBounds {
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
const string: String = ToThisString(receiver, methodName);
// 3. Let position be ? ToInteger(pos).
const indexNumber: Number = ToInteger_Inline(position);
// Convert the {position} to a uintptr and check that it's in bounds of
// the {string}.
typeswitch (indexNumber) {
case (indexSmi: Smi): {
const length: uintptr = string.length_uintptr;
const index: uintptr = Unsigned(Convert<intptr>(indexSmi));
// Max string length fits Smi range, so we can do an unsigned bounds
// check.
const kMaxStringLengthFitsSmi: constexpr bool =
kStringMaxLengthUintptr < kSmiMaxValue;
StaticAssert(kMaxStringLengthFitsSmi);
if (index >= length) goto IfOutOfBounds;
goto IfInBounds(string, index, length);
}
case (indexHeapNumber: HeapNumber): {
assert(IsNumberNormalized(indexHeapNumber));
// Valid string indices fit into Smi range, so HeapNumber index is
// definitely an out of bounds case.
goto IfOutOfBounds;
}
case (indexHeapNumber: HeapNumber): {
assert(IsNumberNormalized(indexHeapNumber));
// Valid string indices fit into Smi range, so HeapNumber index is
// definitely an out of bounds case.
goto IfOutOfBounds;
}
}
// ES6 #sec-string.prototype.charat
transitioning javascript builtin StringPrototypeCharAt(
js-implicit context: NativeContext,
receiver: JSAny)(position: JSAny): JSAny {
try {
GenerateStringAt(receiver, position, 'String.prototype.charAt')
otherwise IfInBounds, IfOutOfBounds;
} label IfInBounds(string: String, index: uintptr, _length: uintptr) {
const code: int32 = StringCharCodeAt(string, index);
return StringFromSingleCharCode(code);
} label IfOutOfBounds {
return kEmptyString;
}
}
// ES6 #sec-string.prototype.charcodeat
transitioning javascript builtin StringPrototypeCharCodeAt(
js-implicit context: NativeContext,
receiver: JSAny)(position: JSAny): JSAny {
try {
GenerateStringAt(receiver, position, 'String.prototype.charCodeAt')
otherwise IfInBounds, IfOutOfBounds;
} label IfInBounds(string: String, index: uintptr, _length: uintptr) {
const code: int32 = StringCharCodeAt(string, index);
return Convert<Smi>(code);
} label IfOutOfBounds {
return kNaN;
}
}
// ES6 #sec-string.prototype.codepointat
transitioning javascript builtin StringPrototypeCodePointAt(
js-implicit context: NativeContext,
receiver: JSAny)(position: JSAny): JSAny {
try {
GenerateStringAt(receiver, position, 'String.prototype.codePointAt')
otherwise IfInBounds, IfOutOfBounds;
} label IfInBounds(string: String, index: uintptr, length: uintptr) {
// This is always a call to a builtin from Javascript, so we need to
// produce UTF32.
const code: int32 = LoadSurrogatePairAt(
string, Signed(length), Signed(index), UnicodeEncoding::UTF32);
return Convert<Smi>(code);
} label IfOutOfBounds {
return Undefined;
}
}
// ES6 String.prototype.concat(...args)
// ES6 #sec-string.prototype.concat
transitioning javascript builtin StringPrototypeConcat(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSAny {
// Check that {receiver} is coercible to Object and convert it to a String.
let string: String = ToThisString(receiver, 'String.prototype.concat');
// Concatenate all the arguments passed to this builtin.
const length: intptr = Convert<intptr>(arguments.length);
for (let i: intptr = 0; i < length; i++) {
const temp: String = ToString_Inline(arguments[i]);
string = string + temp;
}
return string;
}
extern transitioning runtime
SymbolDescriptiveString(implicit context: Context)(Symbol): String;
// ES #sec-string-constructor
// https://tc39.github.io/ecma262/#sec-string-constructor
transitioning javascript builtin StringConstructor(
js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny,
target: JSFunction)(...arguments): JSAny {
const length: intptr = Convert<intptr>(arguments.length);
let s: String;
// 1. If no arguments were passed to this function invocation, let s be "".
if (length == 0) {
s = EmptyStringConstant();
} else {
// 2. Else,
// 2. a. If NewTarget is undefined and Type(value) is Symbol, return
// SymbolDescriptiveString(value).
if (newTarget == Undefined) {
typeswitch (arguments[0]) {
case (value: Symbol): {
return SymbolDescriptiveString(value);
}
case (JSAny): {
}
}
}
// 2. b. Let s be ? ToString(value).
s = ToString_Inline(arguments[0]);
}
// 3. If NewTarget is undefined, return s.
if (newTarget == Undefined) {
return s;
}
// 4. Return ! StringCreate(s, ? GetPrototypeFromConstructor(NewTarget,
// "%String.prototype%")).
const map = GetDerivedMap(target, UnsafeCast<JSReceiver>(newTarget));
const obj =
UnsafeCast<JSPrimitiveWrapper>(AllocateFastOrSlowJSObjectFromMap(map));
obj.value = s;
return obj;
}
transitioning builtin StringAddConvertLeft(implicit context: Context)(
left: JSAny, right: String): String {
return ToStringImpl(context, ToPrimitiveDefault(left)) + right;
}
transitioning builtin StringAddConvertRight(implicit context: Context)(
left: String, right: JSAny): String {
return left + ToStringImpl(context, ToPrimitiveDefault(right));
}
builtin StringCharAt(implicit context: Context)(
receiver: String, position: uintptr): String {
// Load the character code at the {position} from the {receiver}.
const code: int32 = StringCharCodeAt(receiver, position);
// And return the single character string with only that {code}
return StringFromSingleCharCode(code);
}
}
// ES6 #sec-string.prototype.charat
transitioning javascript builtin StringPrototypeCharAt(
js-implicit context: NativeContext,
receiver: JSAny)(position: JSAny): JSAny {
try {
GenerateStringAt(receiver, position, 'String.prototype.charAt')
otherwise IfInBounds, IfOutOfBounds;
} label IfInBounds(string: String, index: uintptr, _length: uintptr) {
const code: int32 = StringCharCodeAt(string, index);
return StringFromSingleCharCode(code);
} label IfOutOfBounds {
return kEmptyString;
}
}
// ES6 #sec-string.prototype.charcodeat
transitioning javascript builtin StringPrototypeCharCodeAt(
js-implicit context: NativeContext,
receiver: JSAny)(position: JSAny): JSAny {
try {
GenerateStringAt(receiver, position, 'String.prototype.charCodeAt')
otherwise IfInBounds, IfOutOfBounds;
} label IfInBounds(string: String, index: uintptr, _length: uintptr) {
const code: int32 = StringCharCodeAt(string, index);
return Convert<Smi>(code);
} label IfOutOfBounds {
return kNaN;
}
}
// ES6 #sec-string.prototype.codepointat
transitioning javascript builtin StringPrototypeCodePointAt(
js-implicit context: NativeContext,
receiver: JSAny)(position: JSAny): JSAny {
try {
GenerateStringAt(receiver, position, 'String.prototype.codePointAt')
otherwise IfInBounds, IfOutOfBounds;
} label IfInBounds(string: String, index: uintptr, length: uintptr) {
// This is always a call to a builtin from Javascript, so we need to
// produce UTF32.
const code: int32 = LoadSurrogatePairAt(
string, Signed(length), Signed(index), UnicodeEncoding::UTF32);
return Convert<Smi>(code);
} label IfOutOfBounds {
return Undefined;
}
}
// ES6 String.prototype.concat(...args)
// ES6 #sec-string.prototype.concat
transitioning javascript builtin StringPrototypeConcat(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// Check that {receiver} is coercible to Object and convert it to a String.
let string: String = ToThisString(receiver, 'String.prototype.concat');
// Concatenate all the arguments passed to this builtin.
const length: intptr = Convert<intptr>(arguments.length);
for (let i: intptr = 0; i < length; i++) {
const temp: String = ToString_Inline(arguments[i]);
string = string + temp;
}
return string;
}
extern transitioning runtime
SymbolDescriptiveString(implicit context: Context)(Symbol): String;
// ES #sec-string-constructor
// https://tc39.github.io/ecma262/#sec-string-constructor
transitioning javascript builtin StringConstructor(
js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny,
target: JSFunction)(...arguments): JSAny {
const length: intptr = Convert<intptr>(arguments.length);
let s: String;
// 1. If no arguments were passed to this function invocation, let s be "".
if (length == 0) {
s = EmptyStringConstant();
} else {
// 2. Else,
// 2. a. If NewTarget is undefined and Type(value) is Symbol, return
// SymbolDescriptiveString(value).
if (newTarget == Undefined) {
typeswitch (arguments[0]) {
case (value: Symbol): {
return SymbolDescriptiveString(value);
}
case (JSAny): {
}
}
}
// 2. b. Let s be ? ToString(value).
s = ToString_Inline(arguments[0]);
}
// 3. If NewTarget is undefined, return s.
if (newTarget == Undefined) {
return s;
}
// 4. Return ! StringCreate(s, ? GetPrototypeFromConstructor(NewTarget,
// "%String.prototype%")).
const map = GetDerivedMap(target, UnsafeCast<JSReceiver>(newTarget));
const obj =
UnsafeCast<JSPrimitiveWrapper>(AllocateFastOrSlowJSObjectFromMap(map));
obj.value = s;
return obj;
}
transitioning builtin StringAddConvertLeft(implicit context: Context)(
left: JSAny, right: String): String {
return ToStringImpl(context, ToPrimitiveDefault(left)) + right;
}
transitioning builtin StringAddConvertRight(implicit context: Context)(
left: String, right: JSAny): String {
return left + ToStringImpl(context, ToPrimitiveDefault(right));
}
builtin StringCharAt(implicit context: Context)(
receiver: String, position: uintptr): String {
// Load the character code at the {position} from the {receiver}.
const code: int32 = StringCharCodeAt(receiver, position);
// And return the single character string with only that {code}
return StringFromSingleCharCode(code);
}
}

View File

@ -5,53 +5,53 @@
#include 'src/builtins/builtins-collections-gen.h'
namespace collections {
@export
macro LoadKeyValuePairNoSideEffects(implicit context: Context)(o: JSAny):
KeyValuePair labels MayHaveSideEffects {
typeswitch (o) {
case (a: FastJSArray): {
const length: Smi = a.length;
typeswitch (a.elements) {
case (elements: FixedArray): {
return KeyValuePair{
key: length > 0 ? array::LoadElementOrUndefined(elements, 0) :
Undefined,
value: length > 1 ? array::LoadElementOrUndefined(elements, 1) :
Undefined
};
}
case (elements: FixedDoubleArray): {
return KeyValuePair{
key: length > 0 ? array::LoadElementOrUndefined(elements, 0) :
Undefined,
value: length > 1 ? array::LoadElementOrUndefined(elements, 1) :
Undefined
};
}
case (FixedArrayBase): deferred {
unreachable;
}
@export
macro LoadKeyValuePairNoSideEffects(implicit context: Context)(o: JSAny):
KeyValuePair labels MayHaveSideEffects {
typeswitch (o) {
case (a: FastJSArray): {
const length: Smi = a.length;
typeswitch (a.elements) {
case (elements: FixedArray): {
return KeyValuePair{
key: length > 0 ? array::LoadElementOrUndefined(elements, 0) :
Undefined,
value: length > 1 ? array::LoadElementOrUndefined(elements, 1) :
Undefined
};
}
case (elements: FixedDoubleArray): {
return KeyValuePair{
key: length > 0 ? array::LoadElementOrUndefined(elements, 0) :
Undefined,
value: length > 1 ? array::LoadElementOrUndefined(elements, 1) :
Undefined
};
}
case (FixedArrayBase): deferred {
unreachable;
}
}
case (JSReceiver): {
goto MayHaveSideEffects;
}
case (o: JSAny): deferred {
ThrowTypeError(MessageTemplate::kIteratorValueNotAnObject, o);
}
}
}
@export
transitioning macro LoadKeyValuePair(implicit context: Context)(o: JSAny):
KeyValuePair {
try {
return LoadKeyValuePairNoSideEffects(o) otherwise Generic;
} label Generic {
return KeyValuePair{
key: GetProperty(o, Convert<Smi>(0)),
value: GetProperty(o, Convert<Smi>(1))
};
case (JSReceiver): {
goto MayHaveSideEffects;
}
case (o: JSAny): deferred {
ThrowTypeError(MessageTemplate::kIteratorValueNotAnObject, o);
}
}
}
@export
transitioning macro LoadKeyValuePair(implicit context: Context)(o: JSAny):
KeyValuePair {
try {
return LoadKeyValuePairNoSideEffects(o) otherwise Generic;
} label Generic {
return KeyValuePair{
key: GetProperty(o, Convert<Smi>(0)),
value: GetProperty(o, Convert<Smi>(1))
};
}
}
}

View File

@ -3,16 +3,16 @@
// found in the LICENSE file.
namespace console {
extern builtin ConsoleAssert(implicit context:
Context)(JSFunction, JSAny, int32): JSAny;
extern builtin ConsoleAssert(implicit context: Context)(
JSFunction, JSAny, int32): JSAny;
javascript builtin FastConsoleAssert(
js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny,
target: JSFunction)(...arguments): JSAny {
if (ToBoolean(arguments[0])) {
return Undefined;
} else {
tail ConsoleAssert(target, newTarget, Convert<int32>(arguments.length));
}
javascript builtin FastConsoleAssert(
js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny,
target: JSFunction)(...arguments): JSAny {
if (ToBoolean(arguments[0])) {
return Undefined;
} else {
tail ConsoleAssert(target, newTarget, Convert<int32>(arguments.length));
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,104 +3,103 @@
// found in the LICENSE file.
namespace runtime {
extern runtime
ShrinkFinalizationRegistryUnregisterTokenMap(Context, JSFinalizationRegistry):
void;
extern runtime
ShrinkFinalizationRegistryUnregisterTokenMap(
Context, JSFinalizationRegistry): void;
}
namespace weakref {
extern transitioning macro
RemoveFinalizationRegistryCellFromUnregisterTokenMap(
JSFinalizationRegistry, WeakCell): void;
extern transitioning macro
RemoveFinalizationRegistryCellFromUnregisterTokenMap(
JSFinalizationRegistry, WeakCell): void;
macro SplitOffTail(weakCell: WeakCell): WeakCell|Undefined {
const weakCellTail = weakCell.next;
weakCell.next = Undefined;
typeswitch (weakCellTail) {
case (Undefined): {
}
case (tailIsNowAHead: WeakCell): {
assert(tailIsNowAHead.prev == weakCell);
tailIsNowAHead.prev = Undefined;
}
macro SplitOffTail(weakCell: WeakCell): WeakCell|Undefined {
const weakCellTail = weakCell.next;
weakCell.next = Undefined;
typeswitch (weakCellTail) {
case (Undefined): {
}
return weakCellTail;
}
transitioning macro
PopClearedCell(finalizationRegistry: JSFinalizationRegistry): WeakCell
|Undefined {
typeswitch (finalizationRegistry.cleared_cells) {
case (Undefined): {
return Undefined;
}
case (weakCell: WeakCell): {
assert(weakCell.prev == Undefined);
finalizationRegistry.cleared_cells = SplitOffTail(weakCell);
// If the WeakCell has an unregister token, remove the cell from the
// unregister token linked lists and and the unregister token from
// key_map. This doesn't shrink key_map, which is done manually after
// the cleanup loop to avoid a runtime call.
if (weakCell.unregister_token != Undefined) {
RemoveFinalizationRegistryCellFromUnregisterTokenMap(
finalizationRegistry, weakCell);
}
return weakCell;
}
case (tailIsNowAHead: WeakCell): {
assert(tailIsNowAHead.prev == weakCell);
tailIsNowAHead.prev = Undefined;
}
}
return weakCellTail;
}
transitioning macro
FinalizationRegistryCleanupLoop(implicit context: Context)(
finalizationRegistry: JSFinalizationRegistry, callback: Callable) {
while (true) {
const weakCellHead = PopClearedCell(finalizationRegistry);
typeswitch (weakCellHead) {
case (Undefined): {
break;
}
case (weakCell: WeakCell): {
try {
Call(context, callback, Undefined, weakCell.holdings);
} catch (e) {
runtime::ShrinkFinalizationRegistryUnregisterTokenMap(
context, finalizationRegistry);
ReThrow(context, e);
}
}
transitioning macro
PopClearedCell(finalizationRegistry: JSFinalizationRegistry): WeakCell|
Undefined {
typeswitch (finalizationRegistry.cleared_cells) {
case (Undefined): {
return Undefined;
}
case (weakCell: WeakCell): {
assert(weakCell.prev == Undefined);
finalizationRegistry.cleared_cells = SplitOffTail(weakCell);
// If the WeakCell has an unregister token, remove the cell from the
// unregister token linked lists and and the unregister token from
// key_map. This doesn't shrink key_map, which is done manually after
// the cleanup loop to avoid a runtime call.
if (weakCell.unregister_token != Undefined) {
RemoveFinalizationRegistryCellFromUnregisterTokenMap(
finalizationRegistry, weakCell);
}
return weakCell;
}
runtime::ShrinkFinalizationRegistryUnregisterTokenMap(
context, finalizationRegistry);
}
transitioning javascript builtin
FinalizationRegistryPrototypeCleanupSome(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSAny {
// 1. Let finalizationRegistry be the this value.
//
// 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
const methodName: constexpr string =
'FinalizationRegistry.prototype.cleanupSome';
const finalizationRegistry =
Cast<JSFinalizationRegistry>(receiver) otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, methodName, receiver);
let callback: Callable;
if (arguments[0] != Undefined) {
// 4. If callback is not undefined and IsCallable(callback) is
// false, throw a TypeError exception.
callback = Cast<Callable>(arguments[0]) otherwise ThrowTypeError(
MessageTemplate::kWeakRefsCleanupMustBeCallable, arguments[0]);
} else {
callback = finalizationRegistry.cleanup;
}
FinalizationRegistryCleanupLoop(finalizationRegistry, callback);
return Undefined;
}
}
transitioning macro
FinalizationRegistryCleanupLoop(implicit context: Context)(
finalizationRegistry: JSFinalizationRegistry, callback: Callable) {
while (true) {
const weakCellHead = PopClearedCell(finalizationRegistry);
typeswitch (weakCellHead) {
case (Undefined): {
break;
}
case (weakCell: WeakCell): {
try {
Call(context, callback, Undefined, weakCell.holdings);
} catch (e) {
runtime::ShrinkFinalizationRegistryUnregisterTokenMap(
context, finalizationRegistry);
ReThrow(context, e);
}
}
}
}
runtime::ShrinkFinalizationRegistryUnregisterTokenMap(
context, finalizationRegistry);
}
transitioning javascript builtin
FinalizationRegistryPrototypeCleanupSome(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// 1. Let finalizationRegistry be the this value.
//
// 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
const methodName: constexpr string =
'FinalizationRegistry.prototype.cleanupSome';
const finalizationRegistry =
Cast<JSFinalizationRegistry>(receiver) otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, methodName, receiver);
let callback: Callable;
if (arguments[0] != Undefined) {
// 4. If callback is not undefined and IsCallable(callback) is
// false, throw a TypeError exception.
callback = Cast<Callable>(arguments[0]) otherwise ThrowTypeError(
MessageTemplate::kWeakRefsCleanupMustBeCallable, arguments[0]);
} else {
callback = finalizationRegistry.cleanup;
}
FinalizationRegistryCleanupLoop(finalizationRegistry, callback);
return Undefined;
}
}

View File

@ -3,48 +3,48 @@
// found in the LICENSE file.
namespace growable_fixed_array {
// TODO(pwong): Support FixedTypedArrays.
struct GrowableFixedArray {
macro Push(obj: Object) {
this.EnsureCapacity();
this.array.objects[this.length++] = obj;
// TODO(pwong): Support FixedTypedArrays.
struct GrowableFixedArray {
macro Push(obj: Object) {
this.EnsureCapacity();
this.array.objects[this.length++] = obj;
}
macro ResizeFixedArray(newCapacity: intptr): FixedArray {
assert(this.length >= 0);
assert(newCapacity >= 0);
assert(newCapacity >= this.length);
const first: intptr = 0;
return ExtractFixedArray(this.array, first, this.length, newCapacity);
}
macro EnsureCapacity() {
assert(this.length <= this.capacity);
if (this.capacity == this.length) {
// Growth rate is analog to JSObject::NewElementsCapacity:
// new_capacity = (current_capacity + (current_capacity >> 1)) + 16.
this.capacity = this.capacity + (this.capacity >> 1) + 16;
this.array = this.ResizeFixedArray(this.capacity);
}
macro ResizeFixedArray(newCapacity: intptr): FixedArray {
assert(this.length >= 0);
assert(newCapacity >= 0);
assert(newCapacity >= this.length);
const first: intptr = 0;
return ExtractFixedArray(this.array, first, this.length, newCapacity);
}
macro EnsureCapacity() {
assert(this.length <= this.capacity);
if (this.capacity == this.length) {
// Growth rate is analog to JSObject::NewElementsCapacity:
// new_capacity = (current_capacity + (current_capacity >> 1)) + 16.
this.capacity = this.capacity + (this.capacity >> 1) + 16;
this.array = this.ResizeFixedArray(this.capacity);
}
}
macro ToFixedArray(): FixedArray {
return this.ResizeFixedArray(this.length);
}
macro ToJSArray(implicit context: Context)(): JSArray {
const nativeContext: NativeContext = LoadNativeContext(context);
const map: Map =
LoadJSArrayElementsMap(ElementsKind::PACKED_ELEMENTS, nativeContext);
const fixedArray: FixedArray = this.ResizeFixedArray(this.length);
const lengthSmi = Convert<Smi>(this.length);
return AllocateJSArray(map, fixedArray, lengthSmi);
}
array: FixedArray;
// TODO(v8:4153): make capacity and length uintptr
capacity: intptr;
length: intptr;
}
macro ToFixedArray(): FixedArray {
return this.ResizeFixedArray(this.length);
}
macro NewGrowableFixedArray(): GrowableFixedArray {
return GrowableFixedArray{array: kEmptyFixedArray, capacity: 0, length: 0};
macro ToJSArray(implicit context: Context)(): JSArray {
const nativeContext: NativeContext = LoadNativeContext(context);
const map: Map =
LoadJSArrayElementsMap(ElementsKind::PACKED_ELEMENTS, nativeContext);
const fixedArray: FixedArray = this.ResizeFixedArray(this.length);
const lengthSmi = Convert<Smi>(this.length);
return AllocateJSArray(map, fixedArray, lengthSmi);
}
array: FixedArray;
// TODO(v8:4153): make capacity and length uintptr
capacity: intptr;
length: intptr;
}
macro NewGrowableFixedArray(): GrowableFixedArray {
return GrowableFixedArray{array: kEmptyFixedArray, capacity: 0, length: 0};
}
}

View File

@ -4,124 +4,123 @@
namespace ic_callable {
extern macro IncrementCallCount(FeedbackVector, uintptr): void;
extern macro IncrementCallCount(FeedbackVector, uintptr): void;
macro IsMonomorphic(feedback: MaybeObject, target: JSAny): bool {
return IsWeakReferenceToObject(feedback, target);
macro IsMonomorphic(feedback: MaybeObject, target: JSAny): bool {
return IsWeakReferenceToObject(feedback, target);
}
macro InSameNativeContext(lhs: Context, rhs: Context): bool {
return LoadNativeContext(lhs) == LoadNativeContext(rhs);
}
macro MaybeObjectToStrong(maybeObject: MaybeObject):
HeapObject labels IfCleared {
assert(IsWeakOrCleared(maybeObject));
const weakObject = %RawDownCast<Weak<HeapObject>>(maybeObject);
return WeakToStrong(weakObject) otherwise IfCleared;
}
macro TryInitializeAsMonomorphic(implicit context: Context)(
maybeTarget: JSAny, feedbackVector: FeedbackVector,
slotId: uintptr): void labels TransitionToMegamorphic {
const targetHeapObject =
Cast<HeapObject>(maybeTarget) otherwise TransitionToMegamorphic;
let unwrappedTarget = targetHeapObject;
while (Is<JSBoundFunction>(unwrappedTarget)) {
unwrappedTarget =
UnsafeCast<JSBoundFunction>(unwrappedTarget).bound_target_function;
}
macro InSameNativeContext(lhs: Context, rhs: Context): bool {
return LoadNativeContext(lhs) == LoadNativeContext(rhs);
const unwrappedTargetJSFunction =
Cast<JSFunction>(unwrappedTarget) otherwise TransitionToMegamorphic;
if (!InSameNativeContext(unwrappedTargetJSFunction.context, context)) {
goto TransitionToMegamorphic;
}
macro MaybeObjectToStrong(maybeObject: MaybeObject):
HeapObject labels IfCleared {
assert(IsWeakOrCleared(maybeObject));
const weakObject = %RawDownCast<Weak<HeapObject>>(maybeObject);
return WeakToStrong(weakObject) otherwise IfCleared;
}
ic::StoreWeakReferenceInFeedbackVector(
feedbackVector, slotId, targetHeapObject);
ic::ReportFeedbackUpdate(feedbackVector, slotId, 'Call:Initialize');
}
macro TryInitializeAsMonomorphic(implicit context: Context)(
maybeTarget: JSAny, feedbackVector: FeedbackVector,
slotId: uintptr): void labels TransitionToMegamorphic {
const targetHeapObject =
Cast<HeapObject>(maybeTarget) otherwise TransitionToMegamorphic;
macro TransitionToMegamorphic(implicit context: Context)(
feedbackVector: FeedbackVector, slotId: uintptr): void {
ic::StoreFeedbackVectorSlot(feedbackVector, slotId, ic::kMegamorphicSymbol);
ic::ReportFeedbackUpdate(
feedbackVector, slotId, 'Call:TransitionMegamorphic');
}
let unwrappedTarget = targetHeapObject;
while (Is<JSBoundFunction>(unwrappedTarget)) {
unwrappedTarget =
UnsafeCast<JSBoundFunction>(unwrappedTarget).bound_target_function;
}
macro CollectCallFeedback(
maybeTarget: JSAny, context: Context,
maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void {
const feedbackVector =
Cast<FeedbackVector>(maybeFeedbackVector) otherwise return;
IncrementCallCount(feedbackVector, slotId);
const unwrappedTargetJSFunction =
Cast<JSFunction>(unwrappedTarget) otherwise TransitionToMegamorphic;
if (!InSameNativeContext(unwrappedTargetJSFunction.context, context)) {
try {
const feedback: MaybeObject =
ic::LoadFeedbackVectorSlot(feedbackVector, slotId);
if (IsMonomorphic(feedback, maybeTarget)) return;
if (ic::IsMegamorphic(feedback)) return;
if (ic::IsUninitialized(feedback)) goto TryInitializeAsMonomorphic;
// If cleared, we have a new chance to become monomorphic.
const feedbackValue: HeapObject =
MaybeObjectToStrong(feedback) otherwise TryInitializeAsMonomorphic;
// Try transitioning to a feedback cell.
// Check if {target}s feedback cell matches the {feedbackValue}.
const target =
Cast<JSFunction>(maybeTarget) otherwise TransitionToMegamorphic;
const targetFeedbackCell: FeedbackCell = target.feedback_cell;
if (TaggedEqual(feedbackValue, targetFeedbackCell)) return;
// Check if {target} and {feedbackValue} are both JSFunctions with
// the same feedback vector cell, and that those functions were
// actually compiled already.
const feedbackValueJSFunction =
Cast<JSFunction>(feedbackValue) otherwise TransitionToMegamorphic;
const feedbackCell: FeedbackCell = feedbackValueJSFunction.feedback_cell;
if (!TaggedEqual(feedbackCell, targetFeedbackCell))
goto TransitionToMegamorphic;
}
ic::StoreWeakReferenceInFeedbackVector(
feedbackVector, slotId, targetHeapObject);
ic::ReportFeedbackUpdate(feedbackVector, slotId, 'Call:Initialize');
feedbackVector, slotId, feedbackCell);
ic::ReportFeedbackUpdate(feedbackVector, slotId, 'Call:FeedbackVectorCell');
} label TryInitializeAsMonomorphic {
TryInitializeAsMonomorphic(maybeTarget, feedbackVector, slotId)
otherwise TransitionToMegamorphic;
} label TransitionToMegamorphic {
TransitionToMegamorphic(feedbackVector, slotId);
}
}
macro TransitionToMegamorphic(implicit context: Context)(
feedbackVector: FeedbackVector, slotId: uintptr): void {
ic::StoreFeedbackVectorSlot(feedbackVector, slotId, ic::kMegamorphicSymbol);
ic::ReportFeedbackUpdate(
feedbackVector, slotId, 'Call:TransitionMegamorphic');
}
macro CollectCallFeedback(
maybeTarget: JSAny, context: Context,
maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void {
const feedbackVector =
Cast<FeedbackVector>(maybeFeedbackVector) otherwise return;
IncrementCallCount(feedbackVector, slotId);
try {
const feedback: MaybeObject =
ic::LoadFeedbackVectorSlot(feedbackVector, slotId);
if (IsMonomorphic(feedback, maybeTarget)) return;
if (ic::IsMegamorphic(feedback)) return;
if (ic::IsUninitialized(feedback)) goto TryInitializeAsMonomorphic;
// If cleared, we have a new chance to become monomorphic.
const feedbackValue: HeapObject =
MaybeObjectToStrong(feedback) otherwise TryInitializeAsMonomorphic;
// Try transitioning to a feedback cell.
// Check if {target}s feedback cell matches the {feedbackValue}.
const target =
Cast<JSFunction>(maybeTarget) otherwise TransitionToMegamorphic;
const targetFeedbackCell: FeedbackCell = target.feedback_cell;
if (TaggedEqual(feedbackValue, targetFeedbackCell)) return;
// Check if {target} and {feedbackValue} are both JSFunctions with
// the same feedback vector cell, and that those functions were
// actually compiled already.
const feedbackValueJSFunction =
Cast<JSFunction>(feedbackValue) otherwise TransitionToMegamorphic;
const feedbackCell: FeedbackCell = feedbackValueJSFunction.feedback_cell;
if (!TaggedEqual(feedbackCell, targetFeedbackCell))
goto TransitionToMegamorphic;
ic::StoreWeakReferenceInFeedbackVector(
feedbackVector, slotId, feedbackCell);
ic::ReportFeedbackUpdate(
feedbackVector, slotId, 'Call:FeedbackVectorCell');
} label TryInitializeAsMonomorphic {
TryInitializeAsMonomorphic(maybeTarget, feedbackVector, slotId)
otherwise TransitionToMegamorphic;
} label TransitionToMegamorphic {
TransitionToMegamorphic(feedbackVector, slotId);
}
}
macro CollectInstanceOfFeedback(
maybeTarget: JSAny, context: Context,
maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void {
const feedbackVector =
Cast<FeedbackVector>(maybeFeedbackVector) otherwise return;
// Note: The call count is not incremented.
try {
const feedback: MaybeObject =
ic::LoadFeedbackVectorSlot(feedbackVector, slotId);
if (IsMonomorphic(feedback, maybeTarget)) return;
if (ic::IsMegamorphic(feedback)) return;
if (ic::IsUninitialized(feedback)) goto TryInitializeAsMonomorphic;
// If cleared, we have a new chance to become monomorphic.
const _feedbackValue: HeapObject =
MaybeObjectToStrong(feedback) otherwise TryInitializeAsMonomorphic;
goto TransitionToMegamorphic;
} label TryInitializeAsMonomorphic {
TryInitializeAsMonomorphic(maybeTarget, feedbackVector, slotId)
otherwise TransitionToMegamorphic;
} label TransitionToMegamorphic {
TransitionToMegamorphic(feedbackVector, slotId);
}
macro CollectInstanceOfFeedback(
maybeTarget: JSAny, context: Context,
maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void {
const feedbackVector =
Cast<FeedbackVector>(maybeFeedbackVector) otherwise return;
// Note: The call count is not incremented.
try {
const feedback: MaybeObject =
ic::LoadFeedbackVectorSlot(feedbackVector, slotId);
if (IsMonomorphic(feedback, maybeTarget)) return;
if (ic::IsMegamorphic(feedback)) return;
if (ic::IsUninitialized(feedback)) goto TryInitializeAsMonomorphic;
// If cleared, we have a new chance to become monomorphic.
const _feedbackValue: HeapObject =
MaybeObjectToStrong(feedback) otherwise TryInitializeAsMonomorphic;
goto TransitionToMegamorphic;
} label TryInitializeAsMonomorphic {
TryInitializeAsMonomorphic(maybeTarget, feedbackVector, slotId)
otherwise TransitionToMegamorphic;
} label TransitionToMegamorphic {
TransitionToMegamorphic(feedbackVector, slotId);
}
}
} // namespace ic_callable

View File

@ -4,45 +4,44 @@
namespace ic {
// --- The public interface (forwards to the actual implementation).
@export
macro CollectCallFeedback(
maybeTarget: JSAny, context: Context,
maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void {
ic_callable::CollectCallFeedback(
maybeTarget, context, maybeFeedbackVector, slotId);
}
@export
macro CollectInstanceOfFeedback(
maybeTarget: JSAny, context: Context,
maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void {
ic_callable::CollectInstanceOfFeedback(
maybeTarget, context, maybeFeedbackVector, slotId);
}
// --- Common functionality.
extern macro MegamorphicSymbolConstant(): Symbol;
extern macro UninitializedSymbolConstant(): Symbol;
const kMegamorphicSymbol: Symbol = MegamorphicSymbolConstant();
const kUninitializedSymbol: Symbol = UninitializedSymbolConstant();
macro IsMegamorphic(feedback: MaybeObject): bool {
return TaggedEqual(feedback, kMegamorphicSymbol);
}
macro IsUninitialized(feedback: MaybeObject): bool {
return TaggedEqual(feedback, kUninitializedSymbol);
}
extern macro LoadFeedbackVectorSlot(FeedbackVector, uintptr): MaybeObject;
extern macro StoreFeedbackVectorSlot(FeedbackVector, uintptr, MaybeObject):
void;
extern macro StoreWeakReferenceInFeedbackVector(
FeedbackVector, uintptr, HeapObject): MaybeObject;
extern macro ReportFeedbackUpdate(FeedbackVector, uintptr, constexpr string);
// --- The public interface (forwards to the actual implementation).
@export
macro CollectCallFeedback(
maybeTarget: JSAny, context: Context,
maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void {
ic_callable::CollectCallFeedback(
maybeTarget, context, maybeFeedbackVector, slotId);
}
@export
macro CollectInstanceOfFeedback(
maybeTarget: JSAny, context: Context,
maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void {
ic_callable::CollectInstanceOfFeedback(
maybeTarget, context, maybeFeedbackVector, slotId);
}
// --- Common functionality.
extern macro MegamorphicSymbolConstant(): Symbol;
extern macro UninitializedSymbolConstant(): Symbol;
const kMegamorphicSymbol: Symbol = MegamorphicSymbolConstant();
const kUninitializedSymbol: Symbol = UninitializedSymbolConstant();
macro IsMegamorphic(feedback: MaybeObject): bool {
return TaggedEqual(feedback, kMegamorphicSymbol);
}
macro IsUninitialized(feedback: MaybeObject): bool {
return TaggedEqual(feedback, kUninitializedSymbol);
}
extern macro LoadFeedbackVectorSlot(FeedbackVector, uintptr): MaybeObject;
extern macro StoreFeedbackVectorSlot(
FeedbackVector, uintptr, MaybeObject): void;
extern macro StoreWeakReferenceInFeedbackVector(
FeedbackVector, uintptr, HeapObject): MaybeObject;
extern macro ReportFeedbackUpdate(FeedbackVector, uintptr, constexpr string);
}

View File

@ -6,33 +6,34 @@
namespace internal_coverage {
macro GetCoverageInfo(implicit context: Context)(function: JSFunction):
CoverageInfo labels IfNoCoverageInfo {
const shared: SharedFunctionInfo = function.shared_function_info;
const debugInfo = Cast<DebugInfo>(shared.script_or_debug_info)
otherwise goto IfNoCoverageInfo;
macro GetCoverageInfo(implicit context: Context)(function: JSFunction):
CoverageInfo labels IfNoCoverageInfo {
const shared: SharedFunctionInfo = function.shared_function_info;
const debugInfo = Cast<DebugInfo>(shared.script_or_debug_info)
otherwise goto IfNoCoverageInfo;
if (!debugInfo.flags.has_coverage_info) goto IfNoCoverageInfo;
return UnsafeCast<CoverageInfo>(debugInfo.coverage_info);
}
if (!debugInfo.flags.has_coverage_info) goto IfNoCoverageInfo;
return UnsafeCast<CoverageInfo>(debugInfo.coverage_info);
}
macro IncrementBlockCount(implicit context: Context)(
coverageInfo: CoverageInfo, slot: Smi) {
assert(Convert<int32>(slot) < coverageInfo.slot_count);
++coverageInfo.slots[slot].block_count;
}
macro IncrementBlockCount(implicit context: Context)(
coverageInfo: CoverageInfo, slot: Smi) {
assert(Convert<int32>(slot) < coverageInfo.slot_count);
++coverageInfo.slots[slot].block_count;
}
builtin IncBlockCounter(implicit context: Context)(
function: JSFunction, coverageArraySlotIndex: Smi): Undefined {
// It's quite possible that a function contains IncBlockCounter bytecodes,
// but no coverage info exists. This happens e.g. by selecting the
// best-effort coverage collection mode, which triggers deletion of all
// coverage infos in order to avoid memory leaks.
builtin IncBlockCounter(
implicit context:
Context)(function: JSFunction, coverageArraySlotIndex: Smi): Undefined {
// It's quite possible that a function contains IncBlockCounter bytecodes,
// but no coverage info exists. This happens e.g. by selecting the
// best-effort coverage collection mode, which triggers deletion of all
// coverage infos in order to avoid memory leaks.
const coverageInfo: CoverageInfo =
GetCoverageInfo(function) otherwise return Undefined;
IncrementBlockCount(coverageInfo, coverageArraySlotIndex);
return Undefined;
}
const coverageInfo: CoverageInfo =
GetCoverageInfo(function) otherwise return Undefined;
IncrementBlockCount(coverageInfo, coverageArraySlotIndex);
return Undefined;
}
} // namespace internal_coverage

View File

@ -5,99 +5,98 @@
#include 'src/builtins/builtins-iterator-gen.h'
namespace iterator {
// Returned from IteratorBuiltinsAssembler::GetIterator().
@export
struct IteratorRecord {
// iteratorRecord.[[Iterator]]
object: JSReceiver;
// Returned from IteratorBuiltinsAssembler::GetIterator().
@export
struct IteratorRecord {
// iteratorRecord.[[Iterator]]
object: JSReceiver;
// iteratorRecord.[[NextMethod]]
next: JSAny;
}
extern macro IteratorBuiltinsAssembler::FastIterableToList(
implicit context: Context)(JSAny): JSArray labels Slow;
extern macro IteratorBuiltinsAssembler::GetIteratorMethod(
implicit context: Context)(JSAny): JSAny;
extern macro IteratorBuiltinsAssembler::GetIterator(
implicit context: Context)(JSAny): IteratorRecord;
extern macro IteratorBuiltinsAssembler::GetIterator(
implicit context: Context)(JSAny, JSAny): IteratorRecord;
extern macro IteratorBuiltinsAssembler::IteratorStep(
implicit context: Context)(IteratorRecord): JSReceiver
labels Done;
extern macro IteratorBuiltinsAssembler::IteratorStep(
implicit context: Context)(IteratorRecord, Map): JSReceiver
labels Done;
extern macro IteratorBuiltinsAssembler::IteratorValue(
implicit context: Context)(JSReceiver): JSAny;
extern macro IteratorBuiltinsAssembler::IteratorValue(
implicit context: Context)(JSReceiver, Map): JSAny;
extern macro IteratorBuiltinsAssembler::IterableToList(
implicit context: Context)(JSAny, JSAny): JSArray;
extern macro IteratorBuiltinsAssembler::StringListFromIterable(
implicit context: Context)(JSAny): JSArray;
extern builtin IterableToListWithSymbolLookup(implicit context:
Context)(JSAny): JSArray;
extern builtin IterableToFixedArrayWithSymbolLookupSlow(
implicit context: Context)(JSAny): FixedArray;
transitioning builtin GetIteratorWithFeedback(
context: Context, receiver: JSAny, loadSlot: TaggedIndex,
callSlot: TaggedIndex, feedback: Undefined|FeedbackVector): JSAny {
let iteratorMethod: JSAny;
typeswitch (feedback) {
case (Undefined): {
iteratorMethod = GetProperty(receiver, IteratorSymbolConstant());
}
case (feedback: FeedbackVector): {
iteratorMethod = LoadIC(
context, receiver, IteratorSymbolConstant(), loadSlot, feedback);
}
}
// TODO(v8:10047): Use TaggedIndex here once TurboFan supports it.
const callSlotSmi: Smi = TaggedIndexToSmi(callSlot);
return CallIteratorWithFeedback(
context, receiver, iteratorMethod, callSlotSmi, feedback);
}
transitioning builtin CallIteratorWithFeedback(
context: Context, receiver: JSAny, iteratorMethod: JSAny, callSlot: Smi,
feedback: Undefined|FeedbackVector): JSAny {
const callSlotUnTagged: uintptr = Unsigned(SmiUntag(callSlot));
ic::CollectCallFeedback(
iteratorMethod, context, feedback, callSlotUnTagged);
const iteratorCallable: Callable = Cast<Callable>(iteratorMethod)
otherwise ThrowCalledNonCallable(iteratorMethod);
return Call(context, iteratorCallable, receiver);
}
// https://tc39.es/ecma262/#sec-iteratorclose
@export
transitioning macro IteratorCloseOnException(implicit context: Context)(
iterator: IteratorRecord) {
try {
// 4. Let innerResult be GetMethod(iterator, "return").
const method = GetProperty(iterator.object, kReturnString);
// 5. If innerResult.[[Type]] is normal, then
// a. Let return be innerResult.[[Value]].
// b. If return is undefined, return Completion(completion).
if (method == Undefined || method == Null) return;
// c. Set innerResult to Call(return, iterator).
// If an exception occurs, the original exception remains bound
Call(context, method, iterator.object);
} catch (_e) {
// Swallow the exception.
}
// (If completion.[[Type]] is throw) return Completion(completion).
}
// iteratorRecord.[[NextMethod]]
next: JSAny;
}
extern macro IteratorBuiltinsAssembler::FastIterableToList(
implicit context: Context)(JSAny): JSArray labels Slow;
extern macro IteratorBuiltinsAssembler::GetIteratorMethod(
implicit context: Context)(JSAny): JSAny;
extern macro IteratorBuiltinsAssembler::GetIterator(implicit context: Context)(
JSAny): IteratorRecord;
extern macro IteratorBuiltinsAssembler::GetIterator(implicit context: Context)(
JSAny, JSAny): IteratorRecord;
extern macro IteratorBuiltinsAssembler::IteratorStep(implicit context: Context)(
IteratorRecord): JSReceiver
labels Done;
extern macro IteratorBuiltinsAssembler::IteratorStep(implicit context: Context)(
IteratorRecord, Map): JSReceiver
labels Done;
extern macro IteratorBuiltinsAssembler::IteratorValue(
implicit context: Context)(JSReceiver): JSAny;
extern macro IteratorBuiltinsAssembler::IteratorValue(
implicit context: Context)(JSReceiver, Map): JSAny;
extern macro IteratorBuiltinsAssembler::IterableToList(
implicit context: Context)(JSAny, JSAny): JSArray;
extern macro IteratorBuiltinsAssembler::StringListFromIterable(
implicit context: Context)(JSAny): JSArray;
extern builtin IterableToListWithSymbolLookup(implicit context: Context)(JSAny):
JSArray;
extern builtin IterableToFixedArrayWithSymbolLookupSlow(
implicit context: Context)(JSAny): FixedArray;
transitioning builtin GetIteratorWithFeedback(
context: Context, receiver: JSAny, loadSlot: TaggedIndex,
callSlot: TaggedIndex, feedback: Undefined|FeedbackVector): JSAny {
let iteratorMethod: JSAny;
typeswitch (feedback) {
case (Undefined): {
iteratorMethod = GetProperty(receiver, IteratorSymbolConstant());
}
case (feedback: FeedbackVector): {
iteratorMethod = LoadIC(
context, receiver, IteratorSymbolConstant(), loadSlot, feedback);
}
}
// TODO(v8:10047): Use TaggedIndex here once TurboFan supports it.
const callSlotSmi: Smi = TaggedIndexToSmi(callSlot);
return CallIteratorWithFeedback(
context, receiver, iteratorMethod, callSlotSmi, feedback);
}
transitioning builtin CallIteratorWithFeedback(
context: Context, receiver: JSAny, iteratorMethod: JSAny, callSlot: Smi,
feedback: Undefined|FeedbackVector): JSAny {
const callSlotUnTagged: uintptr = Unsigned(SmiUntag(callSlot));
ic::CollectCallFeedback(iteratorMethod, context, feedback, callSlotUnTagged);
const iteratorCallable: Callable = Cast<Callable>(iteratorMethod)
otherwise ThrowCalledNonCallable(iteratorMethod);
return Call(context, iteratorCallable, receiver);
}
// https://tc39.es/ecma262/#sec-iteratorclose
@export
transitioning macro IteratorCloseOnException(implicit context: Context)(
iterator: IteratorRecord) {
try {
// 4. Let innerResult be GetMethod(iterator, "return").
const method = GetProperty(iterator.object, kReturnString);
// 5. If innerResult.[[Type]] is normal, then
// a. Let return be innerResult.[[Value]].
// b. If return is undefined, return Completion(completion).
if (method == Undefined || method == Null) return;
// c. Set innerResult to Call(return, iterator).
// If an exception occurs, the original exception remains bound
Call(context, method, iterator.object);
} catch (_e) {
// Swallow the exception.
}
// (If completion.[[Type]] is throw) return Completion(completion).
}
}

View File

@ -4,457 +4,456 @@
namespace math {
extern transitioning builtin
NonNumberToNumber(implicit context: Context)(HeapObject): Number;
extern transitioning builtin
NonNumberToNumber(implicit context: Context)(HeapObject): Number;
transitioning macro ReduceToSmiOrFloat64(implicit context: Context)(x: JSAny):
never
labels SmiResult(Smi), Float64Result(float64) {
let x1: JSAny = x;
while (true) {
typeswitch (x1) {
case (s: Smi): {
goto SmiResult(s);
}
case (h: HeapNumber): {
goto Float64Result(Convert<float64>(h));
}
case (a: JSAnyNotNumber): {
x1 = NonNumberToNumber(a);
}
transitioning macro ReduceToSmiOrFloat64(implicit context: Context)(x: JSAny):
never
labels SmiResult(Smi), Float64Result(float64) {
let x1: JSAny = x;
while (true) {
typeswitch (x1) {
case (s: Smi): {
goto SmiResult(s);
}
case (h: HeapNumber): {
goto Float64Result(Convert<float64>(h));
}
case (a: JSAnyNotNumber): {
x1 = NonNumberToNumber(a);
}
}
VerifiedUnreachable();
}
VerifiedUnreachable();
}
// ES6 #sec-math.abs
extern macro IsIntPtrAbsWithOverflowSupported(): constexpr bool;
extern macro TrySmiSub(Smi, Smi): Smi labels Overflow;
extern macro TrySmiAbs(Smi): Smi labels Overflow;
extern macro Float64Abs(float64): float64;
const kSmiMaxValuePlusOne:
constexpr float64 generates '0.0 - kSmiMinValue';
// ES6 #sec-math.abs
extern macro IsIntPtrAbsWithOverflowSupported(): constexpr bool;
extern macro TrySmiSub(Smi, Smi): Smi labels Overflow;
extern macro TrySmiAbs(Smi): Smi labels Overflow;
extern macro Float64Abs(float64): float64;
const kSmiMaxValuePlusOne:
constexpr float64 generates '0.0 - kSmiMinValue';
transitioning javascript builtin
MathAbs(js-implicit context: NativeContext)(x: JSAny): Number {
transitioning javascript builtin
MathAbs(js-implicit context: NativeContext)(x: JSAny): Number {
try {
ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result;
} label SmiResult(s: Smi) {
try {
ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result;
} label SmiResult(s: Smi) {
try {
if constexpr (IsIntPtrAbsWithOverflowSupported()) {
const result: Smi = TrySmiAbs(s)
otherwise SmiOverflow;
return result;
} else {
if (0 <= s) {
return s;
} else {
const result: Smi = TrySmiSub(0, s) otherwise SmiOverflow;
return result;
}
}
} label SmiOverflow {
return NumberConstant(kSmiMaxValuePlusOne);
}
} label Float64Result(f: float64) {
return Convert<Number>(Float64Abs(f));
}
}
// ES6 #sec-math.ceil
extern macro Float64Ceil(float64): float64;
transitioning javascript builtin
MathCeil(js-implicit context: NativeContext)(x: JSAny): Number {
try {
ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result;
} label SmiResult(s: Smi) {
return s;
} label Float64Result(f: float64) {
return Convert<Number>(Float64Ceil(f));
}
}
// ES6 #sec-math.floor
extern macro Float64Floor(float64): float64;
transitioning javascript builtin
MathFloor(js-implicit context: NativeContext)(x: JSAny): Number {
try {
ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result;
} label SmiResult(s: Smi) {
return s;
} label Float64Result(f: float64) {
return Convert<Number>(Float64Floor(f));
}
}
// ES6 #sec-math.round
extern macro Float64Round(float64): float64;
transitioning javascript builtin
MathRound(js-implicit context: NativeContext)(x: JSAny): Number {
try {
ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result;
} label SmiResult(s: Smi) {
return s;
} label Float64Result(f: float64) {
return Convert<Number>(Float64Round(f));
}
}
// ES6 #sec-math.trunc
extern macro Float64Trunc(float64): float64;
transitioning javascript builtin
MathTrunc(js-implicit context: NativeContext)(x: JSAny): Number {
try {
ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result;
} label SmiResult(s: Smi) {
return s;
} label Float64Result(f: float64) {
return Convert<Number>(Float64Trunc(f));
}
}
// ES6 #sec-math.pow
extern macro Float64Pow(float64, float64): float64;
extern macro TruncateTaggedToFloat64(implicit context: Context)(JSAny):
float64;
@export
macro MathPowImpl(implicit context: Context)(base: JSAny, exponent: JSAny):
Number {
const baseValue: float64 = TruncateTaggedToFloat64(base);
const exponentValue: float64 = TruncateTaggedToFloat64(exponent);
const result: float64 = Float64Pow(baseValue, exponentValue);
return Convert<Number>(result);
}
transitioning javascript builtin
MathPow(js-implicit context: NativeContext)(base: JSAny, exponent: JSAny):
Number {
return MathPowImpl(base, exponent);
}
// ES6 #sec-math.max
extern macro Float64Max(float64, float64): float64;
transitioning javascript builtin
MathMax(js-implicit context: NativeContext)(...arguments): Number {
let result: float64 = MINUS_V8_INFINITY;
const argCount = arguments.length;
for (let i: intptr = 0; i < argCount; i++) {
const doubleValue = TruncateTaggedToFloat64(arguments[i]);
result = Float64Max(result, doubleValue);
}
return Convert<Number>(result);
}
// ES6 #sec-math.min
extern macro Float64Min(float64, float64): float64;
transitioning javascript builtin
MathMin(js-implicit context: NativeContext)(...arguments): Number {
let result: float64 = V8_INFINITY;
const argCount = arguments.length;
for (let i: intptr = 0; i < argCount; i++) {
const doubleValue = TruncateTaggedToFloat64(arguments[i]);
result = Float64Min(result, doubleValue);
}
return Convert<Number>(result);
}
// ES6 #sec-math.acos
extern macro Float64Acos(float64): float64;
transitioning javascript builtin
MathAcos(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Acos(value));
}
// ES6 #sec-math.acosh
extern macro Float64Acosh(float64): float64;
transitioning javascript builtin
MathAcosh(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Acosh(value));
}
// ES6 #sec-math.asin
extern macro Float64Asin(float64): float64;
transitioning javascript builtin
MathAsin(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Asin(value));
}
// ES6 #sec-math.asinh
extern macro Float64Asinh(float64): float64;
transitioning javascript builtin
MathAsinh(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Asinh(value));
}
// ES6 #sec-math.atan
extern macro Float64Atan(float64): float64;
transitioning javascript builtin
MathAtan(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Atan(value));
}
// ES6 #sec-math.atan2
extern macro Float64Atan2(float64, float64): float64;
transitioning javascript builtin
MathAtan2(js-implicit context: NativeContext)(y: JSAny, x: JSAny): Number {
const yValue = Convert<float64>(ToNumber_Inline(y));
const xValue = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Atan2(yValue, xValue));
}
// ES6 #sec-math.atanh
extern macro Float64Atanh(float64): float64;
transitioning javascript builtin
MathAtanh(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Atanh(value));
}
// ES6 #sec-math.cbrt
extern macro Float64Cbrt(float64): float64;
transitioning javascript builtin
MathCbrt(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Cbrt(value));
}
// ES6 #sec-math.clz32
extern macro Word32Clz(int32): int32;
transitioning javascript builtin
MathClz32(js-implicit context: NativeContext)(x: JSAny): Number {
const value: int32 = Convert<int32>(ToNumber_Inline(x));
return Convert<Number>(Word32Clz(value));
}
// ES6 #sec-math.cos
extern macro Float64Cos(float64): float64;
transitioning javascript builtin
MathCos(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Cos(value));
}
// ES6 #sec-math.cosh
extern macro Float64Cosh(float64): float64;
transitioning javascript builtin
MathCosh(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Cosh(value));
}
// ES6 #sec-math.exp
extern macro Float64Exp(float64): float64;
transitioning javascript builtin
MathExp(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Exp(value));
}
// ES6 #sec-math.expm1
extern macro Float64Expm1(float64): float64;
transitioning javascript builtin
MathExpm1(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Expm1(value));
}
// ES6 #sec-math.fround
transitioning javascript builtin
MathFround(js-implicit context: NativeContext)(x: JSAny): Number {
const x32 = Convert<float32>(ToNumber_Inline(x));
const x64 = Convert<float64>(x32);
return Convert<Number>(x64);
}
// ES6 #sec-math.imul
transitioning javascript builtin
MathImul(js-implicit context: NativeContext)(x: JSAny, y: JSAny): Number {
const x = Convert<int32>(ToNumber_Inline(x));
const y = Convert<int32>(ToNumber_Inline(y));
return Convert<Number>(x * y);
}
// ES6 #sec-math.log
extern macro Float64Log(float64): float64;
transitioning javascript builtin
MathLog(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Log(value));
}
// ES6 #sec-math.log1p
extern macro Float64Log1p(float64): float64;
transitioning javascript builtin
MathLog1p(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Log1p(value));
}
// ES6 #sec-math.log10
extern macro Float64Log10(float64): float64;
transitioning javascript builtin
MathLog10(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Log10(value));
}
// ES6 #sec-math.log2
extern macro Float64Log2(float64): float64;
transitioning javascript builtin
MathLog2(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Log2(value));
}
// ES6 #sec-math.sin
extern macro Float64Sin(float64): float64;
transitioning javascript builtin
MathSin(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Sin(value));
}
// ES6 #sec-math.sign
transitioning javascript builtin
MathSign(js-implicit context: NativeContext)(x: JSAny): Number {
const num = ToNumber_Inline(x);
const value = Convert<float64>(num);
if (value < 0) {
return -1;
} else if (value > 0) {
return 1;
} else {
return num;
}
}
// ES6 #sec-math.sinh
extern macro Float64Sinh(float64): float64;
transitioning javascript builtin
MathSinh(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Sinh(value));
}
// ES6 #sec-math.sqrt
extern macro Float64Sqrt(float64): float64;
transitioning javascript builtin
MathSqrt(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Sqrt(value));
}
// ES6 #sec-math.tan
extern macro Float64Tan(float64): float64;
transitioning javascript builtin
MathTan(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Tan(value));
}
// ES6 #sec-math.tanh
extern macro Float64Tanh(float64): float64;
transitioning javascript builtin
MathTanh(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Tanh(value));
}
// ES6 #sec-math.hypot
transitioning javascript builtin
MathHypot(js-implicit context: NativeContext, receiver: JSAny)(...arguments):
Number {
const length = arguments.length;
if (length == 0) {
return 0;
}
const absValues = AllocateZeroedFixedDoubleArray(length);
let oneArgIsNaN: bool = false;
let max: float64 = 0;
for (let i: intptr = 0; i < length; ++i) {
const value = Convert<float64>(ToNumber_Inline(arguments[i]));
if (Float64IsNaN(value)) {
oneArgIsNaN = true;
if constexpr (IsIntPtrAbsWithOverflowSupported()) {
const result: Smi = TrySmiAbs(s)
otherwise SmiOverflow;
return result;
} else {
const absValue = Float64Abs(value);
absValues.floats[i] = Convert<float64_or_hole>(absValue);
if (absValue > max) {
max = absValue;
if (0 <= s) {
return s;
} else {
const result: Smi = TrySmiSub(0, s) otherwise SmiOverflow;
return result;
}
}
} label SmiOverflow {
return NumberConstant(kSmiMaxValuePlusOne);
}
if (max == V8_INFINITY) {
return V8_INFINITY;
} else if (oneArgIsNaN) {
return kNaN;
} else if (max == 0) {
return 0;
}
assert(max > 0);
// Kahan summation to avoid rounding errors.
// Normalize the numbers to the largest one to avoid overflow.
let sum: float64 = 0;
let compensation: float64 = 0;
for (let i: intptr = 0; i < length; ++i) {
const n = absValues.floats[i].ValueUnsafeAssumeNotHole() / max;
const summand = n * n - compensation;
const preliminary = sum + summand;
compensation = (preliminary - sum) - summand;
sum = preliminary;
}
return Convert<Number>(Float64Sqrt(sum) * max);
}
// ES6 #sec-math.random
extern macro RefillMathRandom(NativeContext): Smi;
transitioning javascript builtin
MathRandom(js-implicit context: NativeContext, receiver: JSAny)(): Number {
let smiIndex: Smi =
Cast<Smi>(context[NativeContextSlot::MATH_RANDOM_INDEX_INDEX])
otherwise unreachable;
if (smiIndex == 0) {
// refill math random.
smiIndex = RefillMathRandom(context);
}
const newSmiIndex: Smi = smiIndex - 1;
context[NativeContextSlot::MATH_RANDOM_INDEX_INDEX] = newSmiIndex;
const array: FixedDoubleArray = Cast<FixedDoubleArray>(
context[NativeContextSlot::MATH_RANDOM_CACHE_INDEX])
otherwise unreachable;
const random: float64 =
array.floats[Convert<intptr>(newSmiIndex)].ValueUnsafeAssumeNotHole();
return AllocateHeapNumberWithValue(random);
} label Float64Result(f: float64) {
return Convert<Number>(Float64Abs(f));
}
}
// ES6 #sec-math.ceil
extern macro Float64Ceil(float64): float64;
transitioning javascript builtin
MathCeil(js-implicit context: NativeContext)(x: JSAny): Number {
try {
ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result;
} label SmiResult(s: Smi) {
return s;
} label Float64Result(f: float64) {
return Convert<Number>(Float64Ceil(f));
}
}
// ES6 #sec-math.floor
extern macro Float64Floor(float64): float64;
transitioning javascript builtin
MathFloor(js-implicit context: NativeContext)(x: JSAny): Number {
try {
ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result;
} label SmiResult(s: Smi) {
return s;
} label Float64Result(f: float64) {
return Convert<Number>(Float64Floor(f));
}
}
// ES6 #sec-math.round
extern macro Float64Round(float64): float64;
transitioning javascript builtin
MathRound(js-implicit context: NativeContext)(x: JSAny): Number {
try {
ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result;
} label SmiResult(s: Smi) {
return s;
} label Float64Result(f: float64) {
return Convert<Number>(Float64Round(f));
}
}
// ES6 #sec-math.trunc
extern macro Float64Trunc(float64): float64;
transitioning javascript builtin
MathTrunc(js-implicit context: NativeContext)(x: JSAny): Number {
try {
ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result;
} label SmiResult(s: Smi) {
return s;
} label Float64Result(f: float64) {
return Convert<Number>(Float64Trunc(f));
}
}
// ES6 #sec-math.pow
extern macro Float64Pow(float64, float64): float64;
extern macro TruncateTaggedToFloat64(implicit context: Context)(JSAny): float64;
@export
macro MathPowImpl(implicit context: Context)(base: JSAny, exponent: JSAny):
Number {
const baseValue: float64 = TruncateTaggedToFloat64(base);
const exponentValue: float64 = TruncateTaggedToFloat64(exponent);
const result: float64 = Float64Pow(baseValue, exponentValue);
return Convert<Number>(result);
}
transitioning javascript builtin
MathPow(js-implicit context: NativeContext)(
base: JSAny, exponent: JSAny): Number {
return MathPowImpl(base, exponent);
}
// ES6 #sec-math.max
extern macro Float64Max(float64, float64): float64;
transitioning javascript builtin
MathMax(js-implicit context: NativeContext)(...arguments): Number {
let result: float64 = MINUS_V8_INFINITY;
const argCount = arguments.length;
for (let i: intptr = 0; i < argCount; i++) {
const doubleValue = TruncateTaggedToFloat64(arguments[i]);
result = Float64Max(result, doubleValue);
}
return Convert<Number>(result);
}
// ES6 #sec-math.min
extern macro Float64Min(float64, float64): float64;
transitioning javascript builtin
MathMin(js-implicit context: NativeContext)(...arguments): Number {
let result: float64 = V8_INFINITY;
const argCount = arguments.length;
for (let i: intptr = 0; i < argCount; i++) {
const doubleValue = TruncateTaggedToFloat64(arguments[i]);
result = Float64Min(result, doubleValue);
}
return Convert<Number>(result);
}
// ES6 #sec-math.acos
extern macro Float64Acos(float64): float64;
transitioning javascript builtin
MathAcos(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Acos(value));
}
// ES6 #sec-math.acosh
extern macro Float64Acosh(float64): float64;
transitioning javascript builtin
MathAcosh(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Acosh(value));
}
// ES6 #sec-math.asin
extern macro Float64Asin(float64): float64;
transitioning javascript builtin
MathAsin(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Asin(value));
}
// ES6 #sec-math.asinh
extern macro Float64Asinh(float64): float64;
transitioning javascript builtin
MathAsinh(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Asinh(value));
}
// ES6 #sec-math.atan
extern macro Float64Atan(float64): float64;
transitioning javascript builtin
MathAtan(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Atan(value));
}
// ES6 #sec-math.atan2
extern macro Float64Atan2(float64, float64): float64;
transitioning javascript builtin
MathAtan2(js-implicit context: NativeContext)(y: JSAny, x: JSAny): Number {
const yValue = Convert<float64>(ToNumber_Inline(y));
const xValue = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Atan2(yValue, xValue));
}
// ES6 #sec-math.atanh
extern macro Float64Atanh(float64): float64;
transitioning javascript builtin
MathAtanh(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Atanh(value));
}
// ES6 #sec-math.cbrt
extern macro Float64Cbrt(float64): float64;
transitioning javascript builtin
MathCbrt(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Cbrt(value));
}
// ES6 #sec-math.clz32
extern macro Word32Clz(int32): int32;
transitioning javascript builtin
MathClz32(js-implicit context: NativeContext)(x: JSAny): Number {
const value: int32 = Convert<int32>(ToNumber_Inline(x));
return Convert<Number>(Word32Clz(value));
}
// ES6 #sec-math.cos
extern macro Float64Cos(float64): float64;
transitioning javascript builtin
MathCos(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Cos(value));
}
// ES6 #sec-math.cosh
extern macro Float64Cosh(float64): float64;
transitioning javascript builtin
MathCosh(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Cosh(value));
}
// ES6 #sec-math.exp
extern macro Float64Exp(float64): float64;
transitioning javascript builtin
MathExp(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Exp(value));
}
// ES6 #sec-math.expm1
extern macro Float64Expm1(float64): float64;
transitioning javascript builtin
MathExpm1(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Expm1(value));
}
// ES6 #sec-math.fround
transitioning javascript builtin
MathFround(js-implicit context: NativeContext)(x: JSAny): Number {
const x32 = Convert<float32>(ToNumber_Inline(x));
const x64 = Convert<float64>(x32);
return Convert<Number>(x64);
}
// ES6 #sec-math.imul
transitioning javascript builtin
MathImul(js-implicit context: NativeContext)(x: JSAny, y: JSAny): Number {
const x = Convert<int32>(ToNumber_Inline(x));
const y = Convert<int32>(ToNumber_Inline(y));
return Convert<Number>(x * y);
}
// ES6 #sec-math.log
extern macro Float64Log(float64): float64;
transitioning javascript builtin
MathLog(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Log(value));
}
// ES6 #sec-math.log1p
extern macro Float64Log1p(float64): float64;
transitioning javascript builtin
MathLog1p(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Log1p(value));
}
// ES6 #sec-math.log10
extern macro Float64Log10(float64): float64;
transitioning javascript builtin
MathLog10(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Log10(value));
}
// ES6 #sec-math.log2
extern macro Float64Log2(float64): float64;
transitioning javascript builtin
MathLog2(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Log2(value));
}
// ES6 #sec-math.sin
extern macro Float64Sin(float64): float64;
transitioning javascript builtin
MathSin(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Sin(value));
}
// ES6 #sec-math.sign
transitioning javascript builtin
MathSign(js-implicit context: NativeContext)(x: JSAny): Number {
const num = ToNumber_Inline(x);
const value = Convert<float64>(num);
if (value < 0) {
return -1;
} else if (value > 0) {
return 1;
} else {
return num;
}
}
// ES6 #sec-math.sinh
extern macro Float64Sinh(float64): float64;
transitioning javascript builtin
MathSinh(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Sinh(value));
}
// ES6 #sec-math.sqrt
extern macro Float64Sqrt(float64): float64;
transitioning javascript builtin
MathSqrt(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Sqrt(value));
}
// ES6 #sec-math.tan
extern macro Float64Tan(float64): float64;
transitioning javascript builtin
MathTan(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Tan(value));
}
// ES6 #sec-math.tanh
extern macro Float64Tanh(float64): float64;
transitioning javascript builtin
MathTanh(js-implicit context: NativeContext)(x: JSAny): Number {
const value = Convert<float64>(ToNumber_Inline(x));
return Convert<Number>(Float64Tanh(value));
}
// ES6 #sec-math.hypot
transitioning javascript builtin
MathHypot(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): Number {
const length = arguments.length;
if (length == 0) {
return 0;
}
const absValues = AllocateZeroedFixedDoubleArray(length);
let oneArgIsNaN: bool = false;
let max: float64 = 0;
for (let i: intptr = 0; i < length; ++i) {
const value = Convert<float64>(ToNumber_Inline(arguments[i]));
if (Float64IsNaN(value)) {
oneArgIsNaN = true;
} else {
const absValue = Float64Abs(value);
absValues.floats[i] = Convert<float64_or_hole>(absValue);
if (absValue > max) {
max = absValue;
}
}
}
if (max == V8_INFINITY) {
return V8_INFINITY;
} else if (oneArgIsNaN) {
return kNaN;
} else if (max == 0) {
return 0;
}
assert(max > 0);
// Kahan summation to avoid rounding errors.
// Normalize the numbers to the largest one to avoid overflow.
let sum: float64 = 0;
let compensation: float64 = 0;
for (let i: intptr = 0; i < length; ++i) {
const n = absValues.floats[i].ValueUnsafeAssumeNotHole() / max;
const summand = n * n - compensation;
const preliminary = sum + summand;
compensation = (preliminary - sum) - summand;
sum = preliminary;
}
return Convert<Number>(Float64Sqrt(sum) * max);
}
// ES6 #sec-math.random
extern macro RefillMathRandom(NativeContext): Smi;
transitioning javascript builtin
MathRandom(js-implicit context: NativeContext, receiver: JSAny)(): Number {
let smiIndex: Smi =
Cast<Smi>(context[NativeContextSlot::MATH_RANDOM_INDEX_INDEX])
otherwise unreachable;
if (smiIndex == 0) {
// refill math random.
smiIndex = RefillMathRandom(context);
}
const newSmiIndex: Smi = smiIndex - 1;
context[NativeContextSlot::MATH_RANDOM_INDEX_INDEX] = newSmiIndex;
const array: FixedDoubleArray = Cast<FixedDoubleArray>(
context[NativeContextSlot::MATH_RANDOM_CACHE_INDEX])
otherwise unreachable;
const random: float64 =
array.floats[Convert<intptr>(newSmiIndex)].ValueUnsafeAssumeNotHole();
return AllocateHeapNumberWithValue(random);
}
}

View File

@ -3,73 +3,71 @@
// LICENSE file.
namespace runtime {
extern transitioning runtime
DoubleToStringWithRadix(implicit context: Context)(Number, Number): String;
extern transitioning runtime
DoubleToStringWithRadix(implicit context: Context)(Number, Number): String;
} // namespace runtime
namespace number {
extern macro NaNStringConstant(): String;
extern macro ZeroStringConstant(): String;
extern macro InfinityStringConstant(): String;
extern macro MinusInfinityStringConstant(): String;
extern macro NaNStringConstant(): String;
extern macro ZeroStringConstant(): String;
extern macro InfinityStringConstant(): String;
extern macro MinusInfinityStringConstant(): String;
const kAsciiZero: constexpr int32 = 48; // '0' (ascii)
const kAsciiLowerCaseA: constexpr int32 = 97; // 'a' (ascii)
const kAsciiZero: constexpr int32 = 48; // '0' (ascii)
const kAsciiLowerCaseA: constexpr int32 = 97; // 'a' (ascii)
transitioning macro ThisNumberValue(implicit context: Context)(
receiver: JSAny, method: constexpr string): Number {
return UnsafeCast<Number>(
ToThisValue(receiver, PrimitiveType::kNumber, method));
}
// https://tc39.github.io/ecma262/#sec-number.prototype.tostring
transitioning javascript builtin NumberPrototypeToString(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): String {
// 1. Let x be ? thisNumberValue(this value).
const x = ThisNumberValue(receiver, 'Number.prototype.toString');
// 2. If radix is not present, let radixNumber be 10.
// 3. Else if radix is undefined, let radixNumber be 10.
// 4. Else, let radixNumber be ? ToInteger(radix).
const radix: JSAny = arguments[0];
const radixNumber: Number =
radix == Undefined ? 10 : ToInteger_Inline(radix);
// 5. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception.
if (radixNumber < 2 || radixNumber > 36) {
ThrowRangeError(MessageTemplate::kToRadixFormatRange);
}
// 6. If radixNumber = 10, return ! ToString(x).
if (radixNumber == 10) {
return NumberToString(x);
}
// 7. Return the String representation of this Number
// value using the radix specified by radixNumber.
// Fast case where the result is a one character string.
if (TaggedIsPositiveSmi(x) && x < radixNumber) {
let charCode = Convert<int32>(UnsafeCast<Smi>(x));
if (charCode < 10) {
charCode += kAsciiZero;
} else {
charCode = charCode - 10 + kAsciiLowerCaseA;
}
return StringFromSingleCharCode(charCode);
}
if (x == -0) {
return ZeroStringConstant();
} else if (NumberIsNaN(x)) {
return NaNStringConstant();
} else if (x == V8_INFINITY) {
return InfinityStringConstant();
} else if (x == MINUS_V8_INFINITY) {
return MinusInfinityStringConstant();
}
return runtime::DoubleToStringWithRadix(x, radixNumber);
}
transitioning macro ThisNumberValue(implicit context: Context)(
receiver: JSAny, method: constexpr string): Number {
return UnsafeCast<Number>(
ToThisValue(receiver, PrimitiveType::kNumber, method));
}
// https://tc39.github.io/ecma262/#sec-number.prototype.tostring
transitioning javascript builtin NumberPrototypeToString(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
// 1. Let x be ? thisNumberValue(this value).
const x = ThisNumberValue(receiver, 'Number.prototype.toString');
// 2. If radix is not present, let radixNumber be 10.
// 3. Else if radix is undefined, let radixNumber be 10.
// 4. Else, let radixNumber be ? ToInteger(radix).
const radix: JSAny = arguments[0];
const radixNumber: Number = radix == Undefined ? 10 : ToInteger_Inline(radix);
// 5. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception.
if (radixNumber < 2 || radixNumber > 36) {
ThrowRangeError(MessageTemplate::kToRadixFormatRange);
}
// 6. If radixNumber = 10, return ! ToString(x).
if (radixNumber == 10) {
return NumberToString(x);
}
// 7. Return the String representation of this Number
// value using the radix specified by radixNumber.
// Fast case where the result is a one character string.
if (TaggedIsPositiveSmi(x) && x < radixNumber) {
let charCode = Convert<int32>(UnsafeCast<Smi>(x));
if (charCode < 10) {
charCode += kAsciiZero;
} else {
charCode = charCode - 10 + kAsciiLowerCaseA;
}
return StringFromSingleCharCode(charCode);
}
if (x == -0) {
return ZeroStringConstant();
} else if (NumberIsNaN(x)) {
return NaNStringConstant();
} else if (x == V8_INFINITY) {
return InfinityStringConstant();
} else if (x == MINUS_V8_INFINITY) {
return MinusInfinityStringConstant();
}
return runtime::DoubleToStringWithRadix(x, radixNumber);
}
}

View File

@ -4,64 +4,63 @@
namespace object {
transitioning macro ObjectFromEntriesFastCase(implicit context: Context)(
iterable: JSAny): JSObject labels IfSlow {
typeswitch (iterable) {
case (array: FastJSArrayWithNoCustomIteration): {
const elements: FixedArray =
Cast<FixedArray>(array.elements) otherwise IfSlow;
const length: Smi = array.length;
const result: JSObject = NewJSObject();
for (let k: Smi = 0; k < length; ++k) {
const value: JSAny = array::LoadElementOrUndefined(elements, k);
const pair: KeyValuePair =
collections::LoadKeyValuePairNoSideEffects(value)
otherwise IfSlow;
// Bail out if ToPropertyKey will attempt to load and call
// Symbol.toPrimitive, toString, and valueOf, which could
// invalidate assumptions about the iterable.
if (Is<JSReceiver>(pair.key)) goto IfSlow;
FastCreateDataProperty(result, pair.key, pair.value);
}
return result;
}
case (JSAny): {
goto IfSlow;
}
}
}
transitioning javascript builtin
ObjectFromEntries(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): JSAny {
const iterable: JSAny = arguments[0];
try {
if (IsNullOrUndefined(iterable)) goto Throw;
return ObjectFromEntriesFastCase(iterable) otherwise IfSlow;
} label IfSlow {
transitioning macro ObjectFromEntriesFastCase(implicit context: Context)(
iterable: JSAny): JSObject labels IfSlow {
typeswitch (iterable) {
case (array: FastJSArrayWithNoCustomIteration): {
const elements: FixedArray =
Cast<FixedArray>(array.elements) otherwise IfSlow;
const length: Smi = array.length;
const result: JSObject = NewJSObject();
const fastIteratorResultMap: Map = GetIteratorResultMap();
let i: iterator::IteratorRecord = iterator::GetIterator(iterable);
try {
assert(!IsNullOrUndefined(i.object));
while (true) {
const step: JSReceiver =
iterator::IteratorStep(i, fastIteratorResultMap)
otherwise return result;
const iteratorValue: JSAny =
iterator::IteratorValue(step, fastIteratorResultMap);
const pair: KeyValuePair =
collections::LoadKeyValuePair(iteratorValue);
FastCreateDataProperty(result, pair.key, pair.value);
}
return result;
} catch (e) deferred {
iterator::IteratorCloseOnException(i);
ReThrow(context, e);
for (let k: Smi = 0; k < length; ++k) {
const value: JSAny = array::LoadElementOrUndefined(elements, k);
const pair: KeyValuePair =
collections::LoadKeyValuePairNoSideEffects(value)
otherwise IfSlow;
// Bail out if ToPropertyKey will attempt to load and call
// Symbol.toPrimitive, toString, and valueOf, which could
// invalidate assumptions about the iterable.
if (Is<JSReceiver>(pair.key)) goto IfSlow;
FastCreateDataProperty(result, pair.key, pair.value);
}
} label Throw deferred {
ThrowTypeError(MessageTemplate::kNotIterable);
return result;
}
case (JSAny): {
goto IfSlow;
}
}
}
transitioning javascript builtin
ObjectFromEntries(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
const iterable: JSAny = arguments[0];
try {
if (IsNullOrUndefined(iterable)) goto Throw;
return ObjectFromEntriesFastCase(iterable) otherwise IfSlow;
} label IfSlow {
const result: JSObject = NewJSObject();
const fastIteratorResultMap: Map = GetIteratorResultMap();
let i: iterator::IteratorRecord = iterator::GetIterator(iterable);
try {
assert(!IsNullOrUndefined(i.object));
while (true) {
const step: JSReceiver =
iterator::IteratorStep(i, fastIteratorResultMap)
otherwise return result;
const iteratorValue: JSAny =
iterator::IteratorValue(step, fastIteratorResultMap);
const pair: KeyValuePair = collections::LoadKeyValuePair(iteratorValue);
FastCreateDataProperty(result, pair.key, pair.value);
}
return result;
} catch (e) deferred {
iterator::IteratorCloseOnException(i);
ReThrow(context, e);
}
} label Throw deferred {
ThrowTypeError(MessageTemplate::kNotIterable);
}
}
} // namespace object

View File

@ -3,205 +3,199 @@
// found in the LICENSE file.
namespace runtime {
extern transitioning runtime
ObjectIsExtensible(implicit context: Context)(JSAny): JSAny;
extern transitioning runtime
ObjectIsExtensible(implicit context: Context)(JSAny): JSAny;
extern transitioning runtime
JSReceiverPreventExtensionsThrow(implicit context: Context)(JSReceiver):
JSAny;
extern transitioning runtime
JSReceiverPreventExtensionsThrow(implicit context: Context)(JSReceiver): JSAny;
extern transitioning runtime
JSReceiverPreventExtensionsDontThrow(implicit context: Context)(JSReceiver):
JSAny;
extern transitioning runtime
JSReceiverPreventExtensionsDontThrow(implicit context: Context)(JSReceiver):
JSAny;
extern transitioning runtime
JSReceiverGetPrototypeOf(implicit context: Context)(JSReceiver): JSAny;
extern transitioning runtime
JSReceiverGetPrototypeOf(implicit context: Context)(JSReceiver): JSAny;
extern transitioning runtime
JSReceiverSetPrototypeOfThrow(implicit context: Context)(JSReceiver, JSAny):
JSAny;
extern transitioning runtime
JSReceiverSetPrototypeOfThrow(implicit context: Context)(
JSReceiver, JSAny): JSAny;
extern transitioning runtime
JSReceiverSetPrototypeOfDontThrow(implicit context:
Context)(JSReceiver, JSAny): JSAny;
extern transitioning runtime
JSReceiverSetPrototypeOfDontThrow(implicit context: Context)(
JSReceiver, JSAny): JSAny;
extern transitioning runtime ObjectCreate(implicit context:
Context)(JSAny, JSAny): JSAny;
extern transitioning runtime ObjectCreate(implicit context: Context)(
JSAny, JSAny): JSAny;
} // namespace runtime
namespace object {
transitioning macro
ObjectIsExtensibleImpl(implicit context: Context)(object: JSAny): JSAny {
const objectJSReceiver = Cast<JSReceiver>(object) otherwise return False;
const objectJSProxy = Cast<JSProxy>(objectJSReceiver)
otherwise return runtime::ObjectIsExtensible(objectJSReceiver);
return proxy::ProxyIsExtensible(objectJSProxy);
}
transitioning macro
ObjectIsExtensibleImpl(implicit context: Context)(object: JSAny): JSAny {
const objectJSReceiver = Cast<JSReceiver>(object) otherwise return False;
const objectJSProxy = Cast<JSProxy>(objectJSReceiver)
otherwise return runtime::ObjectIsExtensible(objectJSReceiver);
return proxy::ProxyIsExtensible(objectJSProxy);
}
transitioning macro
ObjectPreventExtensionsThrow(implicit context: Context)(object: JSAny):
JSAny {
const objectJSReceiver = Cast<JSReceiver>(object) otherwise return object;
const objectJSProxy = Cast<JSProxy>(objectJSReceiver)
otherwise return runtime::JSReceiverPreventExtensionsThrow(
objectJSReceiver);
proxy::ProxyPreventExtensions(objectJSProxy, True);
return objectJSReceiver;
}
transitioning macro
ObjectPreventExtensionsThrow(implicit context: Context)(object: JSAny): JSAny {
const objectJSReceiver = Cast<JSReceiver>(object) otherwise return object;
const objectJSProxy = Cast<JSProxy>(objectJSReceiver)
otherwise return runtime::JSReceiverPreventExtensionsThrow(objectJSReceiver);
proxy::ProxyPreventExtensions(objectJSProxy, True);
return objectJSReceiver;
}
transitioning macro
ObjectPreventExtensionsDontThrow(implicit context: Context)(object: JSAny):
JSAny {
const objectJSReceiver = Cast<JSReceiver>(object) otherwise return False;
const objectJSProxy = Cast<JSProxy>(objectJSReceiver)
otherwise return runtime::JSReceiverPreventExtensionsDontThrow(
objectJSReceiver);
return proxy::ProxyPreventExtensions(objectJSProxy, False);
}
transitioning macro
ObjectPreventExtensionsDontThrow(implicit context: Context)(object: JSAny):
JSAny {
const objectJSReceiver = Cast<JSReceiver>(object) otherwise return False;
const objectJSProxy = Cast<JSProxy>(objectJSReceiver)
otherwise return runtime::JSReceiverPreventExtensionsDontThrow(
objectJSReceiver);
return proxy::ProxyPreventExtensions(objectJSProxy, False);
}
transitioning macro
ObjectGetPrototypeOfImpl(implicit context: Context)(object: JSAny): JSAny {
const objectJSReceiver: JSReceiver = ToObject_Inline(context, object);
return object::JSReceiverGetPrototypeOf(objectJSReceiver);
}
transitioning macro
ObjectGetPrototypeOfImpl(implicit context: Context)(object: JSAny): JSAny {
const objectJSReceiver: JSReceiver = ToObject_Inline(context, object);
return object::JSReceiverGetPrototypeOf(objectJSReceiver);
}
transitioning macro
JSReceiverGetPrototypeOf(implicit context: Context)(object: JSReceiver):
JSAny {
const objectJSProxy = Cast<JSProxy>(object)
otherwise return runtime::JSReceiverGetPrototypeOf(object);
return proxy::ProxyGetPrototypeOf(objectJSProxy);
}
transitioning macro
JSReceiverGetPrototypeOf(implicit context: Context)(object: JSReceiver): JSAny {
const objectJSProxy = Cast<JSProxy>(object)
otherwise return runtime::JSReceiverGetPrototypeOf(object);
return proxy::ProxyGetPrototypeOf(objectJSProxy);
}
transitioning macro
ObjectSetPrototypeOfThrow(implicit context: Context)(
object: JSAny, proto: JSReceiver|Null): JSAny {
const objectJSReceiver = Cast<JSReceiver>(object) otherwise return object;
const objectJSProxy = Cast<JSProxy>(objectJSReceiver)
otherwise return runtime::JSReceiverSetPrototypeOfThrow(
objectJSReceiver, proto);
proxy::ProxySetPrototypeOf(objectJSProxy, proto, True);
return objectJSReceiver;
}
transitioning macro
ObjectSetPrototypeOfThrow(implicit context: Context)(
object: JSAny, proto: JSReceiver|Null): JSAny {
const objectJSReceiver = Cast<JSReceiver>(object) otherwise return object;
const objectJSProxy = Cast<JSProxy>(objectJSReceiver)
otherwise return runtime::JSReceiverSetPrototypeOfThrow(
objectJSReceiver, proto);
proxy::ProxySetPrototypeOf(objectJSProxy, proto, True);
return objectJSReceiver;
}
transitioning macro
ObjectSetPrototypeOfDontThrow(implicit context: Context)(
object: JSAny, proto: JSReceiver|Null): JSAny {
const objectJSReceiver = Cast<JSReceiver>(object) otherwise return False;
const objectJSProxy = Cast<JSProxy>(objectJSReceiver)
otherwise return runtime::JSReceiverSetPrototypeOfDontThrow(
objectJSReceiver, proto);
return proxy::ProxySetPrototypeOf(objectJSProxy, proto, False);
}
transitioning macro
ObjectSetPrototypeOfDontThrow(implicit context: Context)(
object: JSAny, proto: JSReceiver|Null): JSAny {
const objectJSReceiver = Cast<JSReceiver>(object) otherwise return False;
const objectJSProxy = Cast<JSProxy>(objectJSReceiver)
otherwise return runtime::JSReceiverSetPrototypeOfDontThrow(
objectJSReceiver, proto);
return proxy::ProxySetPrototypeOf(objectJSProxy, proto, False);
}
transitioning builtin CreateObjectWithoutProperties(
implicit context: Context)(prototype: JSAny): JSAny {
const nativeContext = LoadNativeContext(context);
transitioning builtin CreateObjectWithoutProperties(implicit context: Context)(
prototype: JSAny): JSAny {
const nativeContext = LoadNativeContext(context);
try {
let map: Map;
let properties: NameDictionary|EmptyFixedArray;
typeswitch (prototype) {
case (Null): {
map = UnsafeCast<Map>(
nativeContext
[NativeContextSlot::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP]);
properties = AllocateNameDictionary(kNameDictionaryInitialCapacity);
}
case (prototype: JSReceiver): {
properties = kEmptyFixedArray;
const objectFunction = UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::OBJECT_FUNCTION_INDEX]);
map = UnsafeCast<Map>(objectFunction.prototype_or_initial_map);
if (prototype != map.prototype) {
const prototypeInfo =
prototype.map.PrototypeInfo() otherwise Runtime;
typeswitch (prototypeInfo.object_create_map) {
case (Undefined): {
goto Runtime;
}
case (weak_map: Weak<Map>): {
map = WeakToStrong(weak_map) otherwise Runtime;
}
try {
let map: Map;
let properties: NameDictionary|EmptyFixedArray;
typeswitch (prototype) {
case (Null): {
map = UnsafeCast<Map>(
nativeContext
[NativeContextSlot::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP]);
properties = AllocateNameDictionary(kNameDictionaryInitialCapacity);
}
case (prototype: JSReceiver): {
properties = kEmptyFixedArray;
const objectFunction = UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::OBJECT_FUNCTION_INDEX]);
map = UnsafeCast<Map>(objectFunction.prototype_or_initial_map);
if (prototype != map.prototype) {
const prototypeInfo = prototype.map.PrototypeInfo() otherwise Runtime;
typeswitch (prototypeInfo.object_create_map) {
case (Undefined): {
goto Runtime;
}
case (weak_map: Weak<Map>): {
map = WeakToStrong(weak_map) otherwise Runtime;
}
}
}
case (JSAny): {
goto Runtime;
}
}
return AllocateJSObjectFromMap(map, properties);
} label Runtime deferred {
return runtime::ObjectCreate(prototype, Undefined);
}
}
// ES6 section 19.1.2.11 Object.isExtensible ( O )
transitioning javascript builtin
ObjectIsExtensible(js-implicit context: NativeContext)(object: JSAny): JSAny {
return object::ObjectIsExtensibleImpl(object);
}
// ES6 section 19.1.2.18 Object.preventExtensions ( O )
transitioning javascript builtin
ObjectPreventExtensions(js-implicit context: NativeContext)(object: JSAny):
JSAny {
return object::ObjectPreventExtensionsThrow(object);
}
// ES6 section 19.1.2.9 Object.getPrototypeOf ( O )
transitioning javascript builtin
ObjectGetPrototypeOf(js-implicit context: NativeContext)(object: JSAny):
JSAny {
return object::ObjectGetPrototypeOfImpl(object);
}
// ES6 section 19.1.2.21 Object.setPrototypeOf ( O, proto )
transitioning javascript builtin ObjectSetPrototypeOf(
js-implicit context: NativeContext)(object: JSAny, proto: JSAny): JSAny {
// 1. Set O to ? RequireObjectCoercible(O).
RequireObjectCoercible(object, 'Object.setPrototypeOf');
// 2. If Type(proto) is neither Object nor Null, throw a TypeError
// exception.
// 3. If Type(O) is not Object, return O.
// 4. Let status be ? O.[[SetPrototypeOf]](proto).
// 5. If status is false, throw a TypeError exception.
// 6. Return O.
typeswitch (proto) {
case (proto: JSReceiver|Null): {
return object::ObjectSetPrototypeOfThrow(object, proto);
}
case (JSAny): {
ThrowTypeError(MessageTemplate::kProtoObjectOrNull, proto);
goto Runtime;
}
}
return AllocateJSObjectFromMap(map, properties);
} label Runtime deferred {
return runtime::ObjectCreate(prototype, Undefined);
}
}
// ES #sec-object.prototype.tostring
transitioning javascript builtin ObjectPrototypeToString(
js-implicit context: Context, receiver: JSAny)(): String {
return ObjectToString(context, receiver);
}
// ES6 section 19.1.2.11 Object.isExtensible ( O )
transitioning javascript builtin
ObjectIsExtensible(js-implicit context: NativeContext)(object: JSAny): JSAny {
return object::ObjectIsExtensibleImpl(object);
}
// ES #sec-object.prototype.valueof
transitioning javascript builtin ObjectPrototypeValueOf(
js-implicit context: Context, receiver: JSAny)(): JSReceiver {
// 1. Return ? ToObject(this value).
return ToObject_Inline(context, receiver);
}
// ES6 section 19.1.2.18 Object.preventExtensions ( O )
transitioning javascript builtin
ObjectPreventExtensions(js-implicit context: NativeContext)(object: JSAny):
JSAny {
return object::ObjectPreventExtensionsThrow(object);
}
// ES #sec-object.prototype.tolocalestring
transitioning javascript builtin ObjectPrototypeToLocaleString(
js-implicit context: Context, receiver: JSAny)(): JSAny {
// 1. Let O be the this value.
// 2. Return ? Invoke(O, "toString").
if (receiver == Null || receiver == Undefined) deferred {
ThrowTypeError(
MessageTemplate::kCalledOnNullOrUndefined,
'Object.prototype.toLocaleString');
}
const method = GetProperty(receiver, 'toString');
return Call(context, method, receiver);
// ES6 section 19.1.2.9 Object.getPrototypeOf ( O )
transitioning javascript builtin
ObjectGetPrototypeOf(js-implicit context: NativeContext)(object: JSAny): JSAny {
return object::ObjectGetPrototypeOfImpl(object);
}
// ES6 section 19.1.2.21 Object.setPrototypeOf ( O, proto )
transitioning javascript builtin ObjectSetPrototypeOf(
js-implicit context: NativeContext)(object: JSAny, proto: JSAny): JSAny {
// 1. Set O to ? RequireObjectCoercible(O).
RequireObjectCoercible(object, 'Object.setPrototypeOf');
// 2. If Type(proto) is neither Object nor Null, throw a TypeError
// exception.
// 3. If Type(O) is not Object, return O.
// 4. Let status be ? O.[[SetPrototypeOf]](proto).
// 5. If status is false, throw a TypeError exception.
// 6. Return O.
typeswitch (proto) {
case (proto: JSReceiver|Null): {
return object::ObjectSetPrototypeOfThrow(object, proto);
}
case (JSAny): {
ThrowTypeError(MessageTemplate::kProtoObjectOrNull, proto);
}
}
}
// ES #sec-object.prototype.tostring
transitioning javascript builtin ObjectPrototypeToString(
js-implicit context: Context, receiver: JSAny)(): String {
return ObjectToString(context, receiver);
}
// ES #sec-object.prototype.valueof
transitioning javascript builtin ObjectPrototypeValueOf(
js-implicit context: Context, receiver: JSAny)(): JSReceiver {
// 1. Return ? ToObject(this value).
return ToObject_Inline(context, receiver);
}
// ES #sec-object.prototype.tolocalestring
transitioning javascript builtin ObjectPrototypeToLocaleString(
js-implicit context: Context, receiver: JSAny)(): JSAny {
// 1. Let O be the this value.
// 2. Return ? Invoke(O, "toString").
if (receiver == Null || receiver == Undefined) deferred {
ThrowTypeError(
MessageTemplate::kCalledOnNullOrUndefined,
'Object.prototype.toLocaleString');
}
const method = GetProperty(receiver, 'toString');
return Call(context, method, receiver);
}
} // namespace object

File diff suppressed because it is too large Load Diff

View File

@ -8,182 +8,180 @@
namespace promise {
struct PromiseAllWrapResultAsFulfilledFunctor {
macro Call(_nativeContext: NativeContext, value: JSAny): JSAny {
return value;
}
}
struct PromiseAllSettledWrapResultAsFulfilledFunctor {
transitioning
macro Call(implicit context: Context)(
nativeContext: NativeContext, value: JSAny): JSAny {
// TODO(gsathya): Optimize the creation using a cached map to
// prevent transitions here.
// 9. Let obj be ! ObjectCreate(%ObjectPrototype%).
const objectFunction = UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::OBJECT_FUNCTION_INDEX]);
const objectFunctionMap =
UnsafeCast<Map>(objectFunction.prototype_or_initial_map);
const obj = AllocateJSObjectFromMap(objectFunctionMap);
// 10. Perform ! CreateDataProperty(obj, "status", "fulfilled").
FastCreateDataProperty(
obj, StringConstant('status'), StringConstant('fulfilled'));
// 11. Perform ! CreateDataProperty(obj, "value", x).
FastCreateDataProperty(obj, StringConstant('value'), value);
return obj;
}
}
struct PromiseAllSettledWrapResultAsRejectedFunctor {
transitioning
macro Call(implicit context: Context)(
nativeContext: NativeContext, value: JSAny): JSAny {
// TODO(gsathya): Optimize the creation using a cached map to
// prevent transitions here.
// 9. Let obj be ! ObjectCreate(%ObjectPrototype%).
const objectFunction = UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::OBJECT_FUNCTION_INDEX]);
const objectFunctionMap =
UnsafeCast<Map>(objectFunction.prototype_or_initial_map);
const obj = AllocateJSObjectFromMap(objectFunctionMap);
// 10. Perform ! CreateDataProperty(obj, "status", "rejected").
FastCreateDataProperty(
obj, StringConstant('status'), StringConstant('rejected'));
// 11. Perform ! CreateDataProperty(obj, "reason", x).
FastCreateDataProperty(obj, StringConstant('reason'), value);
return obj;
}
}
extern macro LoadJSReceiverIdentityHash(Object): intptr labels IfNoHash;
extern enum PromiseAllResolveElementContextSlots extends int31
constexpr 'PromiseBuiltins::PromiseAllResolveElementContextSlots' {
kPromiseAllResolveElementRemainingSlot,
kPromiseAllResolveElementCapabilitySlot,
kPromiseAllResolveElementValuesArraySlot,
kPromiseAllResolveElementLength
}
extern operator '[]=' macro StoreContextElement(
Context, constexpr PromiseAllResolveElementContextSlots, Object): void;
extern operator '[]' macro LoadContextElement(
Context, constexpr PromiseAllResolveElementContextSlots): Object;
const kPropertyArrayNoHashSentinel: constexpr int31
generates 'PropertyArray::kNoHashSentinel';
const kPropertyArrayHashFieldMax: constexpr int31
generates 'PropertyArray::HashField::kMax';
transitioning macro PromiseAllResolveElementClosure<F: type>(
implicit context:
Context)(value: JSAny, function: JSFunction, wrapResultFunctor: F):
JSAny {
// We use the {function}s context as the marker to remember whether this
// resolve element closure was already called. It points to the resolve
// element context (which is a FunctionContext) until it was called the
// first time, in which case we make it point to the native context here
// to mark this resolve element closure as done.
if (IsNativeContext(context)) deferred {
return Undefined;
}
assert(
context.length ==
PromiseAllResolveElementContextSlots::kPromiseAllResolveElementLength);
const nativeContext = LoadNativeContext(context);
function.context = nativeContext;
// Update the value depending on whether Promise.all or
// Promise.allSettled is called.
const updatedValue = wrapResultFunctor.Call(nativeContext, value);
// Determine the index from the {function}.
assert(kPropertyArrayNoHashSentinel == 0);
const identityHash =
LoadJSReceiverIdentityHash(function) otherwise unreachable;
assert(identityHash > 0);
const index = identityHash - 1;
// Check if we need to grow the [[ValuesArray]] to store {value} at {index}.
const valuesArray = UnsafeCast<JSArray>(
context[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementValuesArraySlot]);
const elements = UnsafeCast<FixedArray>(valuesArray.elements);
const valuesLength = Convert<intptr>(valuesArray.length);
if (index < valuesLength) {
// The {index} is in bounds of the {values_array},
// just store the {value} and continue.
elements.objects[index] = updatedValue;
} else {
// Check if we need to grow the backing store.
const newLength = index + 1;
const elementsLength = elements.length_intptr;
if (index < elementsLength) {
// The {index} is within bounds of the {elements} backing store, so
// just store the {value} and update the "length" of the {values_array}.
valuesArray.length = Convert<Smi>(newLength);
elements.objects[index] = updatedValue;
} else
deferred {
// We need to grow the backing store to fit the {index} as well.
const newElementsLength = IntPtrMin(
CalculateNewElementsCapacity(newLength),
kPropertyArrayHashFieldMax + 1);
assert(index < newElementsLength);
assert(elementsLength < newElementsLength);
const newElements =
ExtractFixedArray(elements, 0, elementsLength, newElementsLength);
newElements.objects[index] = updatedValue;
// Update backing store and "length" on {values_array}.
valuesArray.elements = newElements;
valuesArray.length = Convert<Smi>(newLength);
}
}
let remainingElementsCount =
UnsafeCast<Smi>(context[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot]);
remainingElementsCount = remainingElementsCount - 1;
context[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot] =
remainingElementsCount;
if (remainingElementsCount == 0) {
const capability = UnsafeCast<PromiseCapability>(
context[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementCapabilitySlot]);
const resolve = UnsafeCast<JSAny>(capability.resolve);
Call(context, resolve, Undefined, valuesArray);
}
return Undefined;
}
transitioning javascript builtin
PromiseAllResolveElementClosure(
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
return PromiseAllResolveElementClosure(
value, target, PromiseAllWrapResultAsFulfilledFunctor{});
}
transitioning javascript builtin
PromiseAllSettledResolveElementClosure(
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
return PromiseAllResolveElementClosure(
value, target, PromiseAllSettledWrapResultAsFulfilledFunctor{});
}
transitioning javascript builtin
PromiseAllSettledRejectElementClosure(
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
return PromiseAllResolveElementClosure(
value, target, PromiseAllSettledWrapResultAsRejectedFunctor{});
struct PromiseAllWrapResultAsFulfilledFunctor {
macro Call(_nativeContext: NativeContext, value: JSAny): JSAny {
return value;
}
}
struct PromiseAllSettledWrapResultAsFulfilledFunctor {
transitioning
macro Call(implicit context: Context)(
nativeContext: NativeContext, value: JSAny): JSAny {
// TODO(gsathya): Optimize the creation using a cached map to
// prevent transitions here.
// 9. Let obj be ! ObjectCreate(%ObjectPrototype%).
const objectFunction = UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::OBJECT_FUNCTION_INDEX]);
const objectFunctionMap =
UnsafeCast<Map>(objectFunction.prototype_or_initial_map);
const obj = AllocateJSObjectFromMap(objectFunctionMap);
// 10. Perform ! CreateDataProperty(obj, "status", "fulfilled").
FastCreateDataProperty(
obj, StringConstant('status'), StringConstant('fulfilled'));
// 11. Perform ! CreateDataProperty(obj, "value", x).
FastCreateDataProperty(obj, StringConstant('value'), value);
return obj;
}
}
struct PromiseAllSettledWrapResultAsRejectedFunctor {
transitioning
macro Call(implicit context: Context)(
nativeContext: NativeContext, value: JSAny): JSAny {
// TODO(gsathya): Optimize the creation using a cached map to
// prevent transitions here.
// 9. Let obj be ! ObjectCreate(%ObjectPrototype%).
const objectFunction = UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::OBJECT_FUNCTION_INDEX]);
const objectFunctionMap =
UnsafeCast<Map>(objectFunction.prototype_or_initial_map);
const obj = AllocateJSObjectFromMap(objectFunctionMap);
// 10. Perform ! CreateDataProperty(obj, "status", "rejected").
FastCreateDataProperty(
obj, StringConstant('status'), StringConstant('rejected'));
// 11. Perform ! CreateDataProperty(obj, "reason", x).
FastCreateDataProperty(obj, StringConstant('reason'), value);
return obj;
}
}
extern macro LoadJSReceiverIdentityHash(Object): intptr labels IfNoHash;
extern enum PromiseAllResolveElementContextSlots extends int31
constexpr 'PromiseBuiltins::PromiseAllResolveElementContextSlots' {
kPromiseAllResolveElementRemainingSlot,
kPromiseAllResolveElementCapabilitySlot,
kPromiseAllResolveElementValuesArraySlot,
kPromiseAllResolveElementLength
}
extern operator '[]=' macro StoreContextElement(
Context, constexpr PromiseAllResolveElementContextSlots, Object): void;
extern operator '[]' macro LoadContextElement(
Context, constexpr PromiseAllResolveElementContextSlots): Object;
const kPropertyArrayNoHashSentinel: constexpr int31
generates 'PropertyArray::kNoHashSentinel';
const kPropertyArrayHashFieldMax: constexpr int31
generates 'PropertyArray::HashField::kMax';
transitioning macro PromiseAllResolveElementClosure<F: type>(
implicit context: Context)(
value: JSAny, function: JSFunction, wrapResultFunctor: F): JSAny {
// We use the {function}s context as the marker to remember whether this
// resolve element closure was already called. It points to the resolve
// element context (which is a FunctionContext) until it was called the
// first time, in which case we make it point to the native context here
// to mark this resolve element closure as done.
if (IsNativeContext(context)) deferred {
return Undefined;
}
assert(
context.length ==
PromiseAllResolveElementContextSlots::kPromiseAllResolveElementLength);
const nativeContext = LoadNativeContext(context);
function.context = nativeContext;
// Update the value depending on whether Promise.all or
// Promise.allSettled is called.
const updatedValue = wrapResultFunctor.Call(nativeContext, value);
// Determine the index from the {function}.
assert(kPropertyArrayNoHashSentinel == 0);
const identityHash =
LoadJSReceiverIdentityHash(function) otherwise unreachable;
assert(identityHash > 0);
const index = identityHash - 1;
// Check if we need to grow the [[ValuesArray]] to store {value} at {index}.
const valuesArray = UnsafeCast<JSArray>(
context[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementValuesArraySlot]);
const elements = UnsafeCast<FixedArray>(valuesArray.elements);
const valuesLength = Convert<intptr>(valuesArray.length);
if (index < valuesLength) {
// The {index} is in bounds of the {values_array},
// just store the {value} and continue.
elements.objects[index] = updatedValue;
} else {
// Check if we need to grow the backing store.
const newLength = index + 1;
const elementsLength = elements.length_intptr;
if (index < elementsLength) {
// The {index} is within bounds of the {elements} backing store, so
// just store the {value} and update the "length" of the {values_array}.
valuesArray.length = Convert<Smi>(newLength);
elements.objects[index] = updatedValue;
} else
deferred {
// We need to grow the backing store to fit the {index} as well.
const newElementsLength = IntPtrMin(
CalculateNewElementsCapacity(newLength),
kPropertyArrayHashFieldMax + 1);
assert(index < newElementsLength);
assert(elementsLength < newElementsLength);
const newElements =
ExtractFixedArray(elements, 0, elementsLength, newElementsLength);
newElements.objects[index] = updatedValue;
// Update backing store and "length" on {values_array}.
valuesArray.elements = newElements;
valuesArray.length = Convert<Smi>(newLength);
}
}
let remainingElementsCount =
UnsafeCast<Smi>(context[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot]);
remainingElementsCount = remainingElementsCount - 1;
context[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot] = remainingElementsCount;
if (remainingElementsCount == 0) {
const capability = UnsafeCast<PromiseCapability>(
context[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementCapabilitySlot]);
const resolve = UnsafeCast<JSAny>(capability.resolve);
Call(context, resolve, Undefined, valuesArray);
}
return Undefined;
}
transitioning javascript builtin
PromiseAllResolveElementClosure(
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
return PromiseAllResolveElementClosure(
value, target, PromiseAllWrapResultAsFulfilledFunctor{});
}
transitioning javascript builtin
PromiseAllSettledResolveElementClosure(
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
return PromiseAllResolveElementClosure(
value, target, PromiseAllSettledWrapResultAsFulfilledFunctor{});
}
transitioning javascript builtin
PromiseAllSettledRejectElementClosure(
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
return PromiseAllResolveElementClosure(
value, target, PromiseAllSettledWrapResultAsRejectedFunctor{});
}
}

View File

@ -6,376 +6,372 @@
#include 'src/builtins/builtins-promise-gen.h'
namespace promise {
const kPromiseBuiltinsPromiseContextLength: constexpr int31
generates 'PromiseBuiltins::kPromiseContextLength';
const kPromiseBuiltinsPromiseContextLength: constexpr int31
generates 'PromiseBuiltins::kPromiseContextLength';
// Creates the context used by all Promise.all resolve element closures,
// together with the values array. Since all closures for a single Promise.all
// call use the same context, we need to store the indices for the individual
// closures somewhere else (we put them into the identity hash field of the
// closures), and we also need to have a separate marker for when the closure
// was called already (we slap the native context onto the closure in that
// case to mark it's done).
macro CreatePromiseAllResolveElementContext(implicit context: Context)(
capability: PromiseCapability, nativeContext: NativeContext): Context {
// TODO(bmeurer): Manually fold this into a single allocation.
const arrayMap = UnsafeCast<Map>(
nativeContext[NativeContextSlot::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX]);
const valuesArray = AllocateJSArray(
ElementsKind::PACKED_ELEMENTS, arrayMap, IntPtrConstant(0),
SmiConstant(0));
const resolveContext = AllocateSyntheticFunctionContext(
nativeContext,
PromiseAllResolveElementContextSlots::kPromiseAllResolveElementLength);
resolveContext[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot] = SmiConstant(1);
resolveContext[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementCapabilitySlot] = capability;
resolveContext[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementValuesArraySlot] = valuesArray;
return resolveContext;
// Creates the context used by all Promise.all resolve element closures,
// together with the values array. Since all closures for a single Promise.all
// call use the same context, we need to store the indices for the individual
// closures somewhere else (we put them into the identity hash field of the
// closures), and we also need to have a separate marker for when the closure
// was called already (we slap the native context onto the closure in that
// case to mark it's done).
macro CreatePromiseAllResolveElementContext(implicit context: Context)(
capability: PromiseCapability, nativeContext: NativeContext): Context {
// TODO(bmeurer): Manually fold this into a single allocation.
const arrayMap = UnsafeCast<Map>(
nativeContext[NativeContextSlot::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX]);
const valuesArray = AllocateJSArray(
ElementsKind::PACKED_ELEMENTS, arrayMap, IntPtrConstant(0),
SmiConstant(0));
const resolveContext = AllocateSyntheticFunctionContext(
nativeContext,
PromiseAllResolveElementContextSlots::kPromiseAllResolveElementLength);
resolveContext[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot] = SmiConstant(1);
resolveContext[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementCapabilitySlot] = capability;
resolveContext[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementValuesArraySlot] = valuesArray;
return resolveContext;
}
macro CreatePromiseAllResolveElementFunction(implicit context: Context)(
resolveElementContext: Context, index: Smi, nativeContext: NativeContext,
resolveFunction: SharedFunctionInfo): JSFunction {
assert(index > 0);
assert(index < kPropertyArrayHashFieldMax);
const map = UnsafeCast<Map>(
nativeContext
[NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]);
const resolve = AllocateFunctionWithMapAndContext(
map, resolveFunction, resolveElementContext);
assert(kPropertyArrayNoHashSentinel == 0);
resolve.properties_or_hash = index;
return resolve;
}
@export
macro CreatePromiseResolvingFunctionsContext(implicit context: Context)(
promise: JSPromise, debugEvent: Object, nativeContext: NativeContext):
Context {
const resolveContext = AllocateSyntheticFunctionContext(
nativeContext, kPromiseBuiltinsPromiseContextLength);
resolveContext[kPromiseBuiltinsPromiseSlot] = promise;
resolveContext[kPromiseBuiltinsAlreadyResolvedSlot] = False;
resolveContext[kPromiseBuiltinsDebugEventSlot] = debugEvent;
return resolveContext;
}
macro IsPromiseThenLookupChainIntact(implicit context: Context)(
nativeContext: NativeContext, receiverMap: Map): bool {
if (IsForceSlowPath()) return false;
if (!IsJSPromiseMap(receiverMap)) return false;
if (receiverMap.prototype !=
nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX])
return false;
return !IsPromiseThenProtectorCellInvalid();
}
struct PromiseAllResolveElementFunctor {
macro Call(implicit context: Context)(
resolveElementContext: Context, nativeContext: NativeContext, index: Smi,
_capability: PromiseCapability): Callable {
return CreatePromiseAllResolveElementFunction(
resolveElementContext, index, nativeContext,
PromiseAllResolveElementSharedFunConstant());
}
}
macro CreatePromiseAllResolveElementFunction(implicit context: Context)(
resolveElementContext: Context, index: Smi, nativeContext: NativeContext,
resolveFunction: SharedFunctionInfo): JSFunction {
assert(index > 0);
assert(index < kPropertyArrayHashFieldMax);
const map = UnsafeCast<Map>(
nativeContext
[NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]);
const resolve = AllocateFunctionWithMapAndContext(
map, resolveFunction, resolveElementContext);
assert(kPropertyArrayNoHashSentinel == 0);
resolve.properties_or_hash = index;
return resolve;
struct PromiseAllRejectElementFunctor {
macro Call(implicit context: Context)(
_resolveElementContext: Context, _nativeContext: NativeContext,
_index: Smi, capability: PromiseCapability): Callable {
return UnsafeCast<Callable>(capability.reject);
}
}
@export
macro CreatePromiseResolvingFunctionsContext(implicit context: Context)(
promise: JSPromise, debugEvent: Object, nativeContext: NativeContext):
Context {
const resolveContext = AllocateSyntheticFunctionContext(
nativeContext, kPromiseBuiltinsPromiseContextLength);
resolveContext[kPromiseBuiltinsPromiseSlot] = promise;
resolveContext[kPromiseBuiltinsAlreadyResolvedSlot] = False;
resolveContext[kPromiseBuiltinsDebugEventSlot] = debugEvent;
return resolveContext;
struct PromiseAllSettledResolveElementFunctor {
macro Call(implicit context: Context)(
resolveElementContext: Context, nativeContext: NativeContext, index: Smi,
_capability: PromiseCapability): Callable {
return CreatePromiseAllResolveElementFunction(
resolveElementContext, index, nativeContext,
PromiseAllSettledResolveElementSharedFunConstant());
}
}
macro IsPromiseThenLookupChainIntact(implicit context: Context)(
nativeContext: NativeContext, receiverMap: Map): bool {
if (IsForceSlowPath()) return false;
if (!IsJSPromiseMap(receiverMap)) return false;
if (receiverMap.prototype !=
nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX])
return false;
return !IsPromiseThenProtectorCellInvalid();
struct PromiseAllSettledRejectElementFunctor {
macro Call(implicit context: Context)(
resolveElementContext: Context, nativeContext: NativeContext, index: Smi,
_capability: PromiseCapability): Callable {
return CreatePromiseAllResolveElementFunction(
resolveElementContext, index, nativeContext,
PromiseAllSettledRejectElementSharedFunConstant());
}
}
struct PromiseAllResolveElementFunctor {
macro Call(implicit context: Context)(
resolveElementContext: Context, nativeContext: NativeContext,
index: Smi, _capability: PromiseCapability): Callable {
return CreatePromiseAllResolveElementFunction(
resolveElementContext, index, nativeContext,
PromiseAllResolveElementSharedFunConstant());
transitioning macro PerformPromiseAll<F1: type, F2: type>(
implicit context: Context)(
constructor: JSReceiver, capability: PromiseCapability,
iter: iterator::IteratorRecord, createResolveElementFunctor: F1,
createRejectElementFunctor: F2): JSAny labels
Reject(Object) {
const nativeContext = LoadNativeContext(context);
const promise = capability.promise;
const resolve = capability.resolve;
const reject = capability.reject;
// For catch prediction, don't treat the .then calls as handling it;
// instead, recurse outwards.
if (IsDebugActive()) deferred {
SetPropertyStrict(context, reject, kPromiseForwardingHandlerSymbol, True);
}
}
struct PromiseAllRejectElementFunctor {
macro Call(implicit context: Context)(
_resolveElementContext: Context, _nativeContext: NativeContext,
_index: Smi, capability: PromiseCapability): Callable {
return UnsafeCast<Callable>(capability.reject);
}
}
const resolveElementContext =
CreatePromiseAllResolveElementContext(capability, nativeContext);
struct PromiseAllSettledResolveElementFunctor {
macro Call(implicit context: Context)(
resolveElementContext: Context, nativeContext: NativeContext,
index: Smi, _capability: PromiseCapability): Callable {
return CreatePromiseAllResolveElementFunction(
resolveElementContext, index, nativeContext,
PromiseAllSettledResolveElementSharedFunConstant());
}
}
let index: Smi = 1;
struct PromiseAllSettledRejectElementFunctor {
macro Call(implicit context: Context)(
resolveElementContext: Context, nativeContext: NativeContext,
index: Smi, _capability: PromiseCapability): Callable {
return CreatePromiseAllResolveElementFunction(
resolveElementContext, index, nativeContext,
PromiseAllSettledRejectElementSharedFunConstant());
}
}
transitioning macro PerformPromiseAll<F1: type, F2: type>(implicit context:
Context)(
constructor: JSReceiver, capability: PromiseCapability,
iter: iterator::IteratorRecord, createResolveElementFunctor: F1,
createRejectElementFunctor: F2): JSAny labels Reject(Object) {
const nativeContext = LoadNativeContext(context);
const promise = capability.promise;
const resolve = capability.resolve;
const reject = capability.reject;
// For catch prediction, don't treat the .then calls as handling it;
// instead, recurse outwards.
if (IsDebugActive()) deferred {
SetPropertyStrict(
context, reject, kPromiseForwardingHandlerSymbol, True);
}
const resolveElementContext =
CreatePromiseAllResolveElementContext(capability, nativeContext);
let index: Smi = 1;
// We can skip the "resolve" lookup on {constructor} if it's the
// Promise constructor and the Promise.resolve protector is intact,
// as that guards the lookup path for the "resolve" property on the
// Promise constructor.
let promiseResolveFunction: JSAny = Undefined;
// We can skip the "resolve" lookup on {constructor} if it's the
// Promise constructor and the Promise.resolve protector is intact,
// as that guards the lookup path for the "resolve" property on the
// Promise constructor.
let promiseResolveFunction: JSAny = Undefined;
try {
try {
try {
if (!IsPromiseResolveLookupChainIntact(nativeContext, constructor)) {
let promiseResolve: JSAny;
if (!IsPromiseResolveLookupChainIntact(nativeContext, constructor)) {
let promiseResolve: JSAny;
// 5. Let _promiseResolve_ be ? Get(_constructor_, `"resolve"`).
promiseResolve = GetProperty(constructor, kResolveString);
// 5. Let _promiseResolve_ be ? Get(_constructor_, `"resolve"`).
promiseResolve = GetProperty(constructor, kResolveString);
// 6. If IsCallable(_promiseResolve_) is *false*, throw a *TypeError*
// exception.
promiseResolveFunction =
Cast<Callable>(promiseResolve) otherwise ThrowTypeError(
MessageTemplate::kCalledNonCallable, 'resolve');
}
const fastIteratorResultMap = UnsafeCast<Map>(
nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]);
while (true) {
let nextValue: JSAny;
try {
// Let next be IteratorStep(iteratorRecord.[[Iterator]]).
// If next is an abrupt completion, set iteratorRecord.[[Done]] to
// true. ReturnIfAbrupt(next).
const next: JSReceiver = iterator::IteratorStep(
iter, fastIteratorResultMap) otherwise goto Done;
// Let nextValue be IteratorValue(next).
// If nextValue is an abrupt completion, set iteratorRecord.[[Done]]
// to true.
// ReturnIfAbrupt(nextValue).
nextValue = iterator::IteratorValue(next, fastIteratorResultMap);
} catch (e) {
goto Reject(e);
}
// Check if we reached the limit.
if (index == kPropertyArrayHashFieldMax) {
// If there are too many elements (currently more than 2**21-1),
// raise a RangeError here (which is caught below and turned into
// a rejection of the resulting promise). We could gracefully handle
// this case as well and support more than this number of elements
// by going to a separate function and pass the larger indices via a
// separate context, but it doesn't seem likely that we need this,
// and it's unclear how the rest of the system deals with 2**21 live
// Promises anyway.
ThrowRangeError(
MessageTemplate::kTooManyElementsInPromiseCombinator, 'all');
}
// Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] + 1.
const remainingElementsCount =
UnsafeCast<Smi>(resolveElementContext
[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot]);
resolveElementContext[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot] =
remainingElementsCount + 1;
// Let resolveElement be CreateBuiltinFunction(steps,
// « [[AlreadyCalled]],
// [[Index]],
// [[Values]],
// [[Capability]],
// [[RemainingElements]]
// »).
// Set resolveElement.[[AlreadyCalled]] to a Record { [[Value]]: false
// }. Set resolveElement.[[Index]] to index. Set
// resolveElement.[[Values]] to values. Set
// resolveElement.[[Capability]] to resultCapability. Set
// resolveElement.[[RemainingElements]] to remainingElementsCount.
const resolveElementFun = createResolveElementFunctor.Call(
resolveElementContext, nativeContext, index, capability);
const rejectElementFun = createRejectElementFunctor.Call(
resolveElementContext, nativeContext, index, capability);
// We can skip the "resolve" lookup on the {constructor} as well as
// the "then" lookup on the result of the "resolve" call, and
// immediately chain continuation onto the {next_value} if:
//
// (a) The {constructor} is the intrinsic %Promise% function, and
// looking up "resolve" on {constructor} yields the initial
// Promise.resolve() builtin, and
// (b) the promise @@species protector cell is valid, meaning that
// no one messed with the Symbol.species property on any
// intrinsic promise or on the Promise.prototype, and
// (c) the {next_value} is a JSPromise whose [[Prototype]] field
// contains the intrinsic %PromisePrototype%, and
// (d) we're not running with async_hooks or DevTools enabled.
//
// In that case we also don't need to allocate a chained promise for
// the PromiseReaction (aka we can pass undefined to
// PerformPromiseThen), since this is only necessary for DevTools and
// PromiseHooks.
if (promiseResolveFunction != Undefined ||
IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() ||
IsPromiseSpeciesProtectorCellInvalid() || Is<Smi>(nextValue) ||
!IsPromiseThenLookupChainIntact(
nativeContext, UnsafeCast<HeapObject>(nextValue).map)) {
// Let nextPromise be ? Call(constructor, _promiseResolve_, «
// nextValue »).
const nextPromise = CallResolve(
UnsafeCast<Constructor>(constructor), promiseResolveFunction,
nextValue);
// Perform ? Invoke(nextPromise, "then", « resolveElement,
// resultCapability.[[Reject]] »).
const then = GetProperty(nextPromise, kThenString);
const thenResult = Call(
nativeContext, then, nextPromise, resolveElementFun,
rejectElementFun);
// For catch prediction, mark that rejections here are
// semantically handled by the combined Promise.
if (IsDebugActive() && Is<JSPromise>(thenResult)) deferred {
SetPropertyStrict(
context, thenResult, kPromiseHandledBySymbol, promise);
}
} else {
PerformPromiseThenImpl(
UnsafeCast<JSPromise>(nextValue), resolveElementFun,
rejectElementFun, Undefined);
}
// Set index to index + 1.
index += 1;
}
} catch (e) deferred {
iterator::IteratorCloseOnException(iter);
goto Reject(e);
// 6. If IsCallable(_promiseResolve_) is *false*, throw a *TypeError*
// exception.
promiseResolveFunction =
Cast<Callable>(promiseResolve) otherwise ThrowTypeError(
MessageTemplate::kCalledNonCallable, 'resolve');
}
} label Done {}
// Set iteratorRecord.[[Done]] to true.
// Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] - 1.
let remainingElementsCount = UnsafeCast<Smi>(
const fastIteratorResultMap = UnsafeCast<Map>(
nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]);
while (true) {
let nextValue: JSAny;
try {
// Let next be IteratorStep(iteratorRecord.[[Iterator]]).
// If next is an abrupt completion, set iteratorRecord.[[Done]] to
// true. ReturnIfAbrupt(next).
const next: JSReceiver = iterator::IteratorStep(
iter, fastIteratorResultMap) otherwise goto Done;
// Let nextValue be IteratorValue(next).
// If nextValue is an abrupt completion, set iteratorRecord.[[Done]]
// to true.
// ReturnIfAbrupt(nextValue).
nextValue = iterator::IteratorValue(next, fastIteratorResultMap);
} catch (e) {
goto Reject(e);
}
// Check if we reached the limit.
if (index == kPropertyArrayHashFieldMax) {
// If there are too many elements (currently more than 2**21-1),
// raise a RangeError here (which is caught below and turned into
// a rejection of the resulting promise). We could gracefully handle
// this case as well and support more than this number of elements
// by going to a separate function and pass the larger indices via a
// separate context, but it doesn't seem likely that we need this,
// and it's unclear how the rest of the system deals with 2**21 live
// Promises anyway.
ThrowRangeError(
MessageTemplate::kTooManyElementsInPromiseCombinator, 'all');
}
// Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] + 1.
const remainingElementsCount = UnsafeCast<Smi>(
resolveElementContext[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot]);
resolveElementContext[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot]);
remainingElementsCount -= 1;
resolveElementContext[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot] =
remainingElementsCount;
if (remainingElementsCount > 0) {
// Pre-allocate the backing store for the {values_array} to the desired
// capacity here. We may already have elements here in case of some
// fancy Thenable that calls the resolve callback immediately, so we need
// to handle that correctly here.
const valuesArray = UnsafeCast<JSArray>(
kPromiseAllResolveElementRemainingSlot] =
remainingElementsCount + 1;
// Let resolveElement be CreateBuiltinFunction(steps,
// « [[AlreadyCalled]],
// [[Index]],
// [[Values]],
// [[Capability]],
// [[RemainingElements]]
// »).
// Set resolveElement.[[AlreadyCalled]] to a Record { [[Value]]: false
// }. Set resolveElement.[[Index]] to index. Set
// resolveElement.[[Values]] to values. Set
// resolveElement.[[Capability]] to resultCapability. Set
// resolveElement.[[RemainingElements]] to remainingElementsCount.
const resolveElementFun = createResolveElementFunctor.Call(
resolveElementContext, nativeContext, index, capability);
const rejectElementFun = createRejectElementFunctor.Call(
resolveElementContext, nativeContext, index, capability);
// We can skip the "resolve" lookup on the {constructor} as well as
// the "then" lookup on the result of the "resolve" call, and
// immediately chain continuation onto the {next_value} if:
//
// (a) The {constructor} is the intrinsic %Promise% function, and
// looking up "resolve" on {constructor} yields the initial
// Promise.resolve() builtin, and
// (b) the promise @@species protector cell is valid, meaning that
// no one messed with the Symbol.species property on any
// intrinsic promise or on the Promise.prototype, and
// (c) the {next_value} is a JSPromise whose [[Prototype]] field
// contains the intrinsic %PromisePrototype%, and
// (d) we're not running with async_hooks or DevTools enabled.
//
// In that case we also don't need to allocate a chained promise for
// the PromiseReaction (aka we can pass undefined to
// PerformPromiseThen), since this is only necessary for DevTools and
// PromiseHooks.
if (promiseResolveFunction != Undefined ||
IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() ||
IsPromiseSpeciesProtectorCellInvalid() || Is<Smi>(nextValue) ||
!IsPromiseThenLookupChainIntact(
nativeContext, UnsafeCast<HeapObject>(nextValue).map)) {
// Let nextPromise be ? Call(constructor, _promiseResolve_, «
// nextValue »).
const nextPromise = CallResolve(
UnsafeCast<Constructor>(constructor), promiseResolveFunction,
nextValue);
// Perform ? Invoke(nextPromise, "then", « resolveElement,
// resultCapability.[[Reject]] »).
const then = GetProperty(nextPromise, kThenString);
const thenResult = Call(
nativeContext, then, nextPromise, resolveElementFun,
rejectElementFun);
// For catch prediction, mark that rejections here are
// semantically handled by the combined Promise.
if (IsDebugActive() && Is<JSPromise>(thenResult)) deferred {
SetPropertyStrict(
context, thenResult, kPromiseHandledBySymbol, promise);
}
} else {
PerformPromiseThenImpl(
UnsafeCast<JSPromise>(nextValue), resolveElementFun,
rejectElementFun, Undefined);
}
// Set index to index + 1.
index += 1;
}
} catch (e) deferred {
iterator::IteratorCloseOnException(iter);
goto Reject(e);
}
} label Done {}
// Set iteratorRecord.[[Done]] to true.
// Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] - 1.
let remainingElementsCount = UnsafeCast<Smi>(
resolveElementContext[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot]);
remainingElementsCount -= 1;
resolveElementContext[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot] =
remainingElementsCount;
if (remainingElementsCount > 0) {
// Pre-allocate the backing store for the {values_array} to the desired
// capacity here. We may already have elements here in case of some
// fancy Thenable that calls the resolve callback immediately, so we need
// to handle that correctly here.
const valuesArray = UnsafeCast<JSArray>(
resolveElementContext[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementValuesArraySlot]);
const oldElements = UnsafeCast<FixedArray>(valuesArray.elements);
const oldCapacity = oldElements.length_intptr;
const newCapacity = SmiUntag(index);
if (oldCapacity < newCapacity) {
valuesArray.elements =
ExtractFixedArray(oldElements, 0, oldCapacity, newCapacity);
}
} else
deferred {
// If remainingElementsCount.[[Value]] is 0, then
// Let valuesArray be CreateArrayFromList(values).
// Perform ? Call(resultCapability.[[Resolve]], undefined,
// « valuesArray »).
assert(remainingElementsCount == 0);
const valuesArray = UnsafeCast<JSAny>(
resolveElementContext[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementValuesArraySlot]);
const oldElements = UnsafeCast<FixedArray>(valuesArray.elements);
const oldCapacity = oldElements.length_intptr;
const newCapacity = SmiUntag(index);
if (oldCapacity < newCapacity) {
valuesArray.elements =
ExtractFixedArray(oldElements, 0, oldCapacity, newCapacity);
}
} else
deferred {
// If remainingElementsCount.[[Value]] is 0, then
// Let valuesArray be CreateArrayFromList(values).
// Perform ? Call(resultCapability.[[Resolve]], undefined,
// « valuesArray »).
assert(remainingElementsCount == 0);
const valuesArray = UnsafeCast<JSAny>(
resolveElementContext
[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementValuesArraySlot]);
Call(nativeContext, UnsafeCast<JSAny>(resolve), Undefined, valuesArray);
}
// Return resultCapability.[[Promise]].
return promise;
}
transitioning macro GeneratePromiseAll<F1: type, F2: type>(implicit context:
Context)(
receiver: JSAny, iterable: JSAny, createResolveElementFunctor: F1,
createRejectElementFunctor: F2): JSAny {
// Let C be the this value.
// If Type(C) is not Object, throw a TypeError exception.
const receiver = Cast<JSReceiver>(receiver)
otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Promise.all');
// Let promiseCapability be ? NewPromiseCapability(C).
// Don't fire debugEvent so that forwarding the rejection through all does
// not trigger redundant ExceptionEvents
const capability = NewPromiseCapability(receiver, False);
try {
// Let iterator be GetIterator(iterable).
// IfAbruptRejectPromise(iterator, promiseCapability).
let i = iterator::GetIterator(iterable);
// Let result be PerformPromiseAll(iteratorRecord, C,
// promiseCapability). If result is an abrupt completion, then
// If iteratorRecord.[[Done]] is false, let result be
// IteratorClose(iterator, result).
// IfAbruptRejectPromise(result, promiseCapability).
return PerformPromiseAll(
receiver, capability, i, createResolveElementFunctor,
createRejectElementFunctor) otherwise Reject;
} catch (e) deferred {
goto Reject(e);
} label Reject(e: Object) deferred {
// Exception must be bound to a JS value.
const e = UnsafeCast<JSAny>(e);
const reject = UnsafeCast<JSAny>(capability.reject);
Call(context, reject, Undefined, e);
return capability.promise;
Call(nativeContext, UnsafeCast<JSAny>(resolve), Undefined, valuesArray);
}
}
// ES#sec-promise.all
transitioning javascript builtin PromiseAll(
js-implicit context: Context, receiver: JSAny)(iterable: JSAny): JSAny {
return GeneratePromiseAll(
receiver, iterable, PromiseAllResolveElementFunctor{},
PromiseAllRejectElementFunctor{});
}
// ES#sec-promise.allsettled
// Promise.allSettled ( iterable )
transitioning javascript builtin PromiseAllSettled(
js-implicit context: Context, receiver: JSAny)(iterable: JSAny): JSAny {
return GeneratePromiseAll(
receiver, iterable, PromiseAllSettledResolveElementFunctor{},
PromiseAllSettledRejectElementFunctor{});
}
extern macro PromiseAllResolveElementSharedFunConstant(): SharedFunctionInfo;
extern macro PromiseAllSettledRejectElementSharedFunConstant():
SharedFunctionInfo;
extern macro PromiseAllSettledResolveElementSharedFunConstant():
SharedFunctionInfo;
// Return resultCapability.[[Promise]].
return promise;
}
transitioning macro GeneratePromiseAll<F1: type, F2: type>(
implicit context: Context)(
receiver: JSAny, iterable: JSAny, createResolveElementFunctor: F1,
createRejectElementFunctor: F2): JSAny {
// Let C be the this value.
// If Type(C) is not Object, throw a TypeError exception.
const receiver = Cast<JSReceiver>(receiver)
otherwise ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'Promise.all');
// Let promiseCapability be ? NewPromiseCapability(C).
// Don't fire debugEvent so that forwarding the rejection through all does
// not trigger redundant ExceptionEvents
const capability = NewPromiseCapability(receiver, False);
try {
// Let iterator be GetIterator(iterable).
// IfAbruptRejectPromise(iterator, promiseCapability).
let i = iterator::GetIterator(iterable);
// Let result be PerformPromiseAll(iteratorRecord, C,
// promiseCapability). If result is an abrupt completion, then
// If iteratorRecord.[[Done]] is false, let result be
// IteratorClose(iterator, result).
// IfAbruptRejectPromise(result, promiseCapability).
return PerformPromiseAll(
receiver, capability, i, createResolveElementFunctor,
createRejectElementFunctor) otherwise Reject;
} catch (e) deferred {
goto Reject(e);
} label Reject(e: Object) deferred {
// Exception must be bound to a JS value.
const e = UnsafeCast<JSAny>(e);
const reject = UnsafeCast<JSAny>(capability.reject);
Call(context, reject, Undefined, e);
return capability.promise;
}
}
// ES#sec-promise.all
transitioning javascript builtin PromiseAll(
js-implicit context: Context, receiver: JSAny)(iterable: JSAny): JSAny {
return GeneratePromiseAll(
receiver, iterable, PromiseAllResolveElementFunctor{},
PromiseAllRejectElementFunctor{});
}
// ES#sec-promise.allsettled
// Promise.allSettled ( iterable )
transitioning javascript builtin PromiseAllSettled(
js-implicit context: Context, receiver: JSAny)(iterable: JSAny): JSAny {
return GeneratePromiseAll(
receiver, iterable, PromiseAllSettledResolveElementFunctor{},
PromiseAllSettledRejectElementFunctor{});
}
extern macro PromiseAllResolveElementSharedFunConstant(): SharedFunctionInfo;
extern macro PromiseAllSettledRejectElementSharedFunConstant():
SharedFunctionInfo;
extern macro PromiseAllSettledResolveElementSharedFunConstant():
SharedFunctionInfo;
}

View File

@ -5,371 +5,368 @@
#include 'src/builtins/builtins-promise-gen.h'
namespace promise {
extern enum PromiseAnyRejectElementContextSlots extends int31
constexpr 'PromiseBuiltins::PromiseAnyRejectElementContextSlots' {
kPromiseAnyRejectElementRemainingSlot,
kPromiseAnyRejectElementCapabilitySlot,
kPromiseAnyRejectElementErrorsArraySlot,
kPromiseAnyRejectElementLength
}
extern enum PromiseAnyRejectElementContextSlots extends int31
constexpr 'PromiseBuiltins::PromiseAnyRejectElementContextSlots' {
kPromiseAnyRejectElementRemainingSlot,
kPromiseAnyRejectElementCapabilitySlot,
kPromiseAnyRejectElementErrorsArraySlot,
kPromiseAnyRejectElementLength
}
extern operator '[]=' macro StoreContextElement(
Context, constexpr PromiseAnyRejectElementContextSlots, Object): void;
extern operator '[]' macro LoadContextElement(
Context, constexpr PromiseAnyRejectElementContextSlots): Object;
extern operator '[]=' macro StoreContextElement(
Context, constexpr PromiseAnyRejectElementContextSlots, Object): void;
extern operator '[]' macro LoadContextElement(
Context, constexpr PromiseAnyRejectElementContextSlots): Object;
// Creates the context used by all Promise.any reject element closures,
// together with the errors array. Since all closures for a single Promise.any
// call use the same context, we need to store the indices for the individual
// closures somewhere else (we put them into the identity hash field of the
// closures), and we also need to have a separate marker for when the closure
// was called already (we slap the native context onto the closure in that
// case to mark it's done). See Promise.all which uses the same approach.
transitioning macro CreatePromiseAnyRejectElementContext(implicit context:
Context)(
capability: PromiseCapability, nativeContext: NativeContext): Context {
const rejectContext = AllocateSyntheticFunctionContext(
nativeContext,
PromiseAnyRejectElementContextSlots::kPromiseAnyRejectElementLength);
rejectContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementRemainingSlot] = SmiConstant(1);
rejectContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementCapabilitySlot] = capability;
// Will be set later.
rejectContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementErrorsArraySlot] = Undefined;
return rejectContext;
}
// Creates the context used by all Promise.any reject element closures,
// together with the errors array. Since all closures for a single Promise.any
// call use the same context, we need to store the indices for the individual
// closures somewhere else (we put them into the identity hash field of the
// closures), and we also need to have a separate marker for when the closure
// was called already (we slap the native context onto the closure in that
// case to mark it's done). See Promise.all which uses the same approach.
transitioning macro CreatePromiseAnyRejectElementContext(
implicit context: Context)(
capability: PromiseCapability, nativeContext: NativeContext): Context {
const rejectContext = AllocateSyntheticFunctionContext(
nativeContext,
PromiseAnyRejectElementContextSlots::kPromiseAnyRejectElementLength);
rejectContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementRemainingSlot] = SmiConstant(1);
rejectContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementCapabilitySlot] = capability;
// Will be set later.
rejectContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementErrorsArraySlot] = Undefined;
return rejectContext;
}
macro CreatePromiseAnyRejectElementFunction(implicit context: Context)(
rejectElementContext: Context, index: Smi,
nativeContext: NativeContext): JSFunction {
assert(index > 0);
assert(index < kPropertyArrayHashFieldMax);
const map = UnsafeCast<Map>(
nativeContext
[NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]);
const rejectInfo = PromiseAnyRejectElementSharedFunConstant();
const reject = AllocateFunctionWithMapAndContext(
map, rejectInfo, rejectElementContext);
assert(kPropertyArrayNoHashSentinel == 0);
reject.properties_or_hash = index;
return reject;
}
macro CreatePromiseAnyRejectElementFunction(implicit context: Context)(
rejectElementContext: Context, index: Smi,
nativeContext: NativeContext): JSFunction {
assert(index > 0);
assert(index < kPropertyArrayHashFieldMax);
const map = UnsafeCast<Map>(
nativeContext
[NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]);
const rejectInfo = PromiseAnyRejectElementSharedFunConstant();
const reject =
AllocateFunctionWithMapAndContext(map, rejectInfo, rejectElementContext);
assert(kPropertyArrayNoHashSentinel == 0);
reject.properties_or_hash = index;
return reject;
}
// https://tc39.es/proposal-promise-any/#sec-promise.any-reject-element-functions
transitioning javascript builtin
PromiseAnyRejectElementClosure(
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
// 1. Let F be the active function object.
// https://tc39.es/proposal-promise-any/#sec-promise.any-reject-element-functions
transitioning javascript builtin
PromiseAnyRejectElementClosure(
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
// 1. Let F be the active function object.
// 2. Let alreadyCalled be F.[[AlreadyCalled]].
// 2. Let alreadyCalled be F.[[AlreadyCalled]].
// 3. If alreadyCalled.[[Value]] is true, return undefined.
// 3. If alreadyCalled.[[Value]] is true, return undefined.
// We use the function's context as the marker to remember whether this
// reject element closure was already called. It points to the reject
// element context (which is a FunctionContext) until it was called the
// first time, in which case we make it point to the native context here
// to mark this reject element closure as done.
if (IsNativeContext(context)) deferred {
return Undefined;
}
assert(
context.length ==
PromiseAnyRejectElementContextSlots::kPromiseAnyRejectElementLength);
// 4. Set alreadyCalled.[[Value]] to true.
const nativeContext = LoadNativeContext(context);
target.context = nativeContext;
// 5. Let index be F.[[Index]].
assert(kPropertyArrayNoHashSentinel == 0);
const identityHash =
LoadJSReceiverIdentityHash(target) otherwise unreachable;
assert(identityHash > 0);
const index = identityHash - 1;
// 6. Let errors be F.[[Errors]].
if (context[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementErrorsArraySlot] == Undefined) {
// We're going to reject the Promise with a more fundamental error (e.g.,
// something went wrong with iterating the Promises). We don't need to
// construct the "errors" array.
// We use the function's context as the marker to remember whether this
// reject element closure was already called. It points to the reject
// element context (which is a FunctionContext) until it was called the
// first time, in which case we make it point to the native context here
// to mark this reject element closure as done.
if (IsNativeContext(context)) deferred {
return Undefined;
}
const errorsArray = UnsafeCast<FixedArray>(
context[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementErrorsArraySlot]);
assert(
context.length ==
PromiseAnyRejectElementContextSlots::kPromiseAnyRejectElementLength);
// 7. Let promiseCapability be F.[[Capability]].
// 4. Set alreadyCalled.[[Value]] to true.
const nativeContext = LoadNativeContext(context);
target.context = nativeContext;
// 8. Let remainingElementsCount be F.[[RemainingElements]].
let remainingElementsCount =
UnsafeCast<Smi>(context[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementRemainingSlot]);
// 9. Set errors[index] to x.
errorsArray.objects[index] = value;
// 5. Let index be F.[[Index]].
assert(kPropertyArrayNoHashSentinel == 0);
const identityHash = LoadJSReceiverIdentityHash(target) otherwise unreachable;
assert(identityHash > 0);
const index = identityHash - 1;
// 10. Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] - 1.
remainingElementsCount = remainingElementsCount - 1;
context[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementRemainingSlot] = remainingElementsCount;
// 11. If remainingElementsCount.[[Value]] is 0, then
if (remainingElementsCount == 0) {
// a. Let error be a newly created AggregateError object.
// b. Set error.[[AggregateErrors]] to errors.
const error = ConstructAggregateError(errorsArray);
// c. Return ? Call(promiseCapability.[[Reject]], undefined, « error »).
const capability = UnsafeCast<PromiseCapability>(
context[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementCapabilitySlot]);
Call(context, UnsafeCast<Callable>(capability.reject), Undefined, error);
}
// 12. Return undefined.
// 6. Let errors be F.[[Errors]].
if (context[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementErrorsArraySlot] == Undefined) {
// We're going to reject the Promise with a more fundamental error (e.g.,
// something went wrong with iterating the Promises). We don't need to
// construct the "errors" array.
return Undefined;
}
transitioning macro PerformPromiseAny(implicit context: Context)(
iteratorRecord: iterator::IteratorRecord, constructor: Constructor,
resultCapability: PromiseCapability): JSAny labels
Reject(Object) {
// 1. Assert: ! IsConstructor(constructor) is true.
// 2. Assert: resultCapability is a PromiseCapability Record.
const errorsArray = UnsafeCast<FixedArray>(
context[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementErrorsArraySlot]);
const nativeContext = LoadNativeContext(context);
// 7. Let promiseCapability be F.[[Capability]].
// 3. Let errors be a new empty List.
let growableErrorsArray = growable_fixed_array::NewGrowableFixedArray();
// 8. Let remainingElementsCount be F.[[RemainingElements]].
let remainingElementsCount =
UnsafeCast<Smi>(context[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementRemainingSlot]);
// 9. Set errors[index] to x.
errorsArray.objects[index] = value;
// 4. Let remainingElementsCount be a new Record { [[Value]]: 1 }.
const rejectElementContext =
CreatePromiseAnyRejectElementContext(resultCapability, nativeContext);
// 10. Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] - 1.
remainingElementsCount = remainingElementsCount - 1;
context[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementRemainingSlot] = remainingElementsCount;
// 5. Let index be 0.
// (We subtract 1 in the PromiseAnyRejectElementClosure).
let index: Smi = 1;
// 11. If remainingElementsCount.[[Value]] is 0, then
if (remainingElementsCount == 0) {
// a. Let error be a newly created AggregateError object.
try {
// We can skip the "resolve" lookup on {constructor} if it's the
// Promise constructor and the Promise.resolve protector is intact,
// as that guards the lookup path for the "resolve" property on the
// Promise constructor.
let promiseResolveFunction: JSAny = Undefined;
if (!IsPromiseResolveLookupChainIntact(nativeContext, constructor))
deferred {
// 6. Let promiseResolve be ? Get(constructor, `"resolve"`).
const promiseResolve = GetProperty(constructor, kResolveString);
// 7. If IsCallable(promiseResolve) is false, throw a
// TypeError exception.
promiseResolveFunction = Cast<Callable>(promiseResolve)
otherwise ThrowTypeError(
MessageTemplate::kCalledNonCallable, 'resolve');
}
const fastIteratorResultMap = UnsafeCast<Map>(
nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]);
// 8. Repeat,
while (true) {
let nextValue: JSAny;
try {
// a. Let next be IteratorStep(iteratorRecord).
// b. If next is an abrupt completion, set
// iteratorRecord.[[Done]] to true.
// c. ReturnIfAbrupt(next).
// d. if next is false, then [continues below in "Done"]
const next: JSReceiver = iterator::IteratorStep(
iteratorRecord, fastIteratorResultMap) otherwise goto Done;
// e. Let nextValue be IteratorValue(next).
// f. If nextValue is an abrupt completion, set
// iteratorRecord.[[Done]] to true.
// g. ReturnIfAbrupt(nextValue).
nextValue = iterator::IteratorValue(next, fastIteratorResultMap);
} catch (e) {
goto Reject(e);
}
// We store the indices as identity hash on the reject element
// closures. Thus, we need this limit.
if (index == kPropertyArrayHashFieldMax) {
// If there are too many elements (currently more than
// 2**21-1), raise a RangeError here (which is caught later and
// turned into a rejection of the resulting promise). We could
// gracefully handle this case as well and support more than
// this number of elements by going to a separate function and
// pass the larger indices via a separate context, but it
// doesn't seem likely that we need this, and it's unclear how
// the rest of the system deals with 2**21 live Promises
// anyway.
ThrowRangeError(
MessageTemplate::kTooManyElementsInPromiseCombinator, 'any');
}
// h. Append undefined to errors.
growableErrorsArray.Push(Undefined);
let nextPromise: JSAny;
// i. Let nextPromise be ? Call(constructor, promiseResolve,
// «nextValue »).
nextPromise =
CallResolve(constructor, promiseResolveFunction, nextValue);
// j. Let steps be the algorithm steps defined in Promise.any
// Reject Element Functions.
// k. Let rejectElement be ! CreateBuiltinFunction(steps, «
// [[AlreadyCalled]], [[Index]],
// [[Errors]], [[Capability]], [[RemainingElements]] »).
// l. Set rejectElement.[[AlreadyCalled]] to a new Record {
// [[Value]]: false }.
// m. Set rejectElement.[[Index]] to index.
// n. Set rejectElement.[[Errors]] to errors.
// o. Set rejectElement.[[Capability]] to resultCapability.
// p. Set rejectElement.[[RemainingElements]] to
// remainingElementsCount.
const rejectElement = CreatePromiseAnyRejectElementFunction(
rejectElementContext, index, nativeContext);
// q. Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] + 1.
const remainingElementsCount = UnsafeCast<Smi>(
rejectElementContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementRemainingSlot]);
rejectElementContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementRemainingSlot] =
remainingElementsCount + 1;
// r. Perform ? Invoke(nextPromise, "then", «
// resultCapability.[[Resolve]], rejectElement »).
let thenResult: JSAny;
const then = GetProperty(nextPromise, kThenString);
thenResult = Call(
context, then, nextPromise,
UnsafeCast<JSAny>(resultCapability.resolve), rejectElement);
// s. Increase index by 1.
index += 1;
// For catch prediction, mark that rejections here are
// semantically handled by the combined Promise.
if (IsDebugActive() && Is<JSPromise>(thenResult)) deferred {
SetPropertyStrict(
context, thenResult, kPromiseHandledBySymbol,
resultCapability.promise);
SetPropertyStrict(
context, rejectElement, kPromiseForwardingHandlerSymbol, True);
}
}
} catch (e) deferred {
iterator::IteratorCloseOnException(iteratorRecord);
goto Reject(e);
} label Done {}
// (8.d)
// i. Set iteratorRecord.[[Done]] to true.
// ii. Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] - 1.
let remainingElementsCount = UnsafeCast<Smi>(
rejectElementContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementRemainingSlot]);
remainingElementsCount -= 1;
rejectElementContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementRemainingSlot] =
remainingElementsCount;
const errorsArray = growableErrorsArray.ToFixedArray();
rejectElementContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementErrorsArraySlot] =
errorsArray;
// iii. If remainingElementsCount.[[Value]] is 0, then
if (remainingElementsCount == 0) deferred {
// 1. Let error be a newly created AggregateError object.
// 2. Set error.[[AggregateErrors]] to errors.
const error = ConstructAggregateError(errorsArray);
// 3. Return ThrowCompletion(error).
goto Reject(error);
}
// iv. Return resultCapability.[[Promise]].
return resultCapability.promise;
// b. Set error.[[AggregateErrors]] to errors.
const error = ConstructAggregateError(errorsArray);
// c. Return ? Call(promiseCapability.[[Reject]], undefined, « error »).
const capability = UnsafeCast<PromiseCapability>(
context[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementCapabilitySlot]);
Call(context, UnsafeCast<Callable>(capability.reject), Undefined, error);
}
// https://tc39.es/proposal-promise-any/#sec-promise.any
transitioning javascript builtin
PromiseAny(js-implicit context: Context, receiver: JSAny)(iterable: JSAny):
JSAny {
// 1. Let C be the this value.
const receiver = Cast<JSReceiver>(receiver)
otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Promise.any');
// 12. Return undefined.
return Undefined;
}
// 2. Let promiseCapability be ? NewPromiseCapability(C).
const capability = NewPromiseCapability(receiver, False);
transitioning macro PerformPromiseAny(implicit context: Context)(
iteratorRecord: iterator::IteratorRecord, constructor: Constructor,
resultCapability: PromiseCapability): JSAny labels
Reject(Object) {
// 1. Assert: ! IsConstructor(constructor) is true.
// 2. Assert: resultCapability is a PromiseCapability Record.
// NewPromiseCapability guarantees that receiver is Constructor
assert(Is<Constructor>(receiver));
const constructor = UnsafeCast<Constructor>(receiver);
const nativeContext = LoadNativeContext(context);
try {
let iteratorRecord: iterator::IteratorRecord;
// 3. Let errors be a new empty List.
let growableErrorsArray = growable_fixed_array::NewGrowableFixedArray();
// 4. Let remainingElementsCount be a new Record { [[Value]]: 1 }.
const rejectElementContext =
CreatePromiseAnyRejectElementContext(resultCapability, nativeContext);
// 5. Let index be 0.
// (We subtract 1 in the PromiseAnyRejectElementClosure).
let index: Smi = 1;
try {
// We can skip the "resolve" lookup on {constructor} if it's the
// Promise constructor and the Promise.resolve protector is intact,
// as that guards the lookup path for the "resolve" property on the
// Promise constructor.
let promiseResolveFunction: JSAny = Undefined;
if (!IsPromiseResolveLookupChainIntact(nativeContext, constructor))
deferred {
// 6. Let promiseResolve be ? Get(constructor, `"resolve"`).
const promiseResolve = GetProperty(constructor, kResolveString);
// 7. If IsCallable(promiseResolve) is false, throw a
// TypeError exception.
promiseResolveFunction = Cast<Callable>(promiseResolve)
otherwise ThrowTypeError(
MessageTemplate::kCalledNonCallable, 'resolve');
}
const fastIteratorResultMap = UnsafeCast<Map>(
nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]);
// 8. Repeat,
while (true) {
let nextValue: JSAny;
try {
// 3. Let iteratorRecord be GetIterator(iterable).
// a. Let next be IteratorStep(iteratorRecord).
// 4. IfAbruptRejectPromise(iteratorRecord, promiseCapability).
// (catch below)
iteratorRecord = iterator::GetIterator(iterable);
// b. If next is an abrupt completion, set
// iteratorRecord.[[Done]] to true.
// 5. Let result be PerformPromiseAny(iteratorRecord, C,
// promiseCapability).
// c. ReturnIfAbrupt(next).
// 6. If result is an abrupt completion, then
// d. if next is false, then [continues below in "Done"]
const next: JSReceiver = iterator::IteratorStep(
iteratorRecord, fastIteratorResultMap) otherwise goto Done;
// e. Let nextValue be IteratorValue(next).
// a. If iteratorRecord.[[Done]] is false, set result to
// IteratorClose(iteratorRecord, result).
// f. If nextValue is an abrupt completion, set
// iteratorRecord.[[Done]] to true.
// b. IfAbruptRejectPromise(result, promiseCapability).
// [Iterator closing handled by PerformPromiseAny]
// 7. Return Completion(result).
return PerformPromiseAny(iteratorRecord, constructor, capability)
otherwise Reject;
} catch (e) deferred {
// g. ReturnIfAbrupt(nextValue).
nextValue = iterator::IteratorValue(next, fastIteratorResultMap);
} catch (e) {
goto Reject(e);
}
} label Reject(e: Object) deferred {
// Exception must be bound to a JS value.
assert(e != TheHole);
Call(
context, UnsafeCast<Callable>(capability.reject), Undefined,
UnsafeCast<JSAny>(e));
return capability.promise;
// We store the indices as identity hash on the reject element
// closures. Thus, we need this limit.
if (index == kPropertyArrayHashFieldMax) {
// If there are too many elements (currently more than
// 2**21-1), raise a RangeError here (which is caught later and
// turned into a rejection of the resulting promise). We could
// gracefully handle this case as well and support more than
// this number of elements by going to a separate function and
// pass the larger indices via a separate context, but it
// doesn't seem likely that we need this, and it's unclear how
// the rest of the system deals with 2**21 live Promises
// anyway.
ThrowRangeError(
MessageTemplate::kTooManyElementsInPromiseCombinator, 'any');
}
// h. Append undefined to errors.
growableErrorsArray.Push(Undefined);
let nextPromise: JSAny;
// i. Let nextPromise be ? Call(constructor, promiseResolve,
// «nextValue »).
nextPromise = CallResolve(constructor, promiseResolveFunction, nextValue);
// j. Let steps be the algorithm steps defined in Promise.any
// Reject Element Functions.
// k. Let rejectElement be ! CreateBuiltinFunction(steps, «
// [[AlreadyCalled]], [[Index]],
// [[Errors]], [[Capability]], [[RemainingElements]] »).
// l. Set rejectElement.[[AlreadyCalled]] to a new Record {
// [[Value]]: false }.
// m. Set rejectElement.[[Index]] to index.
// n. Set rejectElement.[[Errors]] to errors.
// o. Set rejectElement.[[Capability]] to resultCapability.
// p. Set rejectElement.[[RemainingElements]] to
// remainingElementsCount.
const rejectElement = CreatePromiseAnyRejectElementFunction(
rejectElementContext, index, nativeContext);
// q. Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] + 1.
const remainingElementsCount = UnsafeCast<Smi>(
rejectElementContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementRemainingSlot]);
rejectElementContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementRemainingSlot] =
remainingElementsCount + 1;
// r. Perform ? Invoke(nextPromise, "then", «
// resultCapability.[[Resolve]], rejectElement »).
let thenResult: JSAny;
const then = GetProperty(nextPromise, kThenString);
thenResult = Call(
context, then, nextPromise,
UnsafeCast<JSAny>(resultCapability.resolve), rejectElement);
// s. Increase index by 1.
index += 1;
// For catch prediction, mark that rejections here are
// semantically handled by the combined Promise.
if (IsDebugActive() && Is<JSPromise>(thenResult)) deferred {
SetPropertyStrict(
context, thenResult, kPromiseHandledBySymbol,
resultCapability.promise);
SetPropertyStrict(
context, rejectElement, kPromiseForwardingHandlerSymbol, True);
}
}
}
} catch (e) deferred {
iterator::IteratorCloseOnException(iteratorRecord);
goto Reject(e);
} label Done {}
transitioning macro ConstructAggregateError(implicit context: Context)(
errorsArray: FixedArray): JSObject {
const obj: JSAggregateError = error::ConstructInternalAggregateErrorHelper(
context, SmiConstant(MessageTemplate::kAllPromisesRejected));
obj.errors = errorsArray;
return obj;
}
// (8.d)
// i. Set iteratorRecord.[[Done]] to true.
// ii. Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] - 1.
let remainingElementsCount = UnsafeCast<Smi>(
rejectElementContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementRemainingSlot]);
remainingElementsCount -= 1;
rejectElementContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementRemainingSlot] =
remainingElementsCount;
extern macro PromiseAnyRejectElementSharedFunConstant(): SharedFunctionInfo;
const errorsArray = growableErrorsArray.ToFixedArray();
rejectElementContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementErrorsArraySlot] =
errorsArray;
// iii. If remainingElementsCount.[[Value]] is 0, then
if (remainingElementsCount == 0) deferred {
// 1. Let error be a newly created AggregateError object.
// 2. Set error.[[AggregateErrors]] to errors.
const error = ConstructAggregateError(errorsArray);
// 3. Return ThrowCompletion(error).
goto Reject(error);
}
// iv. Return resultCapability.[[Promise]].
return resultCapability.promise;
}
// https://tc39.es/proposal-promise-any/#sec-promise.any
transitioning javascript builtin
PromiseAny(
js-implicit context: Context, receiver: JSAny)(iterable: JSAny): JSAny {
// 1. Let C be the this value.
const receiver = Cast<JSReceiver>(receiver)
otherwise ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'Promise.any');
// 2. Let promiseCapability be ? NewPromiseCapability(C).
const capability = NewPromiseCapability(receiver, False);
// NewPromiseCapability guarantees that receiver is Constructor
assert(Is<Constructor>(receiver));
const constructor = UnsafeCast<Constructor>(receiver);
try {
let iteratorRecord: iterator::IteratorRecord;
try {
// 3. Let iteratorRecord be GetIterator(iterable).
// 4. IfAbruptRejectPromise(iteratorRecord, promiseCapability).
// (catch below)
iteratorRecord = iterator::GetIterator(iterable);
// 5. Let result be PerformPromiseAny(iteratorRecord, C,
// promiseCapability).
// 6. If result is an abrupt completion, then
// a. If iteratorRecord.[[Done]] is false, set result to
// IteratorClose(iteratorRecord, result).
// b. IfAbruptRejectPromise(result, promiseCapability).
// [Iterator closing handled by PerformPromiseAny]
// 7. Return Completion(result).
return PerformPromiseAny(iteratorRecord, constructor, capability)
otherwise Reject;
} catch (e) deferred {
goto Reject(e);
}
} label Reject(e: Object) deferred {
// Exception must be bound to a JS value.
assert(e != TheHole);
Call(
context, UnsafeCast<Callable>(capability.reject), Undefined,
UnsafeCast<JSAny>(e));
return capability.promise;
}
}
transitioning macro ConstructAggregateError(implicit context: Context)(
errorsArray: FixedArray): JSObject {
const obj: JSAggregateError = error::ConstructInternalAggregateErrorHelper(
context, SmiConstant(MessageTemplate::kAllPromisesRejected));
obj.errors = errorsArray;
return obj;
}
extern macro PromiseAnyRejectElementSharedFunConstant(): SharedFunctionInfo;
}

View File

@ -6,104 +6,104 @@
#include 'src/builtins/builtins-promise-gen.h'
namespace runtime {
extern transitioning runtime
DebugPushPromise(implicit context: Context)(JSAny): JSAny;
extern transitioning runtime
DebugPushPromise(implicit context: Context)(JSAny): JSAny;
extern transitioning runtime
DebugPopPromise(implicit context: Context)(): JSAny;
extern transitioning runtime
DebugPopPromise(implicit context: Context)(): JSAny;
extern transitioning runtime
PromiseHookInit(implicit context: Context)(Object, Object): JSAny;
extern transitioning runtime
PromiseHookInit(implicit context: Context)(Object, Object): JSAny;
}
// https://tc39.es/ecma262/#sec-promise-constructor
namespace promise {
extern runtime IncrementUseCounter(Context, Smi): void;
type UseCounterFeature extends int31
constexpr 'v8::Isolate::UseCounterFeature';
const kPromiseConstructorReturnedUndefined: constexpr UseCounterFeature
generates 'v8::Isolate::kPromiseConstructorReturnedUndefined';
extern runtime IncrementUseCounter(Context, Smi): void;
type UseCounterFeature extends int31
constexpr 'v8::Isolate::UseCounterFeature';
const kPromiseConstructorReturnedUndefined: constexpr UseCounterFeature
generates 'v8::Isolate::kPromiseConstructorReturnedUndefined';
extern macro
IsDebugActive(): bool;
extern macro
IsDebugActive(): bool;
transitioning macro
HasAccessCheckFailed(implicit context: Context)(
nativeContext: NativeContext, promiseFun: JSAny, executor: JSAny): bool {
BranchIfAccessCheckFailed(nativeContext, promiseFun, executor)
otherwise return true;
return false;
}
extern macro ConstructorBuiltinsAssembler::EmitFastNewObject(
Context, JSFunction, JSReceiver): JSObject;
extern macro
PromiseBuiltinsAssembler::IsPromiseHookEnabledOrHasAsyncEventDelegate(): bool;
// https://tc39.es/ecma262/#sec-promise-executor
transitioning javascript builtin
PromiseConstructor(
js-implicit context: NativeContext, receiver: JSAny,
newTarget: JSAny)(executor: JSAny): JSAny {
// 1. If NewTarget is undefined, throw a TypeError exception.
if (newTarget == Undefined) {
ThrowTypeError(MessageTemplate::kNotAPromise, newTarget);
}
// 2. If IsCallable(executor) is false, throw a TypeError exception.
if (!Is<Callable>(executor)) {
ThrowTypeError(MessageTemplate::kResolverNotAFunction, executor);
}
const promiseFun = UnsafeCast<JSFunction>(
context[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
// Silently fail if the stack looks fishy.
if (HasAccessCheckFailed(context, promiseFun, executor)) {
IncrementUseCounter(
context, SmiConstant(kPromiseConstructorReturnedUndefined));
return Undefined;
}
let result: JSPromise;
if (promiseFun == newTarget) {
result = NewJSPromise();
} else {
result = UnsafeCast<JSPromise>(EmitFastNewObject(
context, promiseFun, UnsafeCast<JSReceiver>(newTarget)));
PromiseInit(result);
if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) {
runtime::PromiseHookInit(result, Undefined);
}
}
const isDebugActive = IsDebugActive();
if (isDebugActive) runtime::DebugPushPromise(result);
const funcs = CreatePromiseResolvingFunctions(result, True, context);
const resolve = funcs.resolve;
const reject = funcs.reject;
try {
Call(context, UnsafeCast<Callable>(executor), Undefined, resolve, reject);
} catch (e) {
Call(context, reject, Undefined, e);
}
if (isDebugActive) runtime::DebugPopPromise();
return result;
}
// Promise.prototype.catch ( onRejected )
// https://tc39.es/ecma262/#sec-promise.prototype.catch
transitioning javascript builtin
PromisePrototypeCatch(js-implicit context: Context, receiver: JSAny)(
onRejected: JSAny): JSAny {
// 1. Let promise be the this value.
// 2. Return ? Invoke(promise, "then", « undefined, onRejected »).
const nativeContext = LoadNativeContext(context);
return UnsafeCast<JSAny>(
InvokeThen(nativeContext, receiver, Undefined, onRejected));
}
transitioning macro
HasAccessCheckFailed(implicit context: Context)(
nativeContext: NativeContext, promiseFun: JSAny, executor: JSAny): bool {
BranchIfAccessCheckFailed(nativeContext, promiseFun, executor)
otherwise return true;
return false;
}
extern macro ConstructorBuiltinsAssembler::EmitFastNewObject(
Context, JSFunction, JSReceiver): JSObject;
extern macro
PromiseBuiltinsAssembler::IsPromiseHookEnabledOrHasAsyncEventDelegate(): bool;
// https://tc39.es/ecma262/#sec-promise-executor
transitioning javascript builtin
PromiseConstructor(
js-implicit context: NativeContext, receiver: JSAny,
newTarget: JSAny)(executor: JSAny): JSAny {
// 1. If NewTarget is undefined, throw a TypeError exception.
if (newTarget == Undefined) {
ThrowTypeError(MessageTemplate::kNotAPromise, newTarget);
}
// 2. If IsCallable(executor) is false, throw a TypeError exception.
if (!Is<Callable>(executor)) {
ThrowTypeError(MessageTemplate::kResolverNotAFunction, executor);
}
const promiseFun = UnsafeCast<JSFunction>(
context[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
// Silently fail if the stack looks fishy.
if (HasAccessCheckFailed(context, promiseFun, executor)) {
IncrementUseCounter(
context, SmiConstant(kPromiseConstructorReturnedUndefined));
return Undefined;
}
let result: JSPromise;
if (promiseFun == newTarget) {
result = NewJSPromise();
} else {
result = UnsafeCast<JSPromise>(EmitFastNewObject(
context, promiseFun, UnsafeCast<JSReceiver>(newTarget)));
PromiseInit(result);
if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) {
runtime::PromiseHookInit(result, Undefined);
}
}
const isDebugActive = IsDebugActive();
if (isDebugActive) runtime::DebugPushPromise(result);
const funcs = CreatePromiseResolvingFunctions(result, True, context);
const resolve = funcs.resolve;
const reject = funcs.reject;
try {
Call(context, UnsafeCast<Callable>(executor), Undefined, resolve, reject);
} catch (e) {
Call(context, reject, Undefined, e);
}
if (isDebugActive) runtime::DebugPopPromise();
return result;
}
// Promise.prototype.catch ( onRejected )
// https://tc39.es/ecma262/#sec-promise.prototype.catch
transitioning javascript builtin
PromisePrototypeCatch(
js-implicit context: Context, receiver: JSAny)(onRejected: JSAny): JSAny {
// 1. Let promise be the this value.
// 2. Return ? Invoke(promise, "then", « undefined, onRejected »).
const nativeContext = LoadNativeContext(context);
return UnsafeCast<JSAny>(
InvokeThen(nativeContext, receiver, Undefined, onRejected));
}
}

View File

@ -7,202 +7,201 @@
namespace promise {
// TODO(joshualitt): The below ContextSlots are only available on synthetic
// contexts created by the promise pipeline for use in the promise pipeline.
// However, with Torque we should type the context and its slots to prevent
// accidentially using these slots on contexts which don't support them.
const kPromiseBuiltinsValueSlot: constexpr ContextSlot
generates 'PromiseBuiltins::kValueSlot';
const kPromiseBuiltinsOnFinallySlot: constexpr ContextSlot
generates 'PromiseBuiltins::kOnFinallySlot';
const kPromiseBuiltinsConstructorSlot: constexpr ContextSlot
generates 'PromiseBuiltins::kConstructorSlot';
const kPromiseBuiltinsPromiseValueThunkOrReasonContextLength: constexpr int31
generates 'PromiseBuiltins::kPromiseValueThunkOrReasonContextLength';
const kPromiseBuiltinsPromiseFinallyContextLength: constexpr int31
generates 'PromiseBuiltins::kPromiseFinallyContextLength';
// TODO(joshualitt): The below ContextSlots are only available on synthetic
// contexts created by the promise pipeline for use in the promise pipeline.
// However, with Torque we should type the context and its slots to prevent
// accidentially using these slots on contexts which don't support them.
const kPromiseBuiltinsValueSlot: constexpr ContextSlot
generates 'PromiseBuiltins::kValueSlot';
const kPromiseBuiltinsOnFinallySlot: constexpr ContextSlot
generates 'PromiseBuiltins::kOnFinallySlot';
const kPromiseBuiltinsConstructorSlot: constexpr ContextSlot
generates 'PromiseBuiltins::kConstructorSlot';
const kPromiseBuiltinsPromiseValueThunkOrReasonContextLength: constexpr int31
generates 'PromiseBuiltins::kPromiseValueThunkOrReasonContextLength';
const kPromiseBuiltinsPromiseFinallyContextLength: constexpr int31
generates 'PromiseBuiltins::kPromiseFinallyContextLength';
transitioning javascript builtin
PromiseValueThunkFinally(js-implicit context: Context, receiver: JSAny)():
JSAny {
return UnsafeCast<JSAny>(context[kPromiseBuiltinsValueSlot]);
}
transitioning javascript builtin
PromiseValueThunkFinally(
js-implicit context: Context, receiver: JSAny)(): JSAny {
return UnsafeCast<JSAny>(context[kPromiseBuiltinsValueSlot]);
}
transitioning javascript builtin
PromiseThrowerFinally(js-implicit context: Context, receiver: JSAny)():
never {
const reason = UnsafeCast<JSAny>(context[kPromiseBuiltinsValueSlot]);
Throw(reason);
}
transitioning javascript builtin
PromiseThrowerFinally(js-implicit context: Context, receiver: JSAny)(): never {
const reason = UnsafeCast<JSAny>(context[kPromiseBuiltinsValueSlot]);
Throw(reason);
}
macro CreateThrowerFunction(implicit context: Context)(
nativeContext: NativeContext, reason: JSAny): JSFunction {
const throwerContext = AllocateSyntheticFunctionContext(
nativeContext, kPromiseBuiltinsPromiseValueThunkOrReasonContextLength);
throwerContext[kPromiseBuiltinsValueSlot] = reason;
const map = UnsafeCast<Map>(
nativeContext
[NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]);
const throwerInfo = PromiseThrowerFinallySharedFunConstant();
return AllocateFunctionWithMapAndContext(map, throwerInfo, throwerContext);
}
macro CreateThrowerFunction(implicit context: Context)(
nativeContext: NativeContext, reason: JSAny): JSFunction {
const throwerContext = AllocateSyntheticFunctionContext(
nativeContext, kPromiseBuiltinsPromiseValueThunkOrReasonContextLength);
throwerContext[kPromiseBuiltinsValueSlot] = reason;
const map = UnsafeCast<Map>(
nativeContext
[NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]);
const throwerInfo = PromiseThrowerFinallySharedFunConstant();
return AllocateFunctionWithMapAndContext(map, throwerInfo, throwerContext);
}
transitioning javascript builtin
PromiseCatchFinally(js-implicit context: Context, receiver: JSAny)(
reason: JSAny): JSAny {
// 1. Let onFinally be F.[[OnFinally]].
// 2. Assert: IsCallable(onFinally) is true.
const onFinally =
UnsafeCast<Callable>(context[kPromiseBuiltinsOnFinallySlot]);
transitioning javascript builtin
PromiseCatchFinally(
js-implicit context: Context, receiver: JSAny)(reason: JSAny): JSAny {
// 1. Let onFinally be F.[[OnFinally]].
// 2. Assert: IsCallable(onFinally) is true.
const onFinally =
UnsafeCast<Callable>(context[kPromiseBuiltinsOnFinallySlot]);
// 3. Let result be ? Call(onFinally).
const result = Call(context, onFinally, Undefined);
// 3. Let result be ? Call(onFinally).
const result = Call(context, onFinally, Undefined);
// 4. Let C be F.[[Constructor]].
const constructor =
UnsafeCast<JSFunction>(context[kPromiseBuiltinsConstructorSlot]);
// 4. Let C be F.[[Constructor]].
const constructor =
UnsafeCast<JSFunction>(context[kPromiseBuiltinsConstructorSlot]);
// 5. Assert: IsConstructor(C) is true.
assert(IsConstructor(constructor));
// 5. Assert: IsConstructor(C) is true.
assert(IsConstructor(constructor));
// 6. Let promise be ? PromiseResolve(C, result).
const promise = PromiseResolve(constructor, result);
// 6. Let promise be ? PromiseResolve(C, result).
const promise = PromiseResolve(constructor, result);
// 7. Let thrower be equivalent to a function that throws reason.
const nativeContext = LoadNativeContext(context);
const thrower = CreateThrowerFunction(nativeContext, reason);
// 7. Let thrower be equivalent to a function that throws reason.
const nativeContext = LoadNativeContext(context);
const thrower = CreateThrowerFunction(nativeContext, reason);
// 8. Return ? Invoke(promise, "then", « thrower »).
return UnsafeCast<JSAny>(InvokeThen(nativeContext, promise, thrower));
}
// 8. Return ? Invoke(promise, "then", « thrower »).
return UnsafeCast<JSAny>(InvokeThen(nativeContext, promise, thrower));
}
macro CreateValueThunkFunction(implicit context: Context)(
nativeContext: NativeContext, value: JSAny): JSFunction {
const valueThunkContext = AllocateSyntheticFunctionContext(
nativeContext, kPromiseBuiltinsPromiseValueThunkOrReasonContextLength);
valueThunkContext[kPromiseBuiltinsValueSlot] = value;
const map = UnsafeCast<Map>(
nativeContext
[NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]);
const valueThunkInfo = PromiseValueThunkFinallySharedFunConstant();
return AllocateFunctionWithMapAndContext(
map, valueThunkInfo, valueThunkContext);
}
macro CreateValueThunkFunction(implicit context: Context)(
nativeContext: NativeContext, value: JSAny): JSFunction {
const valueThunkContext = AllocateSyntheticFunctionContext(
nativeContext, kPromiseBuiltinsPromiseValueThunkOrReasonContextLength);
valueThunkContext[kPromiseBuiltinsValueSlot] = value;
const map = UnsafeCast<Map>(
nativeContext
[NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]);
const valueThunkInfo = PromiseValueThunkFinallySharedFunConstant();
return AllocateFunctionWithMapAndContext(
map, valueThunkInfo, valueThunkContext);
}
transitioning javascript builtin
PromiseThenFinally(js-implicit context: Context, receiver: JSAny)(
value: JSAny): JSAny {
// 1. Let onFinally be F.[[OnFinally]].
// 2. Assert: IsCallable(onFinally) is true.
const onFinally =
UnsafeCast<Callable>(context[kPromiseBuiltinsOnFinallySlot]);
transitioning javascript builtin
PromiseThenFinally(
js-implicit context: Context, receiver: JSAny)(value: JSAny): JSAny {
// 1. Let onFinally be F.[[OnFinally]].
// 2. Assert: IsCallable(onFinally) is true.
const onFinally =
UnsafeCast<Callable>(context[kPromiseBuiltinsOnFinallySlot]);
// 3. Let result be ? Call(onFinally).
const result = Call(context, onFinally, Undefined);
// 3. Let result be ? Call(onFinally).
const result = Call(context, onFinally, Undefined);
// 4. Let C be F.[[Constructor]].
const constructor =
UnsafeCast<JSFunction>(context[kPromiseBuiltinsConstructorSlot]);
// 4. Let C be F.[[Constructor]].
const constructor =
UnsafeCast<JSFunction>(context[kPromiseBuiltinsConstructorSlot]);
// 5. Assert: IsConstructor(C) is true.
assert(IsConstructor(constructor));
// 5. Assert: IsConstructor(C) is true.
assert(IsConstructor(constructor));
// 6. Let promise be ? PromiseResolve(C, result).
const promise = PromiseResolve(constructor, result);
// 6. Let promise be ? PromiseResolve(C, result).
const promise = PromiseResolve(constructor, result);
// 7. Let valueThunk be equivalent to a function that returns value.
const nativeContext = LoadNativeContext(context);
const valueThunk = CreateValueThunkFunction(nativeContext, value);
// 7. Let valueThunk be equivalent to a function that returns value.
const nativeContext = LoadNativeContext(context);
const valueThunk = CreateValueThunkFunction(nativeContext, value);
// 8. Return ? Invoke(promise, "then", « valueThunk »).
return UnsafeCast<JSAny>(InvokeThen(nativeContext, promise, valueThunk));
}
// 8. Return ? Invoke(promise, "then", « valueThunk »).
return UnsafeCast<JSAny>(InvokeThen(nativeContext, promise, valueThunk));
}
struct PromiseFinallyFunctions {
then_finally: JSFunction;
catch_finally: JSFunction;
}
struct PromiseFinallyFunctions {
then_finally: JSFunction;
catch_finally: JSFunction;
}
macro CreatePromiseFinallyFunctions(implicit context: Context)(
nativeContext: NativeContext, onFinally: Callable,
constructor: JSReceiver): PromiseFinallyFunctions {
const promiseContext = AllocateSyntheticFunctionContext(
nativeContext, kPromiseBuiltinsPromiseFinallyContextLength);
promiseContext[kPromiseBuiltinsOnFinallySlot] = onFinally;
promiseContext[kPromiseBuiltinsConstructorSlot] = constructor;
const map = UnsafeCast<Map>(
nativeContext
[NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]);
const thenFinallyInfo = PromiseThenFinallySharedFunConstant();
const thenFinally =
AllocateFunctionWithMapAndContext(map, thenFinallyInfo, promiseContext);
const catchFinallyInfo = PromiseCatchFinallySharedFunConstant();
const catchFinally = AllocateFunctionWithMapAndContext(
map, catchFinallyInfo, promiseContext);
return PromiseFinallyFunctions{
then_finally: thenFinally,
catch_finally: catchFinally
};
}
macro CreatePromiseFinallyFunctions(implicit context: Context)(
nativeContext: NativeContext, onFinally: Callable,
constructor: JSReceiver): PromiseFinallyFunctions {
const promiseContext = AllocateSyntheticFunctionContext(
nativeContext, kPromiseBuiltinsPromiseFinallyContextLength);
promiseContext[kPromiseBuiltinsOnFinallySlot] = onFinally;
promiseContext[kPromiseBuiltinsConstructorSlot] = constructor;
const map = UnsafeCast<Map>(
nativeContext
[NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]);
const thenFinallyInfo = PromiseThenFinallySharedFunConstant();
const thenFinally =
AllocateFunctionWithMapAndContext(map, thenFinallyInfo, promiseContext);
const catchFinallyInfo = PromiseCatchFinallySharedFunConstant();
const catchFinally =
AllocateFunctionWithMapAndContext(map, catchFinallyInfo, promiseContext);
return PromiseFinallyFunctions{
then_finally: thenFinally,
catch_finally: catchFinally
};
}
transitioning javascript builtin
PromisePrototypeFinally(js-implicit context: Context, receiver: JSAny)(
onFinally: JSAny): JSAny {
// 1. Let promise be the this value.
// 2. If Type(promise) is not Object, throw a TypeError exception.
const jsReceiver = Cast<JSReceiver>(receiver) otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Promise.prototype.finally');
transitioning javascript builtin
PromisePrototypeFinally(
js-implicit context: Context, receiver: JSAny)(onFinally: JSAny): JSAny {
// 1. Let promise be the this value.
// 2. If Type(promise) is not Object, throw a TypeError exception.
const jsReceiver = Cast<JSReceiver>(receiver) otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Promise.prototype.finally');
// 3. Let C be ? SpeciesConstructor(promise, %Promise%).
const nativeContext = LoadNativeContext(context);
const promiseFun = UnsafeCast<Callable>(
nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
// 3. Let C be ? SpeciesConstructor(promise, %Promise%).
const nativeContext = LoadNativeContext(context);
const promiseFun = UnsafeCast<Callable>(
nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
let constructor: JSReceiver = promiseFun;
const receiverMap = jsReceiver.map;
if (!IsJSPromiseMap(receiverMap) ||
!IsPromiseSpeciesLookupChainIntact(nativeContext, receiverMap))
deferred {
constructor = SpeciesConstructor(jsReceiver, promiseFun);
}
// 4. Assert: IsConstructor(C) is true.
assert(IsConstructor(constructor));
// 5. If IsCallable(onFinally) is not true,
// a. Let thenFinally be onFinally.
// b. Let catchFinally be onFinally.
// 6. Else,
// a. Let thenFinally be a new built-in function object as defined
// in ThenFinally Function.
// b. Let catchFinally be a new built-in function object as
// defined in CatchFinally Function.
// c. Set thenFinally and catchFinally's [[Constructor]] internal
// slots to C.
// d. Set thenFinally and catchFinally's [[OnFinally]] internal
// slots to onFinally.
let thenFinally: JSAny;
let catchFinally: JSAny;
typeswitch (onFinally) {
case (onFinally: Callable): {
const pair = CreatePromiseFinallyFunctions(
nativeContext, onFinally, constructor);
thenFinally = pair.then_finally;
catchFinally = pair.catch_finally;
}
case (JSAny): deferred {
thenFinally = onFinally;
catchFinally = onFinally;
}
let constructor: JSReceiver = promiseFun;
const receiverMap = jsReceiver.map;
if (!IsJSPromiseMap(receiverMap) ||
!IsPromiseSpeciesLookupChainIntact(nativeContext, receiverMap))
deferred {
constructor = SpeciesConstructor(jsReceiver, promiseFun);
}
// 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »).
return UnsafeCast<JSAny>(
InvokeThen(nativeContext, receiver, thenFinally, catchFinally));
// 4. Assert: IsConstructor(C) is true.
assert(IsConstructor(constructor));
// 5. If IsCallable(onFinally) is not true,
// a. Let thenFinally be onFinally.
// b. Let catchFinally be onFinally.
// 6. Else,
// a. Let thenFinally be a new built-in function object as defined
// in ThenFinally Function.
// b. Let catchFinally be a new built-in function object as
// defined in CatchFinally Function.
// c. Set thenFinally and catchFinally's [[Constructor]] internal
// slots to C.
// d. Set thenFinally and catchFinally's [[OnFinally]] internal
// slots to onFinally.
let thenFinally: JSAny;
let catchFinally: JSAny;
typeswitch (onFinally) {
case (onFinally: Callable): {
const pair =
CreatePromiseFinallyFunctions(nativeContext, onFinally, constructor);
thenFinally = pair.then_finally;
catchFinally = pair.catch_finally;
}
case (JSAny): deferred {
thenFinally = onFinally;
catchFinally = onFinally;
}
}
extern macro PromiseCatchFinallySharedFunConstant(): SharedFunctionInfo;
extern macro PromiseThenFinallySharedFunConstant(): SharedFunctionInfo;
extern macro PromiseThrowerFinallySharedFunConstant(): SharedFunctionInfo;
extern macro PromiseValueThunkFinallySharedFunConstant(): SharedFunctionInfo;
// 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »).
return UnsafeCast<JSAny>(
InvokeThen(nativeContext, receiver, thenFinally, catchFinally));
}
extern macro PromiseCatchFinallySharedFunConstant(): SharedFunctionInfo;
extern macro PromiseThenFinallySharedFunConstant(): SharedFunctionInfo;
extern macro PromiseThrowerFinallySharedFunConstant(): SharedFunctionInfo;
extern macro PromiseValueThunkFinallySharedFunConstant(): SharedFunctionInfo;
}

View File

@ -6,68 +6,68 @@
// https://tc39.es/ecma262/#sec-promise-jobs
namespace promise {
extern macro IsJSPromiseMap(Map): bool;
extern macro IsJSPromiseMap(Map): bool;
// https://tc39.es/ecma262/#sec-promiseresolvethenablejob
transitioning builtin
PromiseResolveThenableJob(implicit context: Context)(
promiseToResolve: JSPromise, thenable: JSReceiver, then: JSAny): JSAny {
// We can use a simple optimization here if we know that {then} is the
// initial Promise.prototype.then method, and {thenable} is a JSPromise
// whose
// @@species lookup chain is intact: We can connect {thenable} and
// {promise_to_resolve} directly in that case and avoid the allocation of a
// temporary JSPromise and the closures plus context.
// https://tc39.es/ecma262/#sec-promiseresolvethenablejob
transitioning builtin
PromiseResolveThenableJob(implicit context: Context)(
promiseToResolve: JSPromise, thenable: JSReceiver, then: JSAny): JSAny {
// We can use a simple optimization here if we know that {then} is the
// initial Promise.prototype.then method, and {thenable} is a JSPromise
// whose
// @@species lookup chain is intact: We can connect {thenable} and
// {promise_to_resolve} directly in that case and avoid the allocation of a
// temporary JSPromise and the closures plus context.
//
// We take the generic (slow-)path if a PromiseHook is enabled or the
// debugger is active, to make sure we expose spec compliant behavior.
const nativeContext = LoadNativeContext(context);
const promiseThen = nativeContext[NativeContextSlot::PROMISE_THEN_INDEX];
const thenableMap = thenable.map;
if (TaggedEqual(then, promiseThen) && IsJSPromiseMap(thenableMap) &&
!IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() &&
IsPromiseSpeciesLookupChainIntact(nativeContext, thenableMap)) {
// We know that the {thenable} is a JSPromise, which doesn't require
// any special treatment and that {then} corresponds to the initial
// Promise.prototype.then method. So instead of allocating a temporary
// JSPromise to connect the {thenable} with the {promise_to_resolve},
// we can directly schedule the {promise_to_resolve} with default
// handlers onto the {thenable} promise. This does not only save the
// JSPromise allocation, but also avoids the allocation of the two
// resolving closures and the shared context.
//
// We take the generic (slow-)path if a PromiseHook is enabled or the
// debugger is active, to make sure we expose spec compliant behavior.
const nativeContext = LoadNativeContext(context);
const promiseThen = nativeContext[NativeContextSlot::PROMISE_THEN_INDEX];
const thenableMap = thenable.map;
if (TaggedEqual(then, promiseThen) && IsJSPromiseMap(thenableMap) &&
!IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() &&
IsPromiseSpeciesLookupChainIntact(nativeContext, thenableMap)) {
// We know that the {thenable} is a JSPromise, which doesn't require
// any special treatment and that {then} corresponds to the initial
// Promise.prototype.then method. So instead of allocating a temporary
// JSPromise to connect the {thenable} with the {promise_to_resolve},
// we can directly schedule the {promise_to_resolve} with default
// handlers onto the {thenable} promise. This does not only save the
// JSPromise allocation, but also avoids the allocation of the two
// resolving closures and the shared context.
//
// What happens normally in this case is
//
// resolve, reject = CreateResolvingFunctions(promise_to_resolve)
// result_capability = NewPromiseCapability(%Promise%)
// PerformPromiseThen(thenable, resolve, reject, result_capability)
//
// which means that PerformPromiseThen will either schedule a new
// PromiseReaction with resolve and reject or a PromiseReactionJob
// with resolve or reject based on the state of {thenable}. And
// resolve or reject will just invoke the default [[Resolve]] or
// [[Reject]] functions on the {promise_to_resolve}.
//
// This is the same as just doing
//
// PerformPromiseThen(thenable, undefined, undefined,
// promise_to_resolve)
//
// which performs exactly the same (observable) steps.
return PerformPromiseThen(
UnsafeCast<JSPromise>(thenable), UndefinedConstant(),
UndefinedConstant(), promiseToResolve);
} else {
const funcs = CreatePromiseResolvingFunctions(
promiseToResolve, False, nativeContext);
const resolve = funcs.resolve;
const reject = funcs.reject;
try {
return Call(
context, UnsafeCast<Callable>(then), thenable, resolve, reject);
} catch (e) {
return Call(context, UnsafeCast<Callable>(reject), Undefined, e);
}
// What happens normally in this case is
//
// resolve, reject = CreateResolvingFunctions(promise_to_resolve)
// result_capability = NewPromiseCapability(%Promise%)
// PerformPromiseThen(thenable, resolve, reject, result_capability)
//
// which means that PerformPromiseThen will either schedule a new
// PromiseReaction with resolve and reject or a PromiseReactionJob
// with resolve or reject based on the state of {thenable}. And
// resolve or reject will just invoke the default [[Resolve]] or
// [[Reject]] functions on the {promise_to_resolve}.
//
// This is the same as just doing
//
// PerformPromiseThen(thenable, undefined, undefined,
// promise_to_resolve)
//
// which performs exactly the same (observable) steps.
return PerformPromiseThen(
UnsafeCast<JSPromise>(thenable), UndefinedConstant(),
UndefinedConstant(), promiseToResolve);
} else {
const funcs =
CreatePromiseResolvingFunctions(promiseToResolve, False, nativeContext);
const resolve = funcs.resolve;
const reject = funcs.reject;
try {
return Call(
context, UnsafeCast<Callable>(then), thenable, resolve, reject);
} catch (e) {
return Call(context, UnsafeCast<Callable>(reject), Undefined, e);
}
}
}
}

View File

@ -6,268 +6,267 @@
#include 'src/builtins/builtins-promise-gen.h'
namespace runtime {
extern transitioning runtime
AllowDynamicFunction(implicit context: Context)(JSAny): JSAny;
extern transitioning runtime
AllowDynamicFunction(implicit context: Context)(JSAny): JSAny;
}
// Unsafe functions that should be used very carefully.
namespace promise_internal {
extern macro PromiseBuiltinsAssembler::ZeroOutEmbedderOffsets(JSPromise):
void;
extern macro PromiseBuiltinsAssembler::ZeroOutEmbedderOffsets(JSPromise): void;
extern macro PromiseBuiltinsAssembler::AllocateJSPromise(Context): HeapObject;
extern macro PromiseBuiltinsAssembler::AllocateJSPromise(Context): HeapObject;
}
namespace promise {
extern macro IsFunctionWithPrototypeSlotMap(Map): bool;
extern macro IsFunctionWithPrototypeSlotMap(Map): bool;
@export
macro PromiseHasHandler(promise: JSPromise): bool {
return promise.HasHandler();
@export
macro PromiseHasHandler(promise: JSPromise): bool {
return promise.HasHandler();
}
@export
macro PromiseInit(promise: JSPromise): void {
promise.reactions_or_result = kZero;
promise.flags = SmiTag(JSPromiseFlags{
status: PromiseState::kPending,
has_handler: false,
handled_hint: false,
async_task_id: 0
});
promise_internal::ZeroOutEmbedderOffsets(promise);
}
macro InnerNewJSPromise(implicit context: Context)(): JSPromise {
const nativeContext = LoadNativeContext(context);
const promiseFun = UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
assert(IsFunctionWithPrototypeSlotMap(promiseFun.map));
const promiseMap = UnsafeCast<Map>(promiseFun.prototype_or_initial_map);
const promiseHeapObject = promise_internal::AllocateJSPromise(context);
* UnsafeConstCast(& promiseHeapObject.map) = promiseMap;
const promise = UnsafeCast<JSPromise>(promiseHeapObject);
promise.properties_or_hash = kEmptyFixedArray;
promise.elements = kEmptyFixedArray;
promise.reactions_or_result = kZero;
promise.flags = SmiTag(JSPromiseFlags{
status: PromiseState::kPending,
has_handler: false,
handled_hint: false,
async_task_id: 0
});
return promise;
}
macro NewPromiseFulfillReactionJobTask(implicit context: Context)(
handlerContext: Context, argument: Object, handler: Callable|Undefined,
promiseOrCapability: JSPromise|PromiseCapability|
Undefined): PromiseFulfillReactionJobTask {
const nativeContext = LoadNativeContext(handlerContext);
return new PromiseFulfillReactionJobTask{
map: PromiseFulfillReactionJobTaskMapConstant(),
argument,
context: handlerContext,
handler,
promise_or_capability: promiseOrCapability,
continuation_preserved_embedder_data: nativeContext
[NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX]
};
}
macro NewPromiseRejectReactionJobTask(implicit context: Context)(
handlerContext: Context, argument: Object, handler: Callable|Undefined,
promiseOrCapability: JSPromise|PromiseCapability|
Undefined): PromiseRejectReactionJobTask {
const nativeContext = LoadNativeContext(handlerContext);
return new PromiseRejectReactionJobTask{
map: PromiseRejectReactionJobTaskMapConstant(),
argument,
context: handlerContext,
handler,
promise_or_capability: promiseOrCapability,
continuation_preserved_embedder_data: nativeContext
[NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX]
};
}
// These allocate and initialize a promise with pending state and
// undefined fields.
//
// This uses the given parent as the parent promise for the promise
// init hook.
@export
transitioning macro NewJSPromise(implicit context: Context)(parent: Object):
JSPromise {
const instance = InnerNewJSPromise();
PromiseInit(instance);
if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) {
runtime::PromiseHookInit(instance, parent);
}
return instance;
}
@export
macro PromiseInit(promise: JSPromise): void {
promise.reactions_or_result = kZero;
promise.flags = SmiTag(JSPromiseFlags{
status: PromiseState::kPending,
has_handler: false,
handled_hint: false,
async_task_id: 0
});
promise_internal::ZeroOutEmbedderOffsets(promise);
// This uses undefined as the parent promise for the promise init
// hook.
@export
transitioning macro NewJSPromise(implicit context: Context)(): JSPromise {
return NewJSPromise(Undefined);
}
// This allocates and initializes a promise with the given state and
// fields.
@export
transitioning macro NewJSPromise(implicit context: Context)(
status: constexpr PromiseState, result: JSAny): JSPromise {
assert(status != PromiseState::kPending);
const instance = InnerNewJSPromise();
instance.reactions_or_result = result;
instance.SetStatus(status);
promise_internal::ZeroOutEmbedderOffsets(instance);
if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) {
runtime::PromiseHookInit(instance, Undefined);
}
return instance;
}
macro InnerNewJSPromise(implicit context: Context)(): JSPromise {
const nativeContext = LoadNativeContext(context);
const promiseFun = UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
assert(IsFunctionWithPrototypeSlotMap(promiseFun.map));
const promiseMap = UnsafeCast<Map>(promiseFun.prototype_or_initial_map);
const promiseHeapObject = promise_internal::AllocateJSPromise(context);
* UnsafeConstCast(& promiseHeapObject.map) = promiseMap;
const promise = UnsafeCast<JSPromise>(promiseHeapObject);
promise.properties_or_hash = kEmptyFixedArray;
promise.elements = kEmptyFixedArray;
promise.reactions_or_result = kZero;
promise.flags = SmiTag(JSPromiseFlags{
status: PromiseState::kPending,
has_handler: false,
handled_hint: false,
async_task_id: 0
});
return promise;
}
macro NewPromiseReaction(implicit context: Context)(
handlerContext: Context, next: Zero|PromiseReaction,
promiseOrCapability: JSPromise|PromiseCapability|Undefined,
fulfillHandler: Callable|Undefined,
rejectHandler: Callable|Undefined): PromiseReaction {
const nativeContext = LoadNativeContext(handlerContext);
return new PromiseReaction{
map: PromiseReactionMapConstant(),
next: next,
reject_handler: rejectHandler,
fulfill_handler: fulfillHandler,
promise_or_capability: promiseOrCapability,
continuation_preserved_embedder_data: nativeContext
[NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX]
};
}
macro NewPromiseFulfillReactionJobTask(implicit context: Context)(
handlerContext: Context, argument: Object, handler: Callable|Undefined,
promiseOrCapability: JSPromise|PromiseCapability|
Undefined): PromiseFulfillReactionJobTask {
const nativeContext = LoadNativeContext(handlerContext);
return new PromiseFulfillReactionJobTask{
map: PromiseFulfillReactionJobTaskMapConstant(),
argument,
context: handlerContext,
handler,
promise_or_capability: promiseOrCapability,
continuation_preserved_embedder_data: nativeContext
[NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX]
};
}
extern macro PromiseResolveThenableJobTaskMapConstant(): Map;
macro NewPromiseRejectReactionJobTask(implicit context: Context)(
handlerContext: Context, argument: Object, handler: Callable|Undefined,
promiseOrCapability: JSPromise|PromiseCapability|
Undefined): PromiseRejectReactionJobTask {
const nativeContext = LoadNativeContext(handlerContext);
return new PromiseRejectReactionJobTask{
map: PromiseRejectReactionJobTaskMapConstant(),
argument,
context: handlerContext,
handler,
promise_or_capability: promiseOrCapability,
continuation_preserved_embedder_data: nativeContext
[NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX]
};
}
// These allocate and initialize a promise with pending state and
// undefined fields.
// https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob
macro NewPromiseResolveThenableJobTask(implicit context: Context)(
promiseToResolve: JSPromise, thenable: JSReceiver,
then: Callable): PromiseResolveThenableJobTask {
// 2. Let getThenRealmResult be GetFunctionRealm(then).
// 3. If getThenRealmResult is a normal completion, then let thenRealm be
// getThenRealmResult.[[Value]].
// 4. Otherwise, let thenRealm be null.
//
// This uses the given parent as the parent promise for the promise
// init hook.
@export
transitioning macro NewJSPromise(implicit context: Context)(parent: Object):
JSPromise {
const instance = InnerNewJSPromise();
PromiseInit(instance);
if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) {
runtime::PromiseHookInit(instance, parent);
}
return instance;
}
// The only cases where |thenRealm| can be null is when |then| is a revoked
// Proxy object, which would throw when it is called anyway. So instead of
// setting the context to null as the spec does, we just use the current
// realm.
const thenContext: Context = ExtractHandlerContext(then);
const nativeContext = LoadNativeContext(thenContext);
// This uses undefined as the parent promise for the promise init
// hook.
@export
transitioning macro NewJSPromise(implicit context: Context)(): JSPromise {
return NewJSPromise(Undefined);
}
// This allocates and initializes a promise with the given state and
// fields.
@export
transitioning macro NewJSPromise(implicit context: Context)(
status: constexpr PromiseState, result: JSAny): JSPromise {
assert(status != PromiseState::kPending);
const instance = InnerNewJSPromise();
instance.reactions_or_result = result;
instance.SetStatus(status);
promise_internal::ZeroOutEmbedderOffsets(instance);
if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) {
runtime::PromiseHookInit(instance, Undefined);
}
return instance;
}
macro NewPromiseReaction(implicit context: Context)(
handlerContext: Context, next: Zero|PromiseReaction,
promiseOrCapability: JSPromise|PromiseCapability|Undefined,
fulfillHandler: Callable|Undefined,
rejectHandler: Callable|Undefined): PromiseReaction {
const nativeContext = LoadNativeContext(handlerContext);
return new PromiseReaction{
map: PromiseReactionMapConstant(),
next: next,
reject_handler: rejectHandler,
fulfill_handler: fulfillHandler,
promise_or_capability: promiseOrCapability,
continuation_preserved_embedder_data: nativeContext
[NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX]
};
}
extern macro PromiseResolveThenableJobTaskMapConstant(): Map;
// https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob
macro NewPromiseResolveThenableJobTask(implicit context: Context)(
promiseToResolve: JSPromise, thenable: JSReceiver,
then: Callable): PromiseResolveThenableJobTask {
// 2. Let getThenRealmResult be GetFunctionRealm(then).
// 3. If getThenRealmResult is a normal completion, then let thenRealm be
// getThenRealmResult.[[Value]].
// 4. Otherwise, let thenRealm be null.
//
// The only cases where |thenRealm| can be null is when |then| is a revoked
// Proxy object, which would throw when it is called anyway. So instead of
// setting the context to null as the spec does, we just use the current
// realm.
const thenContext: Context = ExtractHandlerContext(then);
const nativeContext = LoadNativeContext(thenContext);
// 1. Let job be a new Job abstract closure with no parameters that
// captures promiseToResolve, thenable, and then...
// 5. Return { [[Job]]: job, [[Realm]]: thenRealm }.
return new PromiseResolveThenableJobTask{
map: PromiseResolveThenableJobTaskMapConstant(),
context: nativeContext,
promise_to_resolve: promiseToResolve,
thenable,
then
};
}
struct InvokeThenOneArgFunctor {
transitioning
macro Call(
nativeContext: NativeContext, then: JSAny, receiver: JSAny, arg1: JSAny,
_arg2: JSAny): JSAny {
return Call(nativeContext, then, receiver, arg1);
}
}
struct InvokeThenTwoArgFunctor {
transitioning
macro Call(
nativeContext: NativeContext, then: JSAny, receiver: JSAny, arg1: JSAny,
arg2: JSAny): JSAny {
return Call(nativeContext, then, receiver, arg1, arg2);
}
}
// 1. Let job be a new Job abstract closure with no parameters that
// captures promiseToResolve, thenable, and then...
// 5. Return { [[Job]]: job, [[Realm]]: thenRealm }.
return new PromiseResolveThenableJobTask{
map: PromiseResolveThenableJobTaskMapConstant(),
context: nativeContext,
promise_to_resolve: promiseToResolve,
thenable,
then
};
}
struct InvokeThenOneArgFunctor {
transitioning
macro InvokeThen<F: type>(implicit context: Context)(
nativeContext: NativeContext, receiver: JSAny, arg1: JSAny, arg2: JSAny,
callFunctor: F): JSAny {
// We can skip the "then" lookup on {receiver} if it's [[Prototype]]
// is the (initial) Promise.prototype and the Promise#then protector
// is intact, as that guards the lookup path for the "then" property
// on JSPromise instances which have the (initial) %PromisePrototype%.
if (!Is<Smi>(receiver) &&
IsPromiseThenLookupChainIntact(
nativeContext, UnsafeCast<HeapObject>(receiver).map)) {
const then = UnsafeCast<JSAny>(
nativeContext[NativeContextSlot::PROMISE_THEN_INDEX]);
return callFunctor.Call(nativeContext, then, receiver, arg1, arg2);
} else
deferred {
const then = UnsafeCast<JSAny>(GetProperty(receiver, kThenString));
return callFunctor.Call(nativeContext, then, receiver, arg1, arg2);
}
macro Call(
nativeContext: NativeContext, then: JSAny, receiver: JSAny, arg1: JSAny,
_arg2: JSAny): JSAny {
return Call(nativeContext, then, receiver, arg1);
}
}
struct InvokeThenTwoArgFunctor {
transitioning
macro InvokeThen(implicit context: Context)(
nativeContext: NativeContext, receiver: JSAny, arg: JSAny): JSAny {
return InvokeThen(
nativeContext, receiver, arg, Undefined, InvokeThenOneArgFunctor{});
}
transitioning
macro InvokeThen(implicit context: Context)(
nativeContext: NativeContext, receiver: JSAny, arg1: JSAny,
macro Call(
nativeContext: NativeContext, then: JSAny, receiver: JSAny, arg1: JSAny,
arg2: JSAny): JSAny {
return InvokeThen(
nativeContext, receiver, arg1, arg2, InvokeThenTwoArgFunctor{});
return Call(nativeContext, then, receiver, arg1, arg2);
}
}
transitioning
macro BranchIfAccessCheckFailed(implicit context: Context)(
nativeContext: NativeContext, promiseConstructor: JSAny,
executor: JSAny): void labels IfNoAccess {
try {
// If executor is a bound function, load the bound function until we've
// reached an actual function.
let foundExecutor = executor;
while (true) {
typeswitch (foundExecutor) {
case (f: JSFunction): {
// Load the context from the function and compare it to the Promise
// constructor's context. If they match, everything is fine,
// otherwise, bail out to the runtime.
const functionContext = f.context;
const nativeFunctionContext = LoadNativeContext(functionContext);
if (TaggedEqual(nativeContext, nativeFunctionContext)) {
goto HasAccess;
} else {
goto CallRuntime;
}
}
case (b: JSBoundFunction): {
foundExecutor = b.bound_target_function;
}
case (Object): {
transitioning
macro InvokeThen<F: type>(implicit context: Context)(
nativeContext: NativeContext, receiver: JSAny, arg1: JSAny, arg2: JSAny,
callFunctor: F): JSAny {
// We can skip the "then" lookup on {receiver} if it's [[Prototype]]
// is the (initial) Promise.prototype and the Promise#then protector
// is intact, as that guards the lookup path for the "then" property
// on JSPromise instances which have the (initial) %PromisePrototype%.
if (!Is<Smi>(receiver) &&
IsPromiseThenLookupChainIntact(
nativeContext, UnsafeCast<HeapObject>(receiver).map)) {
const then =
UnsafeCast<JSAny>(nativeContext[NativeContextSlot::PROMISE_THEN_INDEX]);
return callFunctor.Call(nativeContext, then, receiver, arg1, arg2);
} else
deferred {
const then = UnsafeCast<JSAny>(GetProperty(receiver, kThenString));
return callFunctor.Call(nativeContext, then, receiver, arg1, arg2);
}
}
transitioning
macro InvokeThen(implicit context: Context)(
nativeContext: NativeContext, receiver: JSAny, arg: JSAny): JSAny {
return InvokeThen(
nativeContext, receiver, arg, Undefined, InvokeThenOneArgFunctor{});
}
transitioning
macro InvokeThen(implicit context: Context)(
nativeContext: NativeContext, receiver: JSAny, arg1: JSAny,
arg2: JSAny): JSAny {
return InvokeThen(
nativeContext, receiver, arg1, arg2, InvokeThenTwoArgFunctor{});
}
transitioning
macro BranchIfAccessCheckFailed(implicit context: Context)(
nativeContext: NativeContext, promiseConstructor: JSAny,
executor: JSAny): void labels IfNoAccess {
try {
// If executor is a bound function, load the bound function until we've
// reached an actual function.
let foundExecutor = executor;
while (true) {
typeswitch (foundExecutor) {
case (f: JSFunction): {
// Load the context from the function and compare it to the Promise
// constructor's context. If they match, everything is fine,
// otherwise, bail out to the runtime.
const functionContext = f.context;
const nativeFunctionContext = LoadNativeContext(functionContext);
if (TaggedEqual(nativeContext, nativeFunctionContext)) {
goto HasAccess;
} else {
goto CallRuntime;
}
}
case (b: JSBoundFunction): {
foundExecutor = b.bound_target_function;
}
case (Object): {
goto CallRuntime;
}
}
} label CallRuntime deferred {
const result = runtime::AllowDynamicFunction(promiseConstructor);
if (result != True) {
goto IfNoAccess;
}
} label HasAccess {}
}
}
} label CallRuntime deferred {
const result = runtime::AllowDynamicFunction(promiseConstructor);
if (result != True) {
goto IfNoAccess;
}
} label HasAccess {}
}
}

View File

@ -6,126 +6,124 @@
namespace promise {
extern macro PromiseForwardingHandlerSymbolConstant(): Symbol;
const kPromiseForwardingHandlerSymbol: Symbol =
PromiseForwardingHandlerSymbolConstant();
extern macro PromiseHandledBySymbolConstant(): Symbol;
const kPromiseHandledBySymbol: Symbol = PromiseHandledBySymbolConstant();
extern macro ResolveStringConstant(): String;
const kResolveString: String = ResolveStringConstant();
extern macro SetPropertyStrict(Context, Object, Object, Object): Object;
extern macro IsPromiseResolveProtectorCellInvalid(): bool;
extern macro PromiseForwardingHandlerSymbolConstant(): Symbol;
const kPromiseForwardingHandlerSymbol: Symbol =
PromiseForwardingHandlerSymbolConstant();
extern macro PromiseHandledBySymbolConstant(): Symbol;
const kPromiseHandledBySymbol: Symbol = PromiseHandledBySymbolConstant();
extern macro ResolveStringConstant(): String;
const kResolveString: String = ResolveStringConstant();
extern macro SetPropertyStrict(Context, Object, Object, Object): Object;
extern macro IsPromiseResolveProtectorCellInvalid(): bool;
macro IsPromiseResolveLookupChainIntact(implicit context: Context)(
nativeContext: NativeContext, constructor: JSReceiver): bool {
if (IsForceSlowPath()) return false;
const promiseFun = UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
return promiseFun == constructor && !IsPromiseResolveProtectorCellInvalid();
}
// https://tc39.es/ecma262/#sec-promise.race
transitioning javascript builtin
PromiseRace(js-implicit context: Context, receiver: JSAny)(iterable: JSAny):
JSAny {
const receiver = Cast<JSReceiver>(receiver)
otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Promise.race');
// Let promiseCapability be ? NewPromiseCapability(C).
// Don't fire debugEvent so that forwarding the rejection through all does
// not trigger redundant ExceptionEvents
const capability = NewPromiseCapability(receiver, False);
const resolve = capability.resolve;
const reject = capability.reject;
const promise = capability.promise;
// For catch prediction, don't treat the .then calls as handling it;
// instead, recurse outwards.
if (IsDebugActive()) deferred {
SetPropertyStrict(
context, reject, kPromiseForwardingHandlerSymbol, True);
}
try {
// Let iterator be GetIterator(iterable).
// IfAbruptRejectPromise(iterator, promiseCapability).
let i: iterator::IteratorRecord;
try {
i = iterator::GetIterator(iterable);
} catch (e) deferred {
goto Reject(e);
}
// Let result be PerformPromiseRace(iteratorRecord, C, promiseCapability).
try {
// We can skip the "resolve" lookup on {constructor} if it's the
// Promise constructor and the Promise.resolve protector is intact,
// as that guards the lookup path for the "resolve" property on the
// Promise constructor.
const nativeContext = LoadNativeContext(context);
let promiseResolveFunction: JSAny = Undefined;
if (!IsPromiseResolveLookupChainIntact(nativeContext, receiver))
deferred {
// 3. Let _promiseResolve_ be ? Get(_constructor_, `"resolve"`).
const resolve = GetProperty(receiver, kResolveString);
// 4. If IsCallable(_promiseResolve_) is *false*, throw a
// *TypeError* exception.
promiseResolveFunction = Cast<Callable>(resolve)
otherwise ThrowTypeError(
MessageTemplate::kCalledNonCallable, 'resolve');
}
const fastIteratorResultMap = UnsafeCast<Map>(
nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]);
while (true) {
let nextValue: JSAny;
try {
// Let next be IteratorStep(iteratorRecord.[[Iterator]]).
// If next is an abrupt completion, set iteratorRecord.[[Done]] to
// true. ReturnIfAbrupt(next).
const next: JSReceiver = iterator::IteratorStep(
i, fastIteratorResultMap) otherwise return promise;
// Let nextValue be IteratorValue(next).
// If nextValue is an abrupt completion, set iteratorRecord.[[Done]]
// to true.
// ReturnIfAbrupt(nextValue).
nextValue = iterator::IteratorValue(next, fastIteratorResultMap);
} catch (e) {
goto Reject(e);
}
// Let nextPromise be ? Call(constructor, _promiseResolve_, «
// nextValue »).
const nextPromise = CallResolve(
UnsafeCast<Constructor>(receiver), promiseResolveFunction,
nextValue);
// Perform ? Invoke(nextPromise, "then", « resolveElement,
// resultCapability.[[Reject]] »).
const then = GetProperty(nextPromise, kThenString);
const thenResult = Call(
context, then, nextPromise, UnsafeCast<JSAny>(resolve),
UnsafeCast<JSAny>(reject));
// For catch prediction, mark that rejections here are semantically
// handled by the combined Promise.
if (IsDebugActive() && !Is<JSPromise>(promise)) deferred {
SetPropertyStrict(
context, thenResult, kPromiseHandledBySymbol, promise);
}
}
} catch (e) deferred {
iterator::IteratorCloseOnException(i);
goto Reject(e);
}
} label Reject(exception: Object) deferred {
Call(
context, UnsafeCast<JSAny>(reject), Undefined,
UnsafeCast<JSAny>(exception));
return promise;
}
unreachable;
}
macro IsPromiseResolveLookupChainIntact(implicit context: Context)(
nativeContext: NativeContext, constructor: JSReceiver): bool {
if (IsForceSlowPath()) return false;
const promiseFun = UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
return promiseFun == constructor && !IsPromiseResolveProtectorCellInvalid();
}
// https://tc39.es/ecma262/#sec-promise.race
transitioning javascript builtin
PromiseRace(
js-implicit context: Context, receiver: JSAny)(iterable: JSAny): JSAny {
const receiver = Cast<JSReceiver>(receiver)
otherwise ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'Promise.race');
// Let promiseCapability be ? NewPromiseCapability(C).
// Don't fire debugEvent so that forwarding the rejection through all does
// not trigger redundant ExceptionEvents
const capability = NewPromiseCapability(receiver, False);
const resolve = capability.resolve;
const reject = capability.reject;
const promise = capability.promise;
// For catch prediction, don't treat the .then calls as handling it;
// instead, recurse outwards.
if (IsDebugActive()) deferred {
SetPropertyStrict(context, reject, kPromiseForwardingHandlerSymbol, True);
}
try {
// Let iterator be GetIterator(iterable).
// IfAbruptRejectPromise(iterator, promiseCapability).
let i: iterator::IteratorRecord;
try {
i = iterator::GetIterator(iterable);
} catch (e) deferred {
goto Reject(e);
}
// Let result be PerformPromiseRace(iteratorRecord, C, promiseCapability).
try {
// We can skip the "resolve" lookup on {constructor} if it's the
// Promise constructor and the Promise.resolve protector is intact,
// as that guards the lookup path for the "resolve" property on the
// Promise constructor.
const nativeContext = LoadNativeContext(context);
let promiseResolveFunction: JSAny = Undefined;
if (!IsPromiseResolveLookupChainIntact(nativeContext, receiver))
deferred {
// 3. Let _promiseResolve_ be ? Get(_constructor_, `"resolve"`).
const resolve = GetProperty(receiver, kResolveString);
// 4. If IsCallable(_promiseResolve_) is *false*, throw a
// *TypeError* exception.
promiseResolveFunction = Cast<Callable>(resolve)
otherwise ThrowTypeError(
MessageTemplate::kCalledNonCallable, 'resolve');
}
const fastIteratorResultMap = UnsafeCast<Map>(
nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]);
while (true) {
let nextValue: JSAny;
try {
// Let next be IteratorStep(iteratorRecord.[[Iterator]]).
// If next is an abrupt completion, set iteratorRecord.[[Done]] to
// true. ReturnIfAbrupt(next).
const next: JSReceiver = iterator::IteratorStep(
i, fastIteratorResultMap) otherwise return promise;
// Let nextValue be IteratorValue(next).
// If nextValue is an abrupt completion, set iteratorRecord.[[Done]]
// to true.
// ReturnIfAbrupt(nextValue).
nextValue = iterator::IteratorValue(next, fastIteratorResultMap);
} catch (e) {
goto Reject(e);
}
// Let nextPromise be ? Call(constructor, _promiseResolve_, «
// nextValue »).
const nextPromise = CallResolve(
UnsafeCast<Constructor>(receiver), promiseResolveFunction,
nextValue);
// Perform ? Invoke(nextPromise, "then", « resolveElement,
// resultCapability.[[Reject]] »).
const then = GetProperty(nextPromise, kThenString);
const thenResult = Call(
context, then, nextPromise, UnsafeCast<JSAny>(resolve),
UnsafeCast<JSAny>(reject));
// For catch prediction, mark that rejections here are semantically
// handled by the combined Promise.
if (IsDebugActive() && !Is<JSPromise>(promise)) deferred {
SetPropertyStrict(
context, thenResult, kPromiseHandledBySymbol, promise);
}
}
} catch (e) deferred {
iterator::IteratorCloseOnException(i);
goto Reject(e);
}
} label Reject(exception: Object) deferred {
Call(
context, UnsafeCast<JSAny>(reject), Undefined,
UnsafeCast<JSAny>(exception));
return promise;
}
unreachable;
}
}

View File

@ -6,118 +6,118 @@
namespace promise {
transitioning
macro RejectPromiseReactionJob(
context: Context,
promiseOrCapability: JSPromise|PromiseCapability|Undefined, reason: JSAny,
reactionType: constexpr PromiseReactionType): JSAny {
if constexpr (reactionType == kPromiseReactionReject) {
typeswitch (promiseOrCapability) {
case (promise: JSPromise): {
// For fast native promises we can skip the indirection via the
// promiseCapability.[[Reject]] function and run the resolve logic
// directly from here.
return RejectPromise(promise, reason, False);
}
case (Undefined): {
return Undefined;
}
case (capability: PromiseCapability): {
// In the general case we need to call the (user provided)
// promiseCapability.[[Reject]] function.
const reject = UnsafeCast<Callable>(capability.reject);
return Call(context, reject, Undefined, reason);
}
}
} else {
StaticAssert(reactionType == kPromiseReactionFulfill);
// We have to call out to the dedicated PromiseRejectReactionJob
// builtin here, instead of just doing the work inline, as otherwise
// the catch predictions in the debugger will be wrong, which just
// walks the stack and checks for certain builtins.
return PromiseRejectReactionJob(reason, Undefined, promiseOrCapability);
}
}
transitioning
macro FuflfillPromiseReactionJob(
context: Context,
promiseOrCapability: JSPromise|PromiseCapability|Undefined, result: JSAny,
reactionType: constexpr PromiseReactionType): JSAny {
transitioning
macro RejectPromiseReactionJob(
context: Context,
promiseOrCapability: JSPromise|PromiseCapability|Undefined, reason: JSAny,
reactionType: constexpr PromiseReactionType): JSAny {
if constexpr (reactionType == kPromiseReactionReject) {
typeswitch (promiseOrCapability) {
case (promise: JSPromise): {
// For fast native promises we can skip the indirection via the
// promiseCapability.[[Resolve]] function and run the resolve logic
// promiseCapability.[[Reject]] function and run the resolve logic
// directly from here.
return ResolvePromise(context, promise, result);
return RejectPromise(promise, reason, False);
}
case (Undefined): {
return Undefined;
}
case (capability: PromiseCapability): {
// In the general case we need to call the (user provided)
// promiseCapability.[[Resolve]] function.
const resolve = UnsafeCast<Callable>(capability.resolve);
try {
return Call(context, resolve, Undefined, result);
} catch (e) {
return RejectPromiseReactionJob(
context, promiseOrCapability, e, reactionType);
}
// promiseCapability.[[Reject]] function.
const reject = UnsafeCast<Callable>(capability.reject);
return Call(context, reject, Undefined, reason);
}
}
} else {
StaticAssert(reactionType == kPromiseReactionFulfill);
// We have to call out to the dedicated PromiseRejectReactionJob
// builtin here, instead of just doing the work inline, as otherwise
// the catch predictions in the debugger will be wrong, which just
// walks the stack and checks for certain builtins.
return PromiseRejectReactionJob(reason, Undefined, promiseOrCapability);
}
}
// https://tc39.es/ecma262/#sec-promisereactionjob
transitioning
macro PromiseReactionJob(
context: Context, argument: JSAny, handler: Callable|Undefined,
promiseOrCapability: JSPromise|PromiseCapability|Undefined,
reactionType: constexpr PromiseReactionType): JSAny {
if (handler == Undefined) {
if constexpr (reactionType == kPromiseReactionFulfill) {
return FuflfillPromiseReactionJob(
context, promiseOrCapability, argument, reactionType);
} else {
StaticAssert(reactionType == kPromiseReactionReject);
return RejectPromiseReactionJob(
context, promiseOrCapability, argument, reactionType);
}
} else {
transitioning
macro FuflfillPromiseReactionJob(
context: Context,
promiseOrCapability: JSPromise|PromiseCapability|Undefined, result: JSAny,
reactionType: constexpr PromiseReactionType): JSAny {
typeswitch (promiseOrCapability) {
case (promise: JSPromise): {
// For fast native promises we can skip the indirection via the
// promiseCapability.[[Resolve]] function and run the resolve logic
// directly from here.
return ResolvePromise(context, promise, result);
}
case (Undefined): {
return Undefined;
}
case (capability: PromiseCapability): {
// In the general case we need to call the (user provided)
// promiseCapability.[[Resolve]] function.
const resolve = UnsafeCast<Callable>(capability.resolve);
try {
const result =
Call(context, UnsafeCast<Callable>(handler), Undefined, argument);
if (promiseOrCapability == Undefined) {
// There's no [[Capability]] for this promise reaction job, which
// means that this is a specification-internal operation (aka
// await) where the result does not matter (see the specification
// change in https://github.com/tc39/ecma262/pull/1146 for
// details).
return Undefined;
} else {
return FuflfillPromiseReactionJob(
context, promiseOrCapability, result, reactionType);
}
return Call(context, resolve, Undefined, result);
} catch (e) {
return RejectPromiseReactionJob(
context, promiseOrCapability, e, reactionType);
}
}
}
}
transitioning builtin
PromiseFulfillReactionJob(implicit context: Context)(
value: JSAny, handler: Callable|Undefined,
promiseOrCapability: JSPromise|PromiseCapability|Undefined): JSAny {
return PromiseReactionJob(
context, value, handler, promiseOrCapability, kPromiseReactionFulfill);
}
transitioning builtin
PromiseRejectReactionJob(implicit context: Context)(
reason: JSAny, handler: Callable|Undefined,
promiseOrCapability: JSPromise|PromiseCapability|Undefined): JSAny {
return PromiseReactionJob(
context, reason, handler, promiseOrCapability, kPromiseReactionReject);
// https://tc39.es/ecma262/#sec-promisereactionjob
transitioning
macro PromiseReactionJob(
context: Context, argument: JSAny, handler: Callable|Undefined,
promiseOrCapability: JSPromise|PromiseCapability|Undefined,
reactionType: constexpr PromiseReactionType): JSAny {
if (handler == Undefined) {
if constexpr (reactionType == kPromiseReactionFulfill) {
return FuflfillPromiseReactionJob(
context, promiseOrCapability, argument, reactionType);
} else {
StaticAssert(reactionType == kPromiseReactionReject);
return RejectPromiseReactionJob(
context, promiseOrCapability, argument, reactionType);
}
} else {
try {
const result =
Call(context, UnsafeCast<Callable>(handler), Undefined, argument);
if (promiseOrCapability == Undefined) {
// There's no [[Capability]] for this promise reaction job, which
// means that this is a specification-internal operation (aka
// await) where the result does not matter (see the specification
// change in https://github.com/tc39/ecma262/pull/1146 for
// details).
return Undefined;
} else {
return FuflfillPromiseReactionJob(
context, promiseOrCapability, result, reactionType);
}
} catch (e) {
return RejectPromiseReactionJob(
context, promiseOrCapability, e, reactionType);
}
}
}
transitioning builtin
PromiseFulfillReactionJob(implicit context: Context)(
value: JSAny, handler: Callable|Undefined,
promiseOrCapability: JSPromise|PromiseCapability|Undefined): JSAny {
return PromiseReactionJob(
context, value, handler, promiseOrCapability, kPromiseReactionFulfill);
}
transitioning builtin
PromiseRejectReactionJob(implicit context: Context)(
reason: JSAny, handler: Callable|Undefined,
promiseOrCapability: JSPromise|PromiseCapability|Undefined): JSAny {
return PromiseReactionJob(
context, reason, handler, promiseOrCapability, kPromiseReactionReject);
}
}

View File

@ -5,180 +5,180 @@
#include 'src/builtins/builtins-promise-gen.h'
namespace runtime {
extern transitioning runtime
ResolvePromise(implicit context: Context)(JSPromise, JSAny): JSAny;
extern transitioning runtime
ResolvePromise(implicit context: Context)(JSPromise, JSAny): JSAny;
}
namespace promise {
extern macro ConstructorStringConstant(): String;
const kConstructorString: String = ConstructorStringConstant();
extern macro ConstructorStringConstant(): String;
const kConstructorString: String = ConstructorStringConstant();
// https://tc39.es/ecma262/#sec-promise.resolve
transitioning javascript builtin
PromiseResolveTrampoline(js-implicit context: NativeContext, receiver: JSAny)(
value: JSAny): JSAny {
// 1. Let C be the this value.
// 2. If Type(C) is not Object, throw a TypeError exception.
const receiver = Cast<JSReceiver>(receiver) otherwise
ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'PromiseResolve');
// https://tc39.es/ecma262/#sec-promise.resolve
transitioning javascript builtin
PromiseResolveTrampoline(
js-implicit context: NativeContext, receiver: JSAny)(value: JSAny): JSAny {
// 1. Let C be the this value.
// 2. If Type(C) is not Object, throw a TypeError exception.
const receiver = Cast<JSReceiver>(receiver) otherwise
ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'PromiseResolve');
// 3. Return ? PromiseResolve(C, x).
return PromiseResolve(receiver, value);
}
// 3. Return ? PromiseResolve(C, x).
return PromiseResolve(receiver, value);
}
transitioning builtin
PromiseResolve(implicit context:
Context)(constructor: JSReceiver, value: JSAny): JSAny {
const nativeContext = LoadNativeContext(context);
const promiseFun = nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX];
try {
// Check if {value} is a JSPromise.
const value = Cast<JSPromise>(value) otherwise NeedToAllocate;
transitioning builtin
PromiseResolve(implicit context: Context)(
constructor: JSReceiver, value: JSAny): JSAny {
const nativeContext = LoadNativeContext(context);
const promiseFun = nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX];
try {
// Check if {value} is a JSPromise.
const value = Cast<JSPromise>(value) otherwise NeedToAllocate;
// We can skip the "constructor" lookup on {value} if it's [[Prototype]]
// is the (initial) Promise.prototype and the @@species protector is
// intact, as that guards the lookup path for "constructor" on
// JSPromise instances which have the (initial) Promise.prototype.
const promisePrototype =
nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX];
if (value.map.prototype != promisePrototype) {
goto SlowConstructor;
}
if (IsPromiseSpeciesProtectorCellInvalid()) goto SlowConstructor;
// If the {constructor} is the Promise function, we just immediately
// return the {value} here and don't bother wrapping it into a
// native Promise.
if (promiseFun != constructor) goto SlowConstructor;
return value;
} label SlowConstructor deferred {
// At this point, value or/and constructor are not native promises, but
// they could be of the same subclass.
const valueConstructor = GetProperty(value, kConstructorString);
if (valueConstructor != constructor) goto NeedToAllocate;
return value;
} label NeedToAllocate {
if (promiseFun == constructor) {
// This adds a fast path for native promises that don't need to
// create NewPromiseCapability.
const result = NewJSPromise();
ResolvePromise(context, result, value);
return result;
} else
deferred {
const capability = NewPromiseCapability(constructor, True);
const resolve = UnsafeCast<Callable>(capability.resolve);
Call(context, resolve, Undefined, value);
return capability.promise;
}
// We can skip the "constructor" lookup on {value} if it's [[Prototype]]
// is the (initial) Promise.prototype and the @@species protector is
// intact, as that guards the lookup path for "constructor" on
// JSPromise instances which have the (initial) Promise.prototype.
const promisePrototype =
nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX];
if (value.map.prototype != promisePrototype) {
goto SlowConstructor;
}
}
extern macro IsJSReceiverMap(Map): bool;
if (IsPromiseSpeciesProtectorCellInvalid()) goto SlowConstructor;
extern macro IsPromiseThenProtectorCellInvalid(): bool;
extern macro ThenStringConstant(): String;
const kThenString: String = ThenStringConstant();
// https://tc39.es/ecma262/#sec-promise-resolve-functions
transitioning builtin
ResolvePromise(implicit context:
Context)(promise: JSPromise, resolution: JSAny): JSAny {
// 7. If SameValue(resolution, promise) is true, then
// If promise hook is enabled or the debugger is active, let
// the runtime handle this operation, which greatly reduces
// the complexity here and also avoids a couple of back and
// forth between JavaScript and C++ land.
// We also let the runtime handle it if promise == resolution.
// We can use pointer comparison here, since the {promise} is guaranteed
// to be a JSPromise inside this function and thus is reference comparable.
if (IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() ||
TaggedEqual(promise, resolution))
// If the {constructor} is the Promise function, we just immediately
// return the {value} here and don't bother wrapping it into a
// native Promise.
if (promiseFun != constructor) goto SlowConstructor;
return value;
} label SlowConstructor deferred {
// At this point, value or/and constructor are not native promises, but
// they could be of the same subclass.
const valueConstructor = GetProperty(value, kConstructorString);
if (valueConstructor != constructor) goto NeedToAllocate;
return value;
} label NeedToAllocate {
if (promiseFun == constructor) {
// This adds a fast path for native promises that don't need to
// create NewPromiseCapability.
const result = NewJSPromise();
ResolvePromise(context, result, value);
return result;
} else
deferred {
return runtime::ResolvePromise(promise, resolution);
const capability = NewPromiseCapability(constructor, True);
const resolve = UnsafeCast<Callable>(capability.resolve);
Call(context, resolve, Undefined, value);
return capability.promise;
}
let then: Object = Undefined;
try {
// 8. If Type(resolution) is not Object, then
// 8.a Return FulfillPromise(promise, resolution).
if (TaggedIsSmi(resolution)) {
return FulfillPromise(promise, resolution);
}
const heapResolution = UnsafeCast<HeapObject>(resolution);
const resolutionMap = heapResolution.map;
if (!IsJSReceiverMap(resolutionMap)) {
return FulfillPromise(promise, resolution);
}
// We can skip the "then" lookup on {resolution} if its [[Prototype]]
// is the (initial) Promise.prototype and the Promise#then protector
// is intact, as that guards the lookup path for the "then" property
// on JSPromise instances which have the (initial) %PromisePrototype%.
if (IsForceSlowPath()) {
goto Slow;
}
if (IsPromiseThenProtectorCellInvalid()) {
goto Slow;
}
const nativeContext = LoadNativeContext(context);
if (!IsJSPromiseMap(resolutionMap)) {
// We can skip the lookup of "then" if the {resolution} is a (newly
// created) IterResultObject, as the Promise#then() protector also
// ensures that the intrinsic %ObjectPrototype% doesn't contain any
// "then" property. This helps to avoid negative lookups on iterator
// results from async generators.
assert(IsJSReceiverMap(resolutionMap));
assert(!IsPromiseThenProtectorCellInvalid());
if (resolutionMap ==
nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]) {
return FulfillPromise(promise, resolution);
} else {
goto Slow;
}
}
const promisePrototype =
nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX];
if (resolutionMap.prototype == promisePrototype) {
// The {resolution} is a native Promise in this case.
then = nativeContext[NativeContextSlot::PROMISE_THEN_INDEX];
goto Enqueue;
}
goto Slow;
} label Slow deferred {
// 9. Let then be Get(resolution, "then").
// 10. If then is an abrupt completion, then
try {
then = GetProperty(resolution, kThenString);
} catch (e) {
// a. Return RejectPromise(promise, then.[[Value]]).
return RejectPromise(promise, e, False);
}
// 11. Let thenAction be then.[[Value]].
// 12. If IsCallable(thenAction) is false, then
if (!Is<Callable>(then)) {
// a. Return FulfillPromise(promise, resolution).
return FulfillPromise(promise, resolution);
}
goto Enqueue;
} label Enqueue {
// 13. Let job be NewPromiseResolveThenableJob(promise, resolution,
// thenAction).
const task = NewPromiseResolveThenableJobTask(
promise, UnsafeCast<JSReceiver>(resolution),
UnsafeCast<Callable>(then));
// 14. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
// 15. Return undefined.
return EnqueueMicrotask(task.context, task);
}
}
}
extern macro IsJSReceiverMap(Map): bool;
extern macro IsPromiseThenProtectorCellInvalid(): bool;
extern macro ThenStringConstant(): String;
const kThenString: String = ThenStringConstant();
// https://tc39.es/ecma262/#sec-promise-resolve-functions
transitioning builtin
ResolvePromise(implicit context: Context)(
promise: JSPromise, resolution: JSAny): JSAny {
// 7. If SameValue(resolution, promise) is true, then
// If promise hook is enabled or the debugger is active, let
// the runtime handle this operation, which greatly reduces
// the complexity here and also avoids a couple of back and
// forth between JavaScript and C++ land.
// We also let the runtime handle it if promise == resolution.
// We can use pointer comparison here, since the {promise} is guaranteed
// to be a JSPromise inside this function and thus is reference comparable.
if (IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() ||
TaggedEqual(promise, resolution))
deferred {
return runtime::ResolvePromise(promise, resolution);
}
let then: Object = Undefined;
try {
// 8. If Type(resolution) is not Object, then
// 8.a Return FulfillPromise(promise, resolution).
if (TaggedIsSmi(resolution)) {
return FulfillPromise(promise, resolution);
}
const heapResolution = UnsafeCast<HeapObject>(resolution);
const resolutionMap = heapResolution.map;
if (!IsJSReceiverMap(resolutionMap)) {
return FulfillPromise(promise, resolution);
}
// We can skip the "then" lookup on {resolution} if its [[Prototype]]
// is the (initial) Promise.prototype and the Promise#then protector
// is intact, as that guards the lookup path for the "then" property
// on JSPromise instances which have the (initial) %PromisePrototype%.
if (IsForceSlowPath()) {
goto Slow;
}
if (IsPromiseThenProtectorCellInvalid()) {
goto Slow;
}
const nativeContext = LoadNativeContext(context);
if (!IsJSPromiseMap(resolutionMap)) {
// We can skip the lookup of "then" if the {resolution} is a (newly
// created) IterResultObject, as the Promise#then() protector also
// ensures that the intrinsic %ObjectPrototype% doesn't contain any
// "then" property. This helps to avoid negative lookups on iterator
// results from async generators.
assert(IsJSReceiverMap(resolutionMap));
assert(!IsPromiseThenProtectorCellInvalid());
if (resolutionMap ==
nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]) {
return FulfillPromise(promise, resolution);
} else {
goto Slow;
}
}
const promisePrototype =
nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX];
if (resolutionMap.prototype == promisePrototype) {
// The {resolution} is a native Promise in this case.
then = nativeContext[NativeContextSlot::PROMISE_THEN_INDEX];
goto Enqueue;
}
goto Slow;
} label Slow deferred {
// 9. Let then be Get(resolution, "then").
// 10. If then is an abrupt completion, then
try {
then = GetProperty(resolution, kThenString);
} catch (e) {
// a. Return RejectPromise(promise, then.[[Value]]).
return RejectPromise(promise, e, False);
}
// 11. Let thenAction be then.[[Value]].
// 12. If IsCallable(thenAction) is false, then
if (!Is<Callable>(then)) {
// a. Return FulfillPromise(promise, resolution).
return FulfillPromise(promise, resolution);
}
goto Enqueue;
} label Enqueue {
// 13. Let job be NewPromiseResolveThenableJob(promise, resolution,
// thenAction).
const task = NewPromiseResolveThenableJobTask(
promise, UnsafeCast<JSReceiver>(resolution),
UnsafeCast<Callable>(then));
// 14. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
// 15. Return undefined.
return EnqueueMicrotask(task.context, task);
}
}
}

View File

@ -6,69 +6,69 @@
namespace promise {
macro
IsPromiseSpeciesLookupChainIntact(
nativeContext: NativeContext, promiseMap: Map): bool {
const promisePrototype =
nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX];
if (IsForceSlowPath()) return false;
if (promiseMap.prototype != promisePrototype) return false;
return !IsPromiseSpeciesProtectorCellInvalid();
}
macro
IsPromiseSpeciesLookupChainIntact(
nativeContext: NativeContext, promiseMap: Map): bool {
const promisePrototype =
nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX];
if (IsForceSlowPath()) return false;
if (promiseMap.prototype != promisePrototype) return false;
return !IsPromiseSpeciesProtectorCellInvalid();
}
// https://tc39.es/ecma262/#sec-promise.prototype.then
transitioning javascript builtin
PromisePrototypeThen(js-implicit context: NativeContext, receiver: JSAny)(
onFulfilled: JSAny, onRejected: JSAny): JSAny {
// 1. Let promise be the this value.
// 2. If IsPromise(promise) is false, throw a TypeError exception.
const promise = Cast<JSPromise>(receiver) otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, 'Promise.prototype.then',
receiver);
// https://tc39.es/ecma262/#sec-promise.prototype.then
transitioning javascript builtin
PromisePrototypeThen(js-implicit context: NativeContext, receiver: JSAny)(
onFulfilled: JSAny, onRejected: JSAny): JSAny {
// 1. Let promise be the this value.
// 2. If IsPromise(promise) is false, throw a TypeError exception.
const promise = Cast<JSPromise>(receiver) otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, 'Promise.prototype.then',
receiver);
// 3. Let C be ? SpeciesConstructor(promise, %Promise%).
const promiseFun = UnsafeCast<JSFunction>(
context[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
// 3. Let C be ? SpeciesConstructor(promise, %Promise%).
const promiseFun = UnsafeCast<JSFunction>(
context[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
// 4. Let resultCapability be ? NewPromiseCapability(C).
let resultPromiseOrCapability: JSPromise|PromiseCapability;
let resultPromise: JSAny;
try {
if (IsPromiseSpeciesLookupChainIntact(context, promise.map)) {
goto AllocateAndInit;
}
const constructor = SpeciesConstructor(promise, promiseFun);
if (TaggedEqual(constructor, promiseFun)) {
goto AllocateAndInit;
} else {
const promiseCapability = NewPromiseCapability(constructor, True);
resultPromiseOrCapability = promiseCapability;
resultPromise = promiseCapability.promise;
}
} label AllocateAndInit {
const resultJSPromise = NewJSPromise(promise);
resultPromiseOrCapability = resultJSPromise;
resultPromise = resultJSPromise;
// 4. Let resultCapability be ? NewPromiseCapability(C).
let resultPromiseOrCapability: JSPromise|PromiseCapability;
let resultPromise: JSAny;
try {
if (IsPromiseSpeciesLookupChainIntact(context, promise.map)) {
goto AllocateAndInit;
}
// We do some work of the PerformPromiseThen operation here, in that
// we check the handlers and turn non-callable handlers into undefined.
// This is because this is the one and only callsite of PerformPromiseThen
// that has to do this.
// 3. If IsCallable(onFulfilled) is false, then
// a. Set onFulfilled to undefined.
const onFulfilled = CastOrDefault<Callable>(onFulfilled, Undefined);
// 4. If IsCallable(onRejected) is false, then
// a. Set onRejected to undefined.
const onRejected = CastOrDefault<Callable>(onRejected, Undefined);
// 5. Return PerformPromiseThen(promise, onFulfilled, onRejected,
// resultCapability).
PerformPromiseThenImpl(
promise, onFulfilled, onRejected, resultPromiseOrCapability);
return resultPromise;
const constructor = SpeciesConstructor(promise, promiseFun);
if (TaggedEqual(constructor, promiseFun)) {
goto AllocateAndInit;
} else {
const promiseCapability = NewPromiseCapability(constructor, True);
resultPromiseOrCapability = promiseCapability;
resultPromise = promiseCapability.promise;
}
} label AllocateAndInit {
const resultJSPromise = NewJSPromise(promise);
resultPromiseOrCapability = resultJSPromise;
resultPromise = resultJSPromise;
}
// We do some work of the PerformPromiseThen operation here, in that
// we check the handlers and turn non-callable handlers into undefined.
// This is because this is the one and only callsite of PerformPromiseThen
// that has to do this.
// 3. If IsCallable(onFulfilled) is false, then
// a. Set onFulfilled to undefined.
const onFulfilled = CastOrDefault<Callable>(onFulfilled, Undefined);
// 4. If IsCallable(onRejected) is false, then
// a. Set onRejected to undefined.
const onRejected = CastOrDefault<Callable>(onRejected, Undefined);
// 5. Return PerformPromiseThen(promise, onFulfilled, onRejected,
// resultCapability).
PerformPromiseThenImpl(
promise, onFulfilled, onRejected, resultPromiseOrCapability);
return resultPromise;
}
}

View File

@ -6,40 +6,40 @@
namespace proxy {
// ES #sec-proxy-constructor
// https://tc39.github.io/ecma262/#sec-proxy-constructor
transitioning javascript builtin
ProxyConstructor(
js-implicit context: NativeContext, receiver: JSAny,
newTarget: JSAny)(target: JSAny, handler: JSAny): JSProxy {
try {
// 1. If NewTarget is undefined, throw a TypeError exception.
if (newTarget == Undefined) {
ThrowTypeError(MessageTemplate::kConstructorNotFunction, 'Proxy');
}
// 2. Return ? ProxyCreate(target, handler).
// https://tc39.github.io/ecma262/#sec-proxycreate
// 1. If Type(target) is not Object, throw a TypeError exception.
// 2. If Type(handler) is not Object, throw a TypeError exception.
const targetJSReceiver =
Cast<JSReceiver>(target) otherwise ThrowProxyNonObject;
const handlerJSReceiver =
Cast<JSReceiver>(handler) otherwise ThrowProxyNonObject;
// 5. Let P be a newly created object.
// 6. Set P's essential internal methods (except for [[Call]] and
// [[Construct]]) to the definitions specified in 9.5.
// 7. If IsCallable(target) is true, then
// a. Set P.[[Call]] as specified in 9.5.12.
// b. If IsConstructor(target) is true, then
// 1. Set P.[[Construct]] as specified in 9.5.13.
// 8. Set P.[[ProxyTarget]] to target.
// 9. Set P.[[ProxyHandler]] to handler.
// 10. Return P.
return AllocateProxy(targetJSReceiver, handlerJSReceiver);
} label ThrowProxyNonObject deferred {
ThrowTypeError(MessageTemplate::kProxyNonObject);
// ES #sec-proxy-constructor
// https://tc39.github.io/ecma262/#sec-proxy-constructor
transitioning javascript builtin
ProxyConstructor(
js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny)(
target: JSAny, handler: JSAny): JSProxy {
try {
// 1. If NewTarget is undefined, throw a TypeError exception.
if (newTarget == Undefined) {
ThrowTypeError(MessageTemplate::kConstructorNotFunction, 'Proxy');
}
// 2. Return ? ProxyCreate(target, handler).
// https://tc39.github.io/ecma262/#sec-proxycreate
// 1. If Type(target) is not Object, throw a TypeError exception.
// 2. If Type(handler) is not Object, throw a TypeError exception.
const targetJSReceiver =
Cast<JSReceiver>(target) otherwise ThrowProxyNonObject;
const handlerJSReceiver =
Cast<JSReceiver>(handler) otherwise ThrowProxyNonObject;
// 5. Let P be a newly created object.
// 6. Set P's essential internal methods (except for [[Call]] and
// [[Construct]]) to the definitions specified in 9.5.
// 7. If IsCallable(target) is true, then
// a. Set P.[[Call]] as specified in 9.5.12.
// b. If IsConstructor(target) is true, then
// 1. Set P.[[Construct]] as specified in 9.5.13.
// 8. Set P.[[ProxyTarget]] to target.
// 9. Set P.[[ProxyHandler]] to handler.
// 10. Return P.
return AllocateProxy(targetJSReceiver, handlerJSReceiver);
} label ThrowProxyNonObject deferred {
ThrowTypeError(MessageTemplate::kProxyNonObject);
}
}
}

View File

@ -6,64 +6,64 @@
namespace proxy {
// ES #sec-proxy-object-internal-methods-and-internal-slots-delete-p
// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-delete-p
transitioning builtin
ProxyDeleteProperty(implicit context: Context)(
proxy: JSProxy, name: PropertyKey, languageMode: LanguageModeSmi): JSAny {
const kTrapName: constexpr string = 'deleteProperty';
// Handle deeply nested proxy.
PerformStackCheck();
// 1. Assert: IsPropertyKey(P) is true.
assert(TaggedIsNotSmi(name));
assert(IsName(name));
assert(!IsPrivateSymbol(name));
// ES #sec-proxy-object-internal-methods-and-internal-slots-delete-p
// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-delete-p
transitioning builtin
ProxyDeleteProperty(implicit context: Context)(
proxy: JSProxy, name: PropertyKey, languageMode: LanguageModeSmi): JSAny {
const kTrapName: constexpr string = 'deleteProperty';
// Handle deeply nested proxy.
PerformStackCheck();
// 1. Assert: IsPropertyKey(P) is true.
assert(TaggedIsNotSmi(name));
assert(IsName(name));
assert(!IsPrivateSymbol(name));
try {
// 2. Let handler be O.[[ProxyHandler]].
// 3. If handler is null, throw a TypeError exception.
// 4. Assert: Type(handler) is Object.
assert(proxy.handler == Null || Is<JSReceiver>(proxy.handler));
const handler =
Cast<JSReceiver>(proxy.handler) otherwise ThrowProxyHandlerRevoked;
try {
// 2. Let handler be O.[[ProxyHandler]].
// 3. If handler is null, throw a TypeError exception.
// 4. Assert: Type(handler) is Object.
assert(proxy.handler == Null || Is<JSReceiver>(proxy.handler));
const handler =
Cast<JSReceiver>(proxy.handler) otherwise ThrowProxyHandlerRevoked;
// 5. Let target be O.[[ProxyTarget]].
const target = UnsafeCast<JSReceiver>(proxy.target);
// 5. Let target be O.[[ProxyTarget]].
const target = UnsafeCast<JSReceiver>(proxy.target);
// 6. Let trap be ? GetMethod(handler, "deleteProperty").
// 7. If trap is undefined, then (see 7.a below).
const trap: Callable = GetMethod(handler, kTrapName)
otherwise goto TrapUndefined(target);
// 6. Let trap be ? GetMethod(handler, "deleteProperty").
// 7. If trap is undefined, then (see 7.a below).
const trap: Callable = GetMethod(handler, kTrapName)
otherwise goto TrapUndefined(target);
// 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler,
// « target, P »)).
const trapResult = Call(context, trap, handler, target, name);
// 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler,
// « target, P »)).
const trapResult = Call(context, trap, handler, target, name);
// 9. If booleanTrapResult is false, return false.
if (!ToBoolean(trapResult)) {
const strictValue: LanguageModeSmi = LanguageMode::kStrict;
if (languageMode == strictValue) {
ThrowTypeError(
MessageTemplate::kProxyTrapReturnedFalsishFor, kTrapName, name);
}
return False;
// 9. If booleanTrapResult is false, return false.
if (!ToBoolean(trapResult)) {
const strictValue: LanguageModeSmi = LanguageMode::kStrict;
if (languageMode == strictValue) {
ThrowTypeError(
MessageTemplate::kProxyTrapReturnedFalsishFor, kTrapName, name);
}
// 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
// 11. If targetDesc is undefined, return true.
// 12. If targetDesc.[[Configurable]] is false, throw a TypeError
// exception.
// 13. Let extensibleTarget be ? IsExtensible(target).
// 14. If extensibleTarget is false, throw a TypeError exception.
CheckDeleteTrapResult(target, proxy, name);
// 15. Return true.
return True;
} label TrapUndefined(target: JSAny) {
// 7.a. Return ? target.[[Delete]](P).
return DeleteProperty(target, name, languageMode);
} label ThrowProxyHandlerRevoked deferred {
ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName);
return False;
}
// 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
// 11. If targetDesc is undefined, return true.
// 12. If targetDesc.[[Configurable]] is false, throw a TypeError
// exception.
// 13. Let extensibleTarget be ? IsExtensible(target).
// 14. If extensibleTarget is false, throw a TypeError exception.
CheckDeleteTrapResult(target, proxy, name);
// 15. Return true.
return True;
} label TrapUndefined(target: JSAny) {
// 7.a. Return ? target.[[Delete]](P).
return DeleteProperty(target, name, languageMode);
} label ThrowProxyHandlerRevoked deferred {
ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName);
}
}
}

View File

@ -6,60 +6,59 @@
namespace proxy {
extern transitioning builtin GetPropertyWithReceiver(
implicit context: Context)(JSAny, Name, JSAny, Smi): JSAny;
extern transitioning builtin GetPropertyWithReceiver(implicit context: Context)(
JSAny, Name, JSAny, Smi): JSAny;
// ES #sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
// https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
transitioning builtin
ProxyGetProperty(implicit context: Context)(
proxy: JSProxy, name: PropertyKey, receiverValue: JSAny,
onNonExistent: Smi): JSAny {
PerformStackCheck();
// 1. Assert: IsPropertyKey(P) is true.
assert(TaggedIsNotSmi(name));
assert(IsName(name));
assert(!IsPrivateSymbol(name));
// ES #sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
// https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
transitioning builtin
ProxyGetProperty(implicit context: Context)(
proxy: JSProxy, name: PropertyKey, receiverValue: JSAny,
onNonExistent: Smi): JSAny {
PerformStackCheck();
// 1. Assert: IsPropertyKey(P) is true.
assert(TaggedIsNotSmi(name));
assert(IsName(name));
assert(!IsPrivateSymbol(name));
// 2. Let handler be O.[[ProxyHandler]].
// 3. If handler is null, throw a TypeError exception.
// 4. Assert: Type(handler) is Object.
let handler: JSReceiver;
typeswitch (proxy.handler) {
case (Null): {
ThrowTypeError(MessageTemplate::kProxyRevoked, 'get');
}
case (h: JSReceiver): {
handler = h;
}
// 2. Let handler be O.[[ProxyHandler]].
// 3. If handler is null, throw a TypeError exception.
// 4. Assert: Type(handler) is Object.
let handler: JSReceiver;
typeswitch (proxy.handler) {
case (Null): {
ThrowTypeError(MessageTemplate::kProxyRevoked, 'get');
}
case (h: JSReceiver): {
handler = h;
}
// 5. Let target be O.[[ProxyTarget]].
const target = Cast<JSReceiver>(proxy.target) otherwise unreachable;
// 6. Let trap be ? GetMethod(handler, "get").
// 7. If trap is undefined, then (see 7.a below).
// 7.a. Return ? target.[[Get]](P, Receiver).
const trap: Callable = GetMethod(handler, 'get')
otherwise return GetPropertyWithReceiver(
target, name, receiverValue, onNonExistent);
// 8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »).
const trapResult =
Call(context, trap, handler, target, name, receiverValue);
// 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
// 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is
// false, then
// a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]]
// is false, then
// i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a
// TypeError exception.
// b. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]]
// is undefined, then
// i. If trapResult is not undefined, throw a TypeError exception.
// 11. Return trapResult.
CheckGetSetTrapResult(target, proxy, name, trapResult, kProxyGet);
return trapResult;
}
// 5. Let target be O.[[ProxyTarget]].
const target = Cast<JSReceiver>(proxy.target) otherwise unreachable;
// 6. Let trap be ? GetMethod(handler, "get").
// 7. If trap is undefined, then (see 7.a below).
// 7.a. Return ? target.[[Get]](P, Receiver).
const trap: Callable = GetMethod(handler, 'get')
otherwise return GetPropertyWithReceiver(
target, name, receiverValue, onNonExistent);
// 8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »).
const trapResult = Call(context, trap, handler, target, name, receiverValue);
// 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
// 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is
// false, then
// a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]]
// is false, then
// i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a
// TypeError exception.
// b. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]]
// is undefined, then
// i. If trapResult is not undefined, throw a TypeError exception.
// 11. Return trapResult.
CheckGetSetTrapResult(target, proxy, name, trapResult, kProxyGet);
return trapResult;
}
}

View File

@ -6,62 +6,62 @@
namespace proxy {
// ES #sec-proxy-object-internal-methods-and-internal-slots-isextensible
// https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-isextensible
transitioning builtin
ProxyGetPrototypeOf(implicit context: Context)(proxy: JSProxy): JSAny {
PerformStackCheck();
const kTrapName: constexpr string = 'getPrototypeOf';
try {
// 1. Let handler be O.[[ProxyHandler]].
// 2. If handler is null, throw a TypeError exception.
// 3. Assert: Type(handler) is Object.
assert(proxy.handler == Null || Is<JSReceiver>(proxy.handler));
const handler =
Cast<JSReceiver>(proxy.handler) otherwise ThrowProxyHandlerRevoked;
// ES #sec-proxy-object-internal-methods-and-internal-slots-isextensible
// https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-isextensible
transitioning builtin
ProxyGetPrototypeOf(implicit context: Context)(proxy: JSProxy): JSAny {
PerformStackCheck();
const kTrapName: constexpr string = 'getPrototypeOf';
try {
// 1. Let handler be O.[[ProxyHandler]].
// 2. If handler is null, throw a TypeError exception.
// 3. Assert: Type(handler) is Object.
assert(proxy.handler == Null || Is<JSReceiver>(proxy.handler));
const handler =
Cast<JSReceiver>(proxy.handler) otherwise ThrowProxyHandlerRevoked;
// 4. Let target be O.[[ProxyTarget]].
const target = proxy.target;
// 4. Let target be O.[[ProxyTarget]].
const target = proxy.target;
// 5. Let trap be ? GetMethod(handler, "getPrototypeOf").
// 6. If trap is undefined, then (see 6.a below).
const trap: Callable = GetMethod(handler, kTrapName)
otherwise goto TrapUndefined(target);
// 5. Let trap be ? GetMethod(handler, "getPrototypeOf").
// 6. If trap is undefined, then (see 6.a below).
const trap: Callable = GetMethod(handler, kTrapName)
otherwise goto TrapUndefined(target);
// 7. Let handlerProto be ? Call(trap, handler, « target »).
const handlerProto = Call(context, trap, handler, target);
// 7. Let handlerProto be ? Call(trap, handler, « target »).
const handlerProto = Call(context, trap, handler, target);
// 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError
// exception.
if (!Is<JSReceiver>(handlerProto) && handlerProto != Null) {
goto ThrowProxyGetPrototypeOfInvalid;
}
// 9. Let extensibleTarget be ? IsExtensible(target).
// 10. If extensibleTarget is true, return handlerProto.
const extensibleTarget: JSAny = object::ObjectIsExtensibleImpl(target);
assert(extensibleTarget == True || extensibleTarget == False);
if (extensibleTarget == True) {
return handlerProto;
}
// 11. Let targetProto be ? target.[[GetPrototypeOf]]().
const targetProto = object::ObjectGetPrototypeOfImpl(target);
// 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError
// exception.
// 13. Return handlerProto.
if (SameValue(targetProto, handlerProto)) {
return handlerProto;
}
ThrowTypeError(MessageTemplate::kProxyGetPrototypeOfNonExtensible);
} label TrapUndefined(target: JSAny) {
// 6.a. Return ? target.[[GetPrototypeOf]]().
return object::ObjectGetPrototypeOfImpl(target);
} label ThrowProxyHandlerRevoked deferred {
ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName);
} label ThrowProxyGetPrototypeOfInvalid deferred {
ThrowTypeError(MessageTemplate::kProxyGetPrototypeOfInvalid);
// 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError
// exception.
if (!Is<JSReceiver>(handlerProto) && handlerProto != Null) {
goto ThrowProxyGetPrototypeOfInvalid;
}
// 9. Let extensibleTarget be ? IsExtensible(target).
// 10. If extensibleTarget is true, return handlerProto.
const extensibleTarget: JSAny = object::ObjectIsExtensibleImpl(target);
assert(extensibleTarget == True || extensibleTarget == False);
if (extensibleTarget == True) {
return handlerProto;
}
// 11. Let targetProto be ? target.[[GetPrototypeOf]]().
const targetProto = object::ObjectGetPrototypeOfImpl(target);
// 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError
// exception.
// 13. Return handlerProto.
if (SameValue(targetProto, handlerProto)) {
return handlerProto;
}
ThrowTypeError(MessageTemplate::kProxyGetPrototypeOfNonExtensible);
} label TrapUndefined(target: JSAny) {
// 6.a. Return ? target.[[GetPrototypeOf]]().
return object::ObjectGetPrototypeOfImpl(target);
} label ThrowProxyHandlerRevoked deferred {
ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName);
} label ThrowProxyGetPrototypeOfInvalid deferred {
ThrowTypeError(MessageTemplate::kProxyGetPrototypeOfInvalid);
}
}
}

View File

@ -6,50 +6,50 @@
namespace proxy {
// ES #sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p
// https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p
transitioning builtin ProxyHasProperty(implicit context: Context)(
proxy: JSProxy, name: PropertyKey): JSAny {
assert(IsJSProxy(proxy));
// ES #sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p
// https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p
transitioning builtin ProxyHasProperty(implicit context: Context)(
proxy: JSProxy, name: PropertyKey): JSAny {
assert(IsJSProxy(proxy));
PerformStackCheck();
PerformStackCheck();
// 1. Assert: IsPropertyKey(P) is true.
assert(IsName(name));
assert(!IsPrivateSymbol(name));
// 1. Assert: IsPropertyKey(P) is true.
assert(IsName(name));
assert(!IsPrivateSymbol(name));
try {
// 2. Let handler be O.[[ProxyHandler]].
// 3. If handler is null, throw a TypeError exception.
// 4. Assert: Type(handler) is Object.
assert(proxy.handler == Null || Is<JSReceiver>(proxy.handler));
const handler =
Cast<JSReceiver>(proxy.handler) otherwise ThrowProxyHandlerRevoked;
try {
// 2. Let handler be O.[[ProxyHandler]].
// 3. If handler is null, throw a TypeError exception.
// 4. Assert: Type(handler) is Object.
assert(proxy.handler == Null || Is<JSReceiver>(proxy.handler));
const handler =
Cast<JSReceiver>(proxy.handler) otherwise ThrowProxyHandlerRevoked;
// 5. Let target be O.[[ProxyTarget]].
const target = Cast<JSReceiver>(proxy.target) otherwise unreachable;
// 5. Let target be O.[[ProxyTarget]].
const target = Cast<JSReceiver>(proxy.target) otherwise unreachable;
// 6. Let trap be ? GetMethod(handler, "has").
// 7. If trap is undefined, then (see 7.a below).
const trap: Callable = GetMethod(handler, 'has')
otherwise goto TrapUndefined(target);
// 6. Let trap be ? GetMethod(handler, "has").
// 7. If trap is undefined, then (see 7.a below).
const trap: Callable = GetMethod(handler, 'has')
otherwise goto TrapUndefined(target);
// 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «
// target»)).
// 9. If booleanTrapResult is false, then (see 9.a. in
// CheckHasTrapResult).
// 10. Return booleanTrapResult.
const trapResult = Call(context, trap, handler, target, name);
if (ToBoolean(trapResult)) {
return True;
}
CheckHasTrapResult(target, proxy, name);
return False;
} label TrapUndefined(target: JSAny) {
// 7.a. Return ? target.[[HasProperty]](P).
tail HasProperty(target, name);
} label ThrowProxyHandlerRevoked deferred {
ThrowTypeError(MessageTemplate::kProxyRevoked, 'has');
// 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «
// target»)).
// 9. If booleanTrapResult is false, then (see 9.a. in
// CheckHasTrapResult).
// 10. Return booleanTrapResult.
const trapResult = Call(context, trap, handler, target, name);
if (ToBoolean(trapResult)) {
return True;
}
CheckHasTrapResult(target, proxy, name);
return False;
} label TrapUndefined(target: JSAny) {
// 7.a. Return ? target.[[HasProperty]](P).
tail HasProperty(target, name);
} label ThrowProxyHandlerRevoked deferred {
ThrowTypeError(MessageTemplate::kProxyRevoked, 'has');
}
}
}

View File

@ -6,50 +6,50 @@
namespace proxy {
// ES #sec-proxy-object-internal-methods-and-internal-slots-isextensible
// https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-isextensible
transitioning builtin ProxyIsExtensible(implicit context:
Context)(proxy: JSProxy): JSAny {
PerformStackCheck();
const kTrapName: constexpr string = 'isExtensible';
try {
// 1. Let handler be O.[[ProxyHandler]].
// 2. If handler is null, throw a TypeError exception.
// 3. Assert: Type(handler) is Object.
assert(proxy.handler == Null || Is<JSReceiver>(proxy.handler));
const handler =
Cast<JSReceiver>(proxy.handler) otherwise ThrowProxyHandlerRevoked;
// ES #sec-proxy-object-internal-methods-and-internal-slots-isextensible
// https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-isextensible
transitioning builtin ProxyIsExtensible(implicit context: Context)(
proxy: JSProxy): JSAny {
PerformStackCheck();
const kTrapName: constexpr string = 'isExtensible';
try {
// 1. Let handler be O.[[ProxyHandler]].
// 2. If handler is null, throw a TypeError exception.
// 3. Assert: Type(handler) is Object.
assert(proxy.handler == Null || Is<JSReceiver>(proxy.handler));
const handler =
Cast<JSReceiver>(proxy.handler) otherwise ThrowProxyHandlerRevoked;
// 4. Let target be O.[[ProxyTarget]].
const target = proxy.target;
// 4. Let target be O.[[ProxyTarget]].
const target = proxy.target;
// 5. Let trap be ? GetMethod(handler, "isExtensible").
// 6. If trap is undefined, then (see 6.a below).
const trap: Callable = GetMethod(handler, kTrapName)
otherwise goto TrapUndefined(target);
// 5. Let trap be ? GetMethod(handler, "isExtensible").
// 6. If trap is undefined, then (see 6.a below).
const trap: Callable = GetMethod(handler, kTrapName)
otherwise goto TrapUndefined(target);
// 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «
// target»)).
const trapResult = ToBoolean(Call(context, trap, handler, target));
// 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «
// target»)).
const trapResult = ToBoolean(Call(context, trap, handler, target));
// 8. Let targetResult be ? IsExtensible(target).
const targetResult: bool =
ToBoolean(object::ObjectIsExtensibleImpl(target));
// 8. Let targetResult be ? IsExtensible(target).
const targetResult: bool =
ToBoolean(object::ObjectIsExtensibleImpl(target));
// 9. If SameValue(booleanTrapResult, targetResult) is false, throw a
// TypeError exception.
if (trapResult != targetResult) {
ThrowTypeError(
MessageTemplate::kProxyIsExtensibleInconsistent,
SelectBooleanConstant(targetResult));
}
// 10. Return booleanTrapResult.
return SelectBooleanConstant(trapResult);
} label TrapUndefined(target: JSAny) {
// 6.a. Return ? IsExtensible(target).
return object::ObjectIsExtensibleImpl(target);
} label ThrowProxyHandlerRevoked deferred {
ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName);
// 9. If SameValue(booleanTrapResult, targetResult) is false, throw a
// TypeError exception.
if (trapResult != targetResult) {
ThrowTypeError(
MessageTemplate::kProxyIsExtensibleInconsistent,
SelectBooleanConstant(targetResult));
}
// 10. Return booleanTrapResult.
return SelectBooleanConstant(trapResult);
} label TrapUndefined(target: JSAny) {
// 6.a. Return ? IsExtensible(target).
return object::ObjectIsExtensibleImpl(target);
} label ThrowProxyHandlerRevoked deferred {
ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName);
}
}
}

View File

@ -6,59 +6,59 @@
namespace proxy {
// ES #sec-proxy-object-internal-methods-and-internal-slots-preventextensions
// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-preventextensions
transitioning builtin
ProxyPreventExtensions(implicit context:
Context)(proxy: JSProxy, doThrow: Boolean): JSAny {
PerformStackCheck();
const kTrapName: constexpr string = 'preventExtensions';
try {
// 1. Let handler be O.[[ProxyHandler]].
// 2. If handler is null, throw a TypeError exception.
// 3. Assert: Type(handler) is Object.
assert(proxy.handler == Null || Is<JSReceiver>(proxy.handler));
const handler =
Cast<JSReceiver>(proxy.handler) otherwise ThrowProxyHandlerRevoked;
// ES #sec-proxy-object-internal-methods-and-internal-slots-preventextensions
// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-preventextensions
transitioning builtin
ProxyPreventExtensions(implicit context: Context)(
proxy: JSProxy, doThrow: Boolean): JSAny {
PerformStackCheck();
const kTrapName: constexpr string = 'preventExtensions';
try {
// 1. Let handler be O.[[ProxyHandler]].
// 2. If handler is null, throw a TypeError exception.
// 3. Assert: Type(handler) is Object.
assert(proxy.handler == Null || Is<JSReceiver>(proxy.handler));
const handler =
Cast<JSReceiver>(proxy.handler) otherwise ThrowProxyHandlerRevoked;
// 4. Let target be O.[[ProxyTarget]].
const target = proxy.target;
// 4. Let target be O.[[ProxyTarget]].
const target = proxy.target;
// 5. Let trap be ? GetMethod(handler, "preventExtensions").
// 6. If trap is undefined, then (see 6.a below).
const trap: Callable = GetMethod(handler, kTrapName)
otherwise goto TrapUndefined(target);
// 5. Let trap be ? GetMethod(handler, "preventExtensions").
// 6. If trap is undefined, then (see 6.a below).
const trap: Callable = GetMethod(handler, kTrapName)
otherwise goto TrapUndefined(target);
// 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «
// target»)).
const trapResult = Call(context, trap, handler, target);
// 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «
// target»)).
const trapResult = Call(context, trap, handler, target);
// 8. If booleanTrapResult is true, then
// 8.a. Let extensibleTarget be ? IsExtensible(target).
// 8.b If extensibleTarget is true, throw a TypeError exception.
if (ToBoolean(trapResult)) {
const extensibleTarget: JSAny = object::ObjectIsExtensibleImpl(target);
assert(extensibleTarget == True || extensibleTarget == False);
if (extensibleTarget == True) {
ThrowTypeError(MessageTemplate::kProxyPreventExtensionsExtensible);
}
} else {
if (doThrow == True) {
ThrowTypeError(MessageTemplate::kProxyTrapReturnedFalsish, kTrapName);
}
return False;
// 8. If booleanTrapResult is true, then
// 8.a. Let extensibleTarget be ? IsExtensible(target).
// 8.b If extensibleTarget is true, throw a TypeError exception.
if (ToBoolean(trapResult)) {
const extensibleTarget: JSAny = object::ObjectIsExtensibleImpl(target);
assert(extensibleTarget == True || extensibleTarget == False);
if (extensibleTarget == True) {
ThrowTypeError(MessageTemplate::kProxyPreventExtensionsExtensible);
}
// 9. Return booleanTrapResult.
return True;
} label TrapUndefined(target: JSAny) {
// 6.a. Return ? target.[[PreventExtensions]]().
} else {
if (doThrow == True) {
return object::ObjectPreventExtensionsThrow(target);
ThrowTypeError(MessageTemplate::kProxyTrapReturnedFalsish, kTrapName);
}
return object::ObjectPreventExtensionsDontThrow(target);
} label ThrowProxyHandlerRevoked deferred {
ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName);
return False;
}
// 9. Return booleanTrapResult.
return True;
} label TrapUndefined(target: JSAny) {
// 6.a. Return ? target.[[PreventExtensions]]().
if (doThrow == True) {
return object::ObjectPreventExtensionsThrow(target);
}
return object::ObjectPreventExtensionsDontThrow(target);
} label ThrowProxyHandlerRevoked deferred {
ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName);
}
}
} // namespace proxy

View File

@ -6,35 +6,35 @@
namespace proxy {
extern macro ProxiesCodeStubAssembler::AllocateProxyRevokeFunction(
implicit context: Context)(JSProxy): JSFunction;
extern macro ProxiesCodeStubAssembler::AllocateProxyRevokeFunction(
implicit context: Context)(JSProxy): JSFunction;
// Proxy.revocable(target, handler)
// https://tc39.github.io/ecma262/#sec-proxy.revocable
transitioning javascript builtin
ProxyRevocable(js-implicit context: NativeContext)(
target: JSAny, handler: JSAny): JSProxyRevocableResult {
try {
// 1. Let p be ? ProxyCreate(target, handler).
const targetJSReceiver =
Cast<JSReceiver>(target) otherwise ThrowProxyNonObject;
const handlerJSReceiver =
Cast<JSReceiver>(handler) otherwise ThrowProxyNonObject;
const proxy: JSProxy = AllocateProxy(targetJSReceiver, handlerJSReceiver);
// Proxy.revocable(target, handler)
// https://tc39.github.io/ecma262/#sec-proxy.revocable
transitioning javascript builtin
ProxyRevocable(js-implicit context: NativeContext)(
target: JSAny, handler: JSAny): JSProxyRevocableResult {
try {
// 1. Let p be ? ProxyCreate(target, handler).
const targetJSReceiver =
Cast<JSReceiver>(target) otherwise ThrowProxyNonObject;
const handlerJSReceiver =
Cast<JSReceiver>(handler) otherwise ThrowProxyNonObject;
const proxy: JSProxy = AllocateProxy(targetJSReceiver, handlerJSReceiver);
// 2. Let steps be the algorithm steps defined in Proxy Revocation
// Functions.
// 3. Let revoker be CreateBuiltinFunction(steps, « [[RevocableProxy]] »).
// 4. Set revoker.[[RevocableProxy]] to p.
const revoke: JSFunction = AllocateProxyRevokeFunction(proxy);
// 2. Let steps be the algorithm steps defined in Proxy Revocation
// Functions.
// 3. Let revoker be CreateBuiltinFunction(steps, « [[RevocableProxy]] »).
// 4. Set revoker.[[RevocableProxy]] to p.
const revoke: JSFunction = AllocateProxyRevokeFunction(proxy);
// 5. Let result be ObjectCreate(%ObjectPrototype%).
// 6. Perform CreateDataProperty(result, "proxy", p).
// 7. Perform CreateDataProperty(result, "revoke", revoker).
// 8. Return result.
return NewJSProxyRevocableResult(proxy, revoke);
} label ThrowProxyNonObject deferred {
ThrowTypeError(MessageTemplate::kProxyNonObject, 'Proxy.revocable');
}
// 5. Let result be ObjectCreate(%ObjectPrototype%).
// 6. Perform CreateDataProperty(result, "proxy", p).
// 7. Perform CreateDataProperty(result, "revoke", revoker).
// 8. Return result.
return NewJSProxyRevocableResult(proxy, revoke);
} label ThrowProxyNonObject deferred {
ThrowTypeError(MessageTemplate::kProxyNonObject, 'Proxy.revocable');
}
}
}

View File

@ -6,31 +6,31 @@
namespace proxy {
// Proxy Revocation Functions
// https://tc39.github.io/ecma262/#sec-proxy-revocation-functions
transitioning javascript builtin
ProxyRevoke(js-implicit context: NativeContext)(): Undefined {
// 1. Let p be F.[[RevocableProxy]].
const proxyObject: Object = context[PROXY_SLOT];
// Proxy Revocation Functions
// https://tc39.github.io/ecma262/#sec-proxy-revocation-functions
transitioning javascript builtin
ProxyRevoke(js-implicit context: NativeContext)(): Undefined {
// 1. Let p be F.[[RevocableProxy]].
const proxyObject: Object = context[PROXY_SLOT];
// 2. If p is null, return undefined
if (proxyObject == Null) {
return Undefined;
}
// 3. Set F.[[RevocableProxy]] to null.
context[PROXY_SLOT] = Null;
// 4. Assert: p is a Proxy object.
const proxy: JSProxy = UnsafeCast<JSProxy>(proxyObject);
// 5. Set p.[[ProxyTarget]] to null.
proxy.target = Null;
// 6. Set p.[[ProxyHandler]] to null.
proxy.handler = Null;
// 7. Return undefined.
// 2. If p is null, return undefined
if (proxyObject == Null) {
return Undefined;
}
// 3. Set F.[[RevocableProxy]] to null.
context[PROXY_SLOT] = Null;
// 4. Assert: p is a Proxy object.
const proxy: JSProxy = UnsafeCast<JSProxy>(proxyObject);
// 5. Set p.[[ProxyTarget]] to null.
proxy.target = Null;
// 6. Set p.[[ProxyHandler]] to null.
proxy.handler = Null;
// 7. Return undefined.
return Undefined;
}
}

View File

@ -6,82 +6,82 @@
namespace proxy {
extern transitioning runtime
SetPropertyWithReceiver(implicit context:
Context)(Object, Name, Object, Object): void;
extern transitioning runtime
SetPropertyWithReceiver(implicit context: Context)(
Object, Name, Object, Object): void;
transitioning macro CallThrowTypeErrorIfStrict(implicit context: Context)(
message: constexpr MessageTemplate) {
ThrowTypeErrorIfStrict(SmiConstant(message), Null, Null);
transitioning macro CallThrowTypeErrorIfStrict(implicit context: Context)(
message: constexpr MessageTemplate) {
ThrowTypeErrorIfStrict(SmiConstant(message), Null, Null);
}
// ES #sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver
// https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver
transitioning builtin
ProxySetProperty(implicit context: Context)(
proxy: JSProxy, name: PropertyKey|PrivateSymbol, value: JSAny,
receiverValue: JSAny): JSAny {
// 1. Assert: IsPropertyKey(P) is true.
assert(TaggedIsNotSmi(name));
assert(IsName(name));
let key: PropertyKey;
typeswitch (name) {
case (PrivateSymbol): {
CallThrowTypeErrorIfStrict(MessageTemplate::kProxyPrivate);
return Undefined;
}
case (name: PropertyKey): {
key = name;
}
}
// ES #sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver
// https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver
transitioning builtin
ProxySetProperty(implicit context: Context)(
proxy: JSProxy, name: PropertyKey|PrivateSymbol, value: JSAny,
receiverValue: JSAny): JSAny {
// 1. Assert: IsPropertyKey(P) is true.
assert(TaggedIsNotSmi(name));
assert(IsName(name));
try {
// 2. Let handler be O.[[ProxyHandler]].
// 3. If handler is null, throw a TypeError exception.
// 4. Assert: Type(handler) is Object.
assert(proxy.handler == Null || Is<JSReceiver>(proxy.handler));
const handler =
Cast<JSReceiver>(proxy.handler) otherwise ThrowProxyHandlerRevoked;
let key: PropertyKey;
typeswitch (name) {
case (PrivateSymbol): {
CallThrowTypeErrorIfStrict(MessageTemplate::kProxyPrivate);
return Undefined;
}
case (name: PropertyKey): {
key = name;
}
}
// 5. Let target be O.[[ProxyTarget]].
const target = UnsafeCast<JSReceiver>(proxy.target);
try {
// 2. Let handler be O.[[ProxyHandler]].
// 3. If handler is null, throw a TypeError exception.
// 4. Assert: Type(handler) is Object.
assert(proxy.handler == Null || Is<JSReceiver>(proxy.handler));
const handler =
Cast<JSReceiver>(proxy.handler) otherwise ThrowProxyHandlerRevoked;
// 6. Let trap be ? GetMethod(handler, "set").
// 7. If trap is undefined, then (see 7.a below).
const trap: Callable = GetMethod(handler, 'set')
otherwise goto TrapUndefined(target);
// 5. Let target be O.[[ProxyTarget]].
const target = UnsafeCast<JSReceiver>(proxy.target);
// 6. Let trap be ? GetMethod(handler, "set").
// 7. If trap is undefined, then (see 7.a below).
const trap: Callable = GetMethod(handler, 'set')
otherwise goto TrapUndefined(target);
// 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler,
// « target, P, V, Receiver »)).
// 9. If booleanTrapResult is false, return false.
// 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
// 11. If targetDesc is not undefined and targetDesc.[[Configurable]] is
// false, then
// a. If IsDataDescriptor(targetDesc) is true and
// targetDesc.[[Writable]] is false, then
// i. If SameValue(V, targetDesc.[[Value]]) is false, throw a
// TypeError exception.
// b. If IsAccessorDescriptor(targetDesc) is true, then
// i. If targetDesc.[[Set]] is undefined, throw a TypeError
// exception.
// 12. Return true.
const trapResult =
Call(context, trap, handler, target, key, value, receiverValue);
if (ToBoolean(trapResult)) {
CheckGetSetTrapResult(target, proxy, name, value, kProxySet);
return value;
}
ThrowTypeErrorIfStrict(
SmiConstant(MessageTemplate::kProxyTrapReturnedFalsishFor), 'set',
name);
// 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler,
// « target, P, V, Receiver »)).
// 9. If booleanTrapResult is false, return false.
// 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
// 11. If targetDesc is not undefined and targetDesc.[[Configurable]] is
// false, then
// a. If IsDataDescriptor(targetDesc) is true and
// targetDesc.[[Writable]] is false, then
// i. If SameValue(V, targetDesc.[[Value]]) is false, throw a
// TypeError exception.
// b. If IsAccessorDescriptor(targetDesc) is true, then
// i. If targetDesc.[[Set]] is undefined, throw a TypeError
// exception.
// 12. Return true.
const trapResult =
Call(context, trap, handler, target, key, value, receiverValue);
if (ToBoolean(trapResult)) {
CheckGetSetTrapResult(target, proxy, name, value, kProxySet);
return value;
} label TrapUndefined(target: Object) {
// 7.a. Return ? target.[[Set]](P, V, Receiver).
SetPropertyWithReceiver(target, name, value, receiverValue);
return value;
} label ThrowProxyHandlerRevoked deferred {
ThrowTypeError(MessageTemplate::kProxyRevoked, 'set');
}
ThrowTypeErrorIfStrict(
SmiConstant(MessageTemplate::kProxyTrapReturnedFalsishFor), 'set',
name);
return value;
} label TrapUndefined(target: Object) {
// 7.a. Return ? target.[[Set]](P, V, Receiver).
SetPropertyWithReceiver(target, name, value, receiverValue);
return value;
} label ThrowProxyHandlerRevoked deferred {
ThrowTypeError(MessageTemplate::kProxyRevoked, 'set');
}
}
}

View File

@ -6,71 +6,71 @@
namespace proxy {
// ES #sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v
// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v
transitioning builtin
ProxySetPrototypeOf(implicit context: Context)(
proxy: JSProxy, proto: Null|JSReceiver, doThrow: Boolean): JSAny {
PerformStackCheck();
const kTrapName: constexpr string = 'setPrototypeOf';
try {
// 1. Assert: Either Type(V) is Object or Type(V) is Null.
assert(proto == Null || Is<JSReceiver>(proto));
// ES #sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v
// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v
transitioning builtin
ProxySetPrototypeOf(implicit context: Context)(
proxy: JSProxy, proto: Null|JSReceiver, doThrow: Boolean): JSAny {
PerformStackCheck();
const kTrapName: constexpr string = 'setPrototypeOf';
try {
// 1. Assert: Either Type(V) is Object or Type(V) is Null.
assert(proto == Null || Is<JSReceiver>(proto));
// 2. Let handler be O.[[ProxyHandler]].
// 3. If handler is null, throw a TypeError exception.
// 4. Assert: Type(handler) is Object.
assert(proxy.handler == Null || Is<JSReceiver>(proxy.handler));
const handler =
Cast<JSReceiver>(proxy.handler) otherwise ThrowProxyHandlerRevoked;
// 2. Let handler be O.[[ProxyHandler]].
// 3. If handler is null, throw a TypeError exception.
// 4. Assert: Type(handler) is Object.
assert(proxy.handler == Null || Is<JSReceiver>(proxy.handler));
const handler =
Cast<JSReceiver>(proxy.handler) otherwise ThrowProxyHandlerRevoked;
// 5. Let target be O.[[ProxyTarget]].
const target = proxy.target;
// 5. Let target be O.[[ProxyTarget]].
const target = proxy.target;
// 6. Let trap be ? GetMethod(handler, "setPrototypeOf").
// 7. If trap is undefined, then (see 7.a below).
const trap: Callable = GetMethod(handler, kTrapName)
otherwise goto TrapUndefined(target, proto);
// 6. Let trap be ? GetMethod(handler, "setPrototypeOf").
// 7. If trap is undefined, then (see 7.a below).
const trap: Callable = GetMethod(handler, kTrapName)
otherwise goto TrapUndefined(target, proto);
// 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, V
// »)).
const trapResult = Call(context, trap, handler, target, proto);
// 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, V
// »)).
const trapResult = Call(context, trap, handler, target, proto);
// 9. If booleanTrapResult is false, return false.
if (!ToBoolean(trapResult)) {
if (doThrow == True) {
ThrowTypeError(
MessageTemplate::kProxyTrapReturnedFalsishFor, kTrapName);
}
return False;
}
// 10. Let extensibleTarget be ? IsExtensible(target).
// 11. If extensibleTarget is true, return true.
const extensibleTarget: Object = object::ObjectIsExtensibleImpl(target);
assert(extensibleTarget == True || extensibleTarget == False);
if (extensibleTarget == True) {
return True;
}
// 12. Let targetProto be ? target.[[GetPrototypeOf]]().
const targetProto = object::ObjectGetPrototypeOfImpl(target);
// 13. If SameValue(V, targetProto) is false, throw a TypeError
// exception.
// 14. Return true.
if (SameValue(proto, targetProto)) {
return True;
}
ThrowTypeError(MessageTemplate::kProxySetPrototypeOfNonExtensible);
} label TrapUndefined(target: JSAny, proto: JSReceiver|Null) {
// 7.a. Return ? target.[[SetPrototypeOf]]().
// 9. If booleanTrapResult is false, return false.
if (!ToBoolean(trapResult)) {
if (doThrow == True) {
return object::ObjectSetPrototypeOfThrow(target, proto);
ThrowTypeError(
MessageTemplate::kProxyTrapReturnedFalsishFor, kTrapName);
}
return object::ObjectSetPrototypeOfDontThrow(target, proto);
} label ThrowProxyHandlerRevoked deferred {
ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName);
return False;
}
// 10. Let extensibleTarget be ? IsExtensible(target).
// 11. If extensibleTarget is true, return true.
const extensibleTarget: Object = object::ObjectIsExtensibleImpl(target);
assert(extensibleTarget == True || extensibleTarget == False);
if (extensibleTarget == True) {
return True;
}
// 12. Let targetProto be ? target.[[GetPrototypeOf]]().
const targetProto = object::ObjectGetPrototypeOfImpl(target);
// 13. If SameValue(V, targetProto) is false, throw a TypeError
// exception.
// 14. Return true.
if (SameValue(proto, targetProto)) {
return True;
}
ThrowTypeError(MessageTemplate::kProxySetPrototypeOfNonExtensible);
} label TrapUndefined(target: JSAny, proto: JSReceiver|Null) {
// 7.a. Return ? target.[[SetPrototypeOf]]().
if (doThrow == True) {
return object::ObjectSetPrototypeOfThrow(target, proto);
}
return object::ObjectSetPrototypeOfDontThrow(target, proto);
} label ThrowProxyHandlerRevoked deferred {
ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName);
}
}
}

View File

@ -6,21 +6,21 @@
namespace proxy {
extern macro ProxiesCodeStubAssembler::AllocateProxy(
implicit context: Context)(JSReceiver, JSReceiver): JSProxy;
extern macro ProxiesCodeStubAssembler::AllocateProxy(implicit context: Context)(
JSReceiver, JSReceiver): JSProxy;
extern transitioning macro ProxiesCodeStubAssembler::CheckGetSetTrapResult(
implicit context:
Context)(JSReceiver, JSProxy, Name, Object, constexpr int31);
extern transitioning macro ProxiesCodeStubAssembler::CheckGetSetTrapResult(
implicit context: Context)(
JSReceiver, JSProxy, Name, Object, constexpr int31);
extern transitioning macro ProxiesCodeStubAssembler::CheckDeleteTrapResult(
implicit context: Context)(JSReceiver, JSProxy, Name);
extern transitioning macro ProxiesCodeStubAssembler::CheckDeleteTrapResult(
implicit context: Context)(JSReceiver, JSProxy, Name);
extern transitioning macro ProxiesCodeStubAssembler::CheckHasTrapResult(
implicit context: Context)(JSReceiver, JSProxy, Name);
extern transitioning macro ProxiesCodeStubAssembler::CheckHasTrapResult(
implicit context: Context)(JSReceiver, JSProxy, Name);
const kProxyGet: constexpr int31
generates 'JSProxy::AccessKind::kGet';
const kProxySet: constexpr int31
generates 'JSProxy::AccessKind::kSet';
const kProxyGet: constexpr int31
generates 'JSProxy::AccessKind::kGet';
const kProxySet: constexpr int31
generates 'JSProxy::AccessKind::kSet';
}

View File

@ -3,92 +3,88 @@
// found in the LICENSE file.
namespace reflect {
// ES6 section 26.1.10 Reflect.isExtensible
transitioning javascript builtin
ReflectIsExtensible(js-implicit context: NativeContext)(object: JSAny):
JSAny {
const objectJSReceiver = Cast<JSReceiver>(object)
otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Reflect.isExtensible');
return object::ObjectIsExtensibleImpl(objectJSReceiver);
}
// ES6 section 26.1.10 Reflect.isExtensible
transitioning javascript builtin
ReflectIsExtensible(js-implicit context: NativeContext)(object: JSAny): JSAny {
const objectJSReceiver = Cast<JSReceiver>(object)
otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Reflect.isExtensible');
return object::ObjectIsExtensibleImpl(objectJSReceiver);
}
// ES6 section 26.1.12 Reflect.preventExtensions
transitioning javascript builtin
ReflectPreventExtensions(js-implicit context: NativeContext)(object: JSAny):
JSAny {
const objectJSReceiver = Cast<JSReceiver>(object)
otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Reflect.preventExtensions');
return object::ObjectPreventExtensionsDontThrow(objectJSReceiver);
}
// ES6 section 26.1.12 Reflect.preventExtensions
transitioning javascript builtin
ReflectPreventExtensions(js-implicit context: NativeContext)(object: JSAny):
JSAny {
const objectJSReceiver = Cast<JSReceiver>(object)
otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Reflect.preventExtensions');
return object::ObjectPreventExtensionsDontThrow(objectJSReceiver);
}
// ES6 section 26.1.8 Reflect.getPrototypeOf
transitioning javascript builtin
ReflectGetPrototypeOf(js-implicit context: NativeContext)(object: JSAny):
JSAny {
const objectJSReceiver = Cast<JSReceiver>(object)
otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Reflect.getPrototypeOf');
return object::JSReceiverGetPrototypeOf(objectJSReceiver);
}
// ES6 section 26.1.8 Reflect.getPrototypeOf
transitioning javascript builtin
ReflectGetPrototypeOf(js-implicit context: NativeContext)(object: JSAny):
JSAny {
const objectJSReceiver = Cast<JSReceiver>(object)
otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Reflect.getPrototypeOf');
return object::JSReceiverGetPrototypeOf(objectJSReceiver);
}
// ES6 section 26.1.14 Reflect.setPrototypeOf
transitioning javascript builtin ReflectSetPrototypeOf(
js-implicit context: NativeContext)(object: JSAny, proto: JSAny): JSAny {
const objectJSReceiver = Cast<JSReceiver>(object)
otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Reflect.setPrototypeOf');
typeswitch (proto) {
case (proto: JSReceiver|Null): {
return object::ObjectSetPrototypeOfDontThrow(objectJSReceiver, proto);
}
case (JSAny): {
ThrowTypeError(MessageTemplate::kProtoObjectOrNull, proto);
}
// ES6 section 26.1.14 Reflect.setPrototypeOf
transitioning javascript builtin ReflectSetPrototypeOf(
js-implicit context: NativeContext)(object: JSAny, proto: JSAny): JSAny {
const objectJSReceiver = Cast<JSReceiver>(object)
otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Reflect.setPrototypeOf');
typeswitch (proto) {
case (proto: JSReceiver|Null): {
return object::ObjectSetPrototypeOfDontThrow(objectJSReceiver, proto);
}
case (JSAny): {
ThrowTypeError(MessageTemplate::kProtoObjectOrNull, proto);
}
}
}
extern transitioning builtin ToName(implicit context: Context)(JSAny):
AnyName;
type OnNonExistent constexpr 'OnNonExistent';
const kReturnUndefined: constexpr OnNonExistent
generates 'OnNonExistent::kReturnUndefined';
extern macro SmiConstant(constexpr OnNonExistent): Smi;
extern transitioning builtin GetPropertyWithReceiver(
implicit context: Context)(JSAny, Name, JSAny, Smi): JSAny;
extern transitioning builtin ToName(implicit context: Context)(JSAny): AnyName;
type OnNonExistent constexpr 'OnNonExistent';
const kReturnUndefined: constexpr OnNonExistent
generates 'OnNonExistent::kReturnUndefined';
extern macro SmiConstant(constexpr OnNonExistent): Smi;
extern transitioning builtin GetPropertyWithReceiver(implicit context: Context)(
JSAny, Name, JSAny, Smi): JSAny;
// ES6 section 26.1.6 Reflect.get
transitioning javascript builtin
ReflectGet(js-implicit context: NativeContext)(...arguments): JSAny {
const object: JSAny = arguments[0];
const objectJSReceiver = Cast<JSReceiver>(object)
otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Reflect.get');
const propertyKey: JSAny = arguments[1];
const name: AnyName = ToName(propertyKey);
const receiver: JSAny =
arguments.length > 2 ? arguments[2] : objectJSReceiver;
return GetPropertyWithReceiver(
objectJSReceiver, name, receiver, SmiConstant(kReturnUndefined));
}
// ES6 section 26.1.6 Reflect.get
transitioning javascript builtin
ReflectGet(js-implicit context: NativeContext)(...arguments): JSAny {
const object: JSAny = arguments[0];
const objectJSReceiver = Cast<JSReceiver>(object)
otherwise ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'Reflect.get');
const propertyKey: JSAny = arguments[1];
const name: AnyName = ToName(propertyKey);
const receiver: JSAny =
arguments.length > 2 ? arguments[2] : objectJSReceiver;
return GetPropertyWithReceiver(
objectJSReceiver, name, receiver, SmiConstant(kReturnUndefined));
}
// ES6 section 26.1.4 Reflect.deleteProperty
transitioning javascript builtin ReflectDeleteProperty(
js-implicit context: NativeContext)(object: JSAny, key: JSAny): JSAny {
const objectJSReceiver = Cast<JSReceiver>(object)
otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Reflect.deleteProperty');
return DeleteProperty(objectJSReceiver, key, LanguageMode::kSloppy);
}
// ES6 section 26.1.4 Reflect.deleteProperty
transitioning javascript builtin ReflectDeleteProperty(
js-implicit context: NativeContext)(object: JSAny, key: JSAny): JSAny {
const objectJSReceiver = Cast<JSReceiver>(object)
otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Reflect.deleteProperty');
return DeleteProperty(objectJSReceiver, key, LanguageMode::kSloppy);
}
// ES section #sec-reflect.has
transitioning javascript builtin
ReflectHas(js-implicit context: NativeContext)(object: JSAny, key: JSAny):
JSAny {
const objectJSReceiver = Cast<JSReceiver>(object)
otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Reflect.has');
return HasProperty(objectJSReceiver, key);
}
// ES section #sec-reflect.has
transitioning javascript builtin
ReflectHas(js-implicit context: NativeContext)(
object: JSAny, key: JSAny): JSAny {
const objectJSReceiver = Cast<JSReceiver>(object)
otherwise ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'Reflect.has');
return HasProperty(objectJSReceiver, key);
}
} // namespace reflect

View File

@ -6,40 +6,39 @@
namespace regexp {
@export
transitioning macro RegExpPrototypeExecBodyFast(implicit context: Context)(
receiver: JSReceiver, string: String): JSAny {
return RegExpPrototypeExecBody(receiver, string, true);
}
transitioning macro RegExpPrototypeExecBodySlow(implicit context: Context)(
receiver: JSReceiver, string: String): JSAny {
return RegExpPrototypeExecBody(receiver, string, false);
}
// Slow path stub for RegExpPrototypeExec to decrease code size.
transitioning builtin
RegExpPrototypeExecSlow(implicit context: Context)(
regexp: JSRegExp, string: String): JSAny {
return RegExpPrototypeExecBodySlow(regexp, string);
}
extern macro RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(
implicit context: Context)(Object): bool;
// ES#sec-regexp.prototype.exec
// RegExp.prototype.exec ( string )
transitioning javascript builtin RegExpPrototypeExec(
js-implicit context: NativeContext,
receiver: JSAny)(string: JSAny): JSAny {
// Ensure {receiver} is a JSRegExp.
const receiver = Cast<JSRegExp>(receiver) otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, 'RegExp.prototype.exec',
receiver);
const string = ToString_Inline(string);
return IsFastRegExpNoPrototype(receiver) ?
RegExpPrototypeExecBodyFast(receiver, string) :
RegExpPrototypeExecSlow(receiver, string);
}
@export
transitioning macro RegExpPrototypeExecBodyFast(implicit context: Context)(
receiver: JSReceiver, string: String): JSAny {
return RegExpPrototypeExecBody(receiver, string, true);
}
transitioning macro RegExpPrototypeExecBodySlow(implicit context: Context)(
receiver: JSReceiver, string: String): JSAny {
return RegExpPrototypeExecBody(receiver, string, false);
}
// Slow path stub for RegExpPrototypeExec to decrease code size.
transitioning builtin
RegExpPrototypeExecSlow(implicit context: Context)(
regexp: JSRegExp, string: String): JSAny {
return RegExpPrototypeExecBodySlow(regexp, string);
}
extern macro RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(
implicit context: Context)(Object): bool;
// ES#sec-regexp.prototype.exec
// RegExp.prototype.exec ( string )
transitioning javascript builtin RegExpPrototypeExec(
js-implicit context: NativeContext, receiver: JSAny)(string: JSAny): JSAny {
// Ensure {receiver} is a JSRegExp.
const receiver = Cast<JSRegExp>(receiver) otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, 'RegExp.prototype.exec',
receiver);
const string = ToString_Inline(string);
return IsFastRegExpNoPrototype(receiver) ?
RegExpPrototypeExecBodyFast(receiver, string) :
RegExpPrototypeExecSlow(receiver, string);
}
}

View File

@ -6,220 +6,217 @@
namespace regexp {
extern transitioning macro
RegExpMatchAllAssembler::CreateRegExpStringIterator(
NativeContext, Object, String, bool, bool): JSAny;
extern transitioning macro
RegExpMatchAllAssembler::CreateRegExpStringIterator(
NativeContext, Object, String, bool, bool): JSAny;
@export
transitioning macro RegExpPrototypeMatchAllImpl(implicit context: Context)(
nativeContext: NativeContext, receiver: JSAny, string: JSAny): JSAny {
// 1. Let R be the this value.
// 2. If Type(R) is not Object, throw a TypeError exception.
ThrowIfNotJSReceiver(
receiver, MessageTemplate::kIncompatibleMethodReceiver,
'RegExp.prototype.@@matchAll');
const receiver = UnsafeCast<JSReceiver>(receiver);
@export
transitioning macro RegExpPrototypeMatchAllImpl(implicit context: Context)(
nativeContext: NativeContext, receiver: JSAny, string: JSAny): JSAny {
// 1. Let R be the this value.
// 2. If Type(R) is not Object, throw a TypeError exception.
ThrowIfNotJSReceiver(
receiver, MessageTemplate::kIncompatibleMethodReceiver,
'RegExp.prototype.@@matchAll');
const receiver = UnsafeCast<JSReceiver>(receiver);
// 3. Let S be ? ToString(O).
const string: String = ToString_Inline(string);
// 3. Let S be ? ToString(O).
const string: String = ToString_Inline(string);
let matcher: Object;
let global: bool;
let unicode: bool;
let matcher: Object;
let global: bool;
let unicode: bool;
// 'FastJSRegExp' uses the strict fast path check because following code
// uses the flags property.
// TODO(jgruber): Handle slow flag accesses on the fast path and make this
// permissive.
typeswitch (receiver) {
case (fastRegExp: FastJSRegExp): {
const source = fastRegExp.source;
// 'FastJSRegExp' uses the strict fast path check because following code
// uses the flags property.
// TODO(jgruber): Handle slow flag accesses on the fast path and make this
// permissive.
typeswitch (receiver) {
case (fastRegExp: FastJSRegExp): {
const source = fastRegExp.source;
// 4. Let C be ? SpeciesConstructor(R, %RegExp%).
// 5. Let flags be ? ToString(? Get(R, "flags")).
// 6. Let matcher be ? Construct(C, « R, flags »).
const flags: String = FastFlagsGetter(fastRegExp);
matcher = RegExpCreate(nativeContext, source, flags);
const matcherRegExp = UnsafeCast<JSRegExp>(matcher);
assert(IsFastRegExpPermissive(matcherRegExp));
// 4. Let C be ? SpeciesConstructor(R, %RegExp%).
// 5. Let flags be ? ToString(? Get(R, "flags")).
// 6. Let matcher be ? Construct(C, « R, flags »).
const flags: String = FastFlagsGetter(fastRegExp);
matcher = RegExpCreate(nativeContext, source, flags);
const matcherRegExp = UnsafeCast<JSRegExp>(matcher);
assert(IsFastRegExpPermissive(matcherRegExp));
// 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
// 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
const fastRegExp = UnsafeCast<FastJSRegExp>(receiver);
FastStoreLastIndex(matcherRegExp, fastRegExp.lastIndex);
// 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
// 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
const fastRegExp = UnsafeCast<FastJSRegExp>(receiver);
FastStoreLastIndex(matcherRegExp, fastRegExp.lastIndex);
// 9. If flags contains "g", let global be true.
// 10. Else, let global be false.
global = FastFlagGetter(matcherRegExp, Flag::kGlobal);
// 9. If flags contains "g", let global be true.
// 10. Else, let global be false.
global = FastFlagGetter(matcherRegExp, Flag::kGlobal);
// 11. If flags contains "u", let fullUnicode be true.
// 12. Else, let fullUnicode be false.
unicode = FastFlagGetter(matcherRegExp, Flag::kUnicode);
}
case (Object): {
// 4. Let C be ? SpeciesConstructor(R, %RegExp%).
const regexpFun = LoadRegExpFunction(nativeContext);
const speciesConstructor =
UnsafeCast<Constructor>(SpeciesConstructor(receiver, regexpFun));
// 5. Let flags be ? ToString(? Get(R, "flags")).
const flags = GetProperty(receiver, 'flags');
const flagsString = ToString_Inline(flags);
// 6. Let matcher be ? Construct(C, « R, flags »).
matcher = Construct(speciesConstructor, receiver, flagsString);
// 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
const lastIndex: Number = ToLength_Inline(SlowLoadLastIndex(receiver));
// 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
SlowStoreLastIndex(UnsafeCast<JSReceiver>(matcher), lastIndex);
// 9. If flags contains "g", let global be true.
// 10. Else, let global be false.
const globalCharString: String = StringConstant('g');
const globalIndex: Smi =
StringIndexOf(flagsString, globalCharString, 0);
global = globalIndex != -1;
// 11. If flags contains "u", let fullUnicode be true.
// 12. Else, let fullUnicode be false.
const unicodeCharString = StringConstant('u');
const unicodeIndex: Smi =
StringIndexOf(flagsString, unicodeCharString, 0);
unicode = unicodeIndex != -1;
}
// 11. If flags contains "u", let fullUnicode be true.
// 12. Else, let fullUnicode be false.
unicode = FastFlagGetter(matcherRegExp, Flag::kUnicode);
}
case (Object): {
// 4. Let C be ? SpeciesConstructor(R, %RegExp%).
const regexpFun = LoadRegExpFunction(nativeContext);
const speciesConstructor =
UnsafeCast<Constructor>(SpeciesConstructor(receiver, regexpFun));
// 13. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode).
return CreateRegExpStringIterator(
nativeContext, matcher, string, global, unicode);
// 5. Let flags be ? ToString(? Get(R, "flags")).
const flags = GetProperty(receiver, 'flags');
const flagsString = ToString_Inline(flags);
// 6. Let matcher be ? Construct(C, « R, flags »).
matcher = Construct(speciesConstructor, receiver, flagsString);
// 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
const lastIndex: Number = ToLength_Inline(SlowLoadLastIndex(receiver));
// 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
SlowStoreLastIndex(UnsafeCast<JSReceiver>(matcher), lastIndex);
// 9. If flags contains "g", let global be true.
// 10. Else, let global be false.
const globalCharString: String = StringConstant('g');
const globalIndex: Smi = StringIndexOf(flagsString, globalCharString, 0);
global = globalIndex != -1;
// 11. If flags contains "u", let fullUnicode be true.
// 12. Else, let fullUnicode be false.
const unicodeCharString = StringConstant('u');
const unicodeIndex: Smi =
StringIndexOf(flagsString, unicodeCharString, 0);
unicode = unicodeIndex != -1;
}
}
// https://tc39.github.io/proposal-string-matchall/
// RegExp.prototype [ @@matchAll ] ( string )
transitioning javascript builtin RegExpPrototypeMatchAll(
js-implicit context: NativeContext,
receiver: JSAny)(string: JSAny): JSAny {
return RegExpPrototypeMatchAllImpl(context, receiver, string);
}
// 13. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode).
return CreateRegExpStringIterator(
nativeContext, matcher, string, global, unicode);
}
// https://tc39.github.io/proposal-string-matchall/
// %RegExpStringIteratorPrototype%.next ( )
transitioning javascript builtin RegExpStringIteratorPrototypeNext(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
// 1. Let O be the this value.
// 2. If Type(O) is not Object, throw a TypeError exception.
// 3. If O does not have all of the internal slots of a RegExp String
// Iterator Object Instance (see 5.3), throw a TypeError exception.
const methodName: constexpr string =
'%RegExpStringIterator%.prototype.next';
const receiver = Cast<JSRegExpStringIterator>(receiver) otherwise
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, methodName, receiver);
// https://tc39.github.io/proposal-string-matchall/
// RegExp.prototype [ @@matchAll ] ( string )
transitioning javascript builtin RegExpPrototypeMatchAll(
js-implicit context: NativeContext, receiver: JSAny)(string: JSAny): JSAny {
return RegExpPrototypeMatchAllImpl(context, receiver, string);
}
// https://tc39.github.io/proposal-string-matchall/
// %RegExpStringIteratorPrototype%.next ( )
transitioning javascript builtin RegExpStringIteratorPrototypeNext(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
// 1. Let O be the this value.
// 2. If Type(O) is not Object, throw a TypeError exception.
// 3. If O does not have all of the internal slots of a RegExp String
// Iterator Object Instance (see 5.3), throw a TypeError exception.
const methodName: constexpr string = '%RegExpStringIterator%.prototype.next';
const receiver = Cast<JSRegExpStringIterator>(receiver) otherwise
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, methodName, receiver);
try {
// 4. If O.[[Done]] is true, then
// a. Return ! CreateIterResultObject(undefined, true).
const flags: SmiTagged<JSRegExpStringIteratorFlags> = receiver.flags;
if (flags.done) goto ReturnEmptyDoneResult;
// 5. Let R be O.[[iteratingRegExp]].
const iteratingRegExp: JSReceiver = receiver.iterating_reg_exp;
// 6. Let S be O.[[IteratedString]].
const iteratingString: String = receiver.iterated_string;
// 7. Let global be O.[[Global]].
// 8. Let fullUnicode be O.[[Unicode]].
// 9. Let match be ? RegExpExec(R, S).
let match: Object;
let isFastRegExp: bool = false;
try {
// 4. If O.[[Done]] is true, then
// a. Return ! CreateIterResultObject(undefined, true).
const flags: SmiTagged<JSRegExpStringIteratorFlags> = receiver.flags;
if (flags.done) goto ReturnEmptyDoneResult;
// 5. Let R be O.[[iteratingRegExp]].
const iteratingRegExp: JSReceiver = receiver.iterating_reg_exp;
// 6. Let S be O.[[IteratedString]].
const iteratingString: String = receiver.iterated_string;
// 7. Let global be O.[[Global]].
// 8. Let fullUnicode be O.[[Unicode]].
// 9. Let match be ? RegExpExec(R, S).
let match: Object;
let isFastRegExp: bool = false;
try {
if (IsFastRegExpPermissive(iteratingRegExp)) {
const regexp = UnsafeCast<JSRegExp>(iteratingRegExp);
const lastIndex = LoadLastIndexAsLength(regexp, true);
const matchIndices: RegExpMatchInfo =
RegExpPrototypeExecBodyWithoutResultFast(
regexp, iteratingString, lastIndex)
otherwise IfNoMatch;
match = ConstructNewResultFromMatchInfo(
regexp, matchIndices, iteratingString, lastIndex);
isFastRegExp = true;
} else {
match = RegExpExec(iteratingRegExp, iteratingString);
if (match == Null) {
goto IfNoMatch;
}
if (IsFastRegExpPermissive(iteratingRegExp)) {
const regexp = UnsafeCast<JSRegExp>(iteratingRegExp);
const lastIndex = LoadLastIndexAsLength(regexp, true);
const matchIndices: RegExpMatchInfo =
RegExpPrototypeExecBodyWithoutResultFast(
regexp, iteratingString, lastIndex)
otherwise IfNoMatch;
match = ConstructNewResultFromMatchInfo(
regexp, matchIndices, iteratingString, lastIndex);
isFastRegExp = true;
} else {
match = RegExpExec(iteratingRegExp, iteratingString);
if (match == Null) {
goto IfNoMatch;
}
// 11. Else,
// b. Else, handle non-global case first.
if (!flags.global) {
// i. Set O.[[Done]] to true.
receiver.flags.done = true;
}
// 11. Else,
// b. Else, handle non-global case first.
if (!flags.global) {
// i. Set O.[[Done]] to true.
receiver.flags.done = true;
// ii. Return ! CreateIterResultObject(match, false).
return AllocateJSIteratorResult(UnsafeCast<JSAny>(match), False);
}
// a. If global is true,
assert(flags.global);
if (isFastRegExp) {
// i. Let matchStr be ? ToString(? Get(match, "0")).
const match = UnsafeCast<JSRegExpResult>(match);
const resultFixedArray = UnsafeCast<FixedArray>(match.elements);
const matchStr = UnsafeCast<String>(resultFixedArray.objects[0]);
// When iterating_regexp is fast, we assume it stays fast even after
// accessing the first match from the RegExp result.
assert(IsFastRegExpPermissive(iteratingRegExp));
const iteratingRegExp = UnsafeCast<JSRegExp>(iteratingRegExp);
if (matchStr == kEmptyString) {
// 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")).
const thisIndex: Smi = FastLoadLastIndex(iteratingRegExp);
// 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex,
// fullUnicode).
const nextIndex: Smi = AdvanceStringIndexFast(
iteratingString, thisIndex, flags.unicode);
// 3. Perform ? Set(R, "lastIndex", nextIndex, true).
FastStoreLastIndex(iteratingRegExp, nextIndex);
}
// iii. Return ! CreateIterResultObject(match, false).
return AllocateJSIteratorResult(match, False);
}
assert(!isFastRegExp);
// ii. Return ! CreateIterResultObject(match, false).
return AllocateJSIteratorResult(UnsafeCast<JSAny>(match), False);
}
// a. If global is true,
assert(flags.global);
if (isFastRegExp) {
// i. Let matchStr be ? ToString(? Get(match, "0")).
const match = UnsafeCast<JSAny>(match);
const matchStr = ToString_Inline(GetProperty(match, SmiConstant(0)));
const match = UnsafeCast<JSRegExpResult>(match);
const resultFixedArray = UnsafeCast<FixedArray>(match.elements);
const matchStr = UnsafeCast<String>(resultFixedArray.objects[0]);
// When iterating_regexp is fast, we assume it stays fast even after
// accessing the first match from the RegExp result.
assert(IsFastRegExpPermissive(iteratingRegExp));
const iteratingRegExp = UnsafeCast<JSRegExp>(iteratingRegExp);
if (matchStr == kEmptyString) {
// 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")).
const lastIndex: JSAny = SlowLoadLastIndex(iteratingRegExp);
const thisIndex: Number = ToLength_Inline(lastIndex);
const thisIndex: Smi = FastLoadLastIndex(iteratingRegExp);
// 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex,
// fullUnicode).
const nextIndex: Number =
AdvanceStringIndexSlow(iteratingString, thisIndex, flags.unicode);
const nextIndex: Smi =
AdvanceStringIndexFast(iteratingString, thisIndex, flags.unicode);
// 3. Perform ? Set(R, "lastIndex", nextIndex, true).
SlowStoreLastIndex(iteratingRegExp, nextIndex);
FastStoreLastIndex(iteratingRegExp, nextIndex);
}
// iii. Return ! CreateIterResultObject(match, false).
return AllocateJSIteratorResult(match, False);
}
// 10. If match is null, then
label IfNoMatch {
// a. Set O.[[Done]] to true.
receiver.flags.done = true;
assert(!isFastRegExp);
// i. Let matchStr be ? ToString(? Get(match, "0")).
const match = UnsafeCast<JSAny>(match);
const matchStr = ToString_Inline(GetProperty(match, SmiConstant(0)));
// b. Return ! CreateIterResultObject(undefined, true).
goto ReturnEmptyDoneResult;
if (matchStr == kEmptyString) {
// 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")).
const lastIndex: JSAny = SlowLoadLastIndex(iteratingRegExp);
const thisIndex: Number = ToLength_Inline(lastIndex);
// 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex,
// fullUnicode).
const nextIndex: Number =
AdvanceStringIndexSlow(iteratingString, thisIndex, flags.unicode);
// 3. Perform ? Set(R, "lastIndex", nextIndex, true).
SlowStoreLastIndex(iteratingRegExp, nextIndex);
}
} label ReturnEmptyDoneResult {
return AllocateJSIteratorResult(Undefined, True);
// iii. Return ! CreateIterResultObject(match, false).
return AllocateJSIteratorResult(match, False);
}
// 10. If match is null, then
label IfNoMatch {
// a. Set O.[[Done]] to true.
receiver.flags.done = true;
// b. Return ! CreateIterResultObject(undefined, true).
goto ReturnEmptyDoneResult;
}
} label ReturnEmptyDoneResult {
return AllocateJSIteratorResult(Undefined, True);
}
}
}

View File

@ -6,158 +6,157 @@
namespace regexp {
const kATOM: constexpr int31
generates 'JSRegExp::ATOM';
const kTagIndex: constexpr int31
generates 'JSRegExp::kTagIndex';
const kAtomPatternIndex: constexpr int31
generates 'JSRegExp::kAtomPatternIndex';
const kATOM: constexpr int31
generates 'JSRegExp::ATOM';
const kTagIndex: constexpr int31
generates 'JSRegExp::kTagIndex';
const kAtomPatternIndex: constexpr int31
generates 'JSRegExp::kAtomPatternIndex';
extern transitioning macro RegExpBuiltinsAssembler::FlagGetter(
implicit context: Context)(Object, constexpr Flag, constexpr bool): bool;
extern transitioning macro RegExpBuiltinsAssembler::FlagGetter(
implicit context: Context)(Object, constexpr Flag, constexpr bool): bool;
extern macro UnsafeLoadFixedArrayElement(RegExpMatchInfo, constexpr int31):
Object;
extern macro UnsafeLoadFixedArrayElement(
RegExpMatchInfo, constexpr int31): Object;
transitioning macro RegExpPrototypeMatchBody(implicit context: Context)(
regexp: JSReceiver, string: String, isFastPath: constexpr bool): JSAny {
if constexpr (isFastPath) {
assert(Is<FastJSRegExp>(regexp));
transitioning macro RegExpPrototypeMatchBody(implicit context: Context)(
regexp: JSReceiver, string: String, isFastPath: constexpr bool): JSAny {
if constexpr (isFastPath) {
assert(Is<FastJSRegExp>(regexp));
}
const isGlobal: bool = FlagGetter(regexp, Flag::kGlobal, isFastPath);
if (!isGlobal) {
return isFastPath ? RegExpPrototypeExecBodyFast(regexp, string) :
RegExpExec(regexp, string);
}
assert(isGlobal);
const isUnicode: bool = FlagGetter(regexp, Flag::kUnicode, isFastPath);
StoreLastIndex(regexp, 0, isFastPath);
// Allocate an array to store the resulting match strings.
let array = growable_fixed_array::NewGrowableFixedArray();
// Check if the regexp is an ATOM type. If so, then keep the literal string
// to search for so that we can avoid calling substring in the loop below.
let atom: bool = false;
let searchString: String = EmptyStringConstant();
if constexpr (isFastPath) {
const maybeAtomRegexp = UnsafeCast<JSRegExp>(regexp);
const data = UnsafeCast<FixedArray>(maybeAtomRegexp.data);
if (UnsafeCast<Smi>(data.objects[kTagIndex]) == kATOM) {
searchString = UnsafeCast<String>(data.objects[kAtomPatternIndex]);
atom = true;
}
}
const isGlobal: bool = FlagGetter(regexp, Flag::kGlobal, isFastPath);
if (!isGlobal) {
return isFastPath ? RegExpPrototypeExecBodyFast(regexp, string) :
RegExpExec(regexp, string);
}
assert(isGlobal);
const isUnicode: bool = FlagGetter(regexp, Flag::kUnicode, isFastPath);
StoreLastIndex(regexp, 0, isFastPath);
// Allocate an array to store the resulting match strings.
let array = growable_fixed_array::NewGrowableFixedArray();
// Check if the regexp is an ATOM type. If so, then keep the literal string
// to search for so that we can avoid calling substring in the loop below.
let atom: bool = false;
let searchString: String = EmptyStringConstant();
if constexpr (isFastPath) {
const maybeAtomRegexp = UnsafeCast<JSRegExp>(regexp);
const data = UnsafeCast<FixedArray>(maybeAtomRegexp.data);
if (UnsafeCast<Smi>(data.objects[kTagIndex]) == kATOM) {
searchString = UnsafeCast<String>(data.objects[kAtomPatternIndex]);
atom = true;
}
}
while (true) {
let match: String = EmptyStringConstant();
try {
if constexpr (isFastPath) {
// On the fast path, grab the matching string from the raw match index
// array.
const matchIndices: RegExpMatchInfo =
RegExpPrototypeExecBodyWithoutResultFast(
UnsafeCast<JSRegExp>(regexp), string) otherwise IfDidNotMatch;
if (atom) {
match = searchString;
} else {
const matchFrom = UnsafeLoadFixedArrayElement(
matchIndices, kRegExpMatchInfoFirstCaptureIndex);
const matchTo = UnsafeLoadFixedArrayElement(
matchIndices, kRegExpMatchInfoFirstCaptureIndex + 1);
match = SubString(
string, UnsafeCast<Smi>(matchFrom), UnsafeCast<Smi>(matchTo));
}
while (true) {
let match: String = EmptyStringConstant();
try {
if constexpr (isFastPath) {
// On the fast path, grab the matching string from the raw match index
// array.
const matchIndices: RegExpMatchInfo =
RegExpPrototypeExecBodyWithoutResultFast(
UnsafeCast<JSRegExp>(regexp), string) otherwise IfDidNotMatch;
if (atom) {
match = searchString;
} else {
assert(!isFastPath);
const resultTemp = RegExpExec(regexp, string);
if (resultTemp == Null) {
goto IfDidNotMatch;
}
match = ToString_Inline(GetProperty(resultTemp, SmiConstant(0)));
const matchFrom = UnsafeLoadFixedArrayElement(
matchIndices, kRegExpMatchInfoFirstCaptureIndex);
const matchTo = UnsafeLoadFixedArrayElement(
matchIndices, kRegExpMatchInfoFirstCaptureIndex + 1);
match = SubString(
string, UnsafeCast<Smi>(matchFrom), UnsafeCast<Smi>(matchTo));
}
goto IfDidMatch;
} label IfDidNotMatch {
return array.length == 0 ? Null : array.ToJSArray();
} label IfDidMatch {
// Store the match, growing the fixed array if needed.
array.Push(match);
// Advance last index if the match is the empty string.
const matchLength: Smi = match.length_smi;
if (matchLength != 0) {
continue;
} else {
assert(!isFastPath);
const resultTemp = RegExpExec(regexp, string);
if (resultTemp == Null) {
goto IfDidNotMatch;
}
let lastIndex = LoadLastIndex(regexp, isFastPath);
if constexpr (isFastPath) {
assert(TaggedIsPositiveSmi(lastIndex));
} else {
lastIndex = ToLength_Inline(lastIndex);
}
const newLastIndex: Number = AdvanceStringIndex(
string, UnsafeCast<Number>(lastIndex), isUnicode, isFastPath);
if constexpr (isFastPath) {
// On the fast path, we can be certain that lastIndex can never be
// incremented to overflow the Smi range since the maximal string
// length is less than the maximal Smi value.
const kMaxStringLengthFitsSmi: constexpr bool =
kStringMaxLengthUintptr < kSmiMaxValue;
StaticAssert(kMaxStringLengthFitsSmi);
assert(TaggedIsPositiveSmi(newLastIndex));
}
StoreLastIndex(regexp, newLastIndex, isFastPath);
match = ToString_Inline(GetProperty(resultTemp, SmiConstant(0)));
}
goto IfDidMatch;
} label IfDidNotMatch {
return array.length == 0 ? Null : array.ToJSArray();
} label IfDidMatch {
// Store the match, growing the fixed array if needed.
array.Push(match);
// Advance last index if the match is the empty string.
const matchLength: Smi = match.length_smi;
if (matchLength != 0) {
continue;
}
let lastIndex = LoadLastIndex(regexp, isFastPath);
if constexpr (isFastPath) {
assert(TaggedIsPositiveSmi(lastIndex));
} else {
lastIndex = ToLength_Inline(lastIndex);
}
const newLastIndex: Number = AdvanceStringIndex(
string, UnsafeCast<Number>(lastIndex), isUnicode, isFastPath);
if constexpr (isFastPath) {
// On the fast path, we can be certain that lastIndex can never be
// incremented to overflow the Smi range since the maximal string
// length is less than the maximal Smi value.
const kMaxStringLengthFitsSmi: constexpr bool =
kStringMaxLengthUintptr < kSmiMaxValue;
StaticAssert(kMaxStringLengthFitsSmi);
assert(TaggedIsPositiveSmi(newLastIndex));
}
StoreLastIndex(regexp, newLastIndex, isFastPath);
}
VerifiedUnreachable();
}
transitioning macro FastRegExpPrototypeMatchBody(implicit context: Context)(
receiver: FastJSRegExp, string: String): JSAny {
return RegExpPrototypeMatchBody(receiver, string, true);
}
transitioning macro SlowRegExpPrototypeMatchBody(implicit context: Context)(
receiver: JSReceiver, string: String): JSAny {
return RegExpPrototypeMatchBody(receiver, string, false);
}
// Helper that skips a few initial checks. and assumes...
// 1) receiver is a "fast" RegExp
// 2) pattern is a string
transitioning builtin RegExpMatchFast(implicit context: Context)(
receiver: FastJSRegExp, string: String): JSAny {
return FastRegExpPrototypeMatchBody(receiver, string);
}
// ES#sec-regexp.prototype-@@match
// RegExp.prototype [ @@match ] ( string )
transitioning javascript builtin RegExpPrototypeMatch(
js-implicit context: NativeContext,
receiver: JSAny)(string: JSAny): JSAny {
ThrowIfNotJSReceiver(
receiver, MessageTemplate::kIncompatibleMethodReceiver,
'RegExp.prototype.@@match');
const receiver = UnsafeCast<JSReceiver>(receiver);
const string: String = ToString_Inline(string);
// Strict: Reads global and unicode properties.
// TODO(jgruber): Handle slow flag accesses on the fast path and make this
// permissive.
const fastRegExp = Cast<FastJSRegExp>(receiver)
otherwise return SlowRegExpPrototypeMatchBody(receiver, string);
// TODO(pwong): Could be optimized to remove the overhead of calling the
// builtin (at the cost of a larger builtin).
return RegExpMatchFast(fastRegExp, string);
}
VerifiedUnreachable();
}
transitioning macro FastRegExpPrototypeMatchBody(implicit context: Context)(
receiver: FastJSRegExp, string: String): JSAny {
return RegExpPrototypeMatchBody(receiver, string, true);
}
transitioning macro SlowRegExpPrototypeMatchBody(implicit context: Context)(
receiver: JSReceiver, string: String): JSAny {
return RegExpPrototypeMatchBody(receiver, string, false);
}
// Helper that skips a few initial checks. and assumes...
// 1) receiver is a "fast" RegExp
// 2) pattern is a string
transitioning builtin RegExpMatchFast(implicit context: Context)(
receiver: FastJSRegExp, string: String): JSAny {
return FastRegExpPrototypeMatchBody(receiver, string);
}
// ES#sec-regexp.prototype-@@match
// RegExp.prototype [ @@match ] ( string )
transitioning javascript builtin RegExpPrototypeMatch(
js-implicit context: NativeContext, receiver: JSAny)(string: JSAny): JSAny {
ThrowIfNotJSReceiver(
receiver, MessageTemplate::kIncompatibleMethodReceiver,
'RegExp.prototype.@@match');
const receiver = UnsafeCast<JSReceiver>(receiver);
const string: String = ToString_Inline(string);
// Strict: Reads global and unicode properties.
// TODO(jgruber): Handle slow flag accesses on the fast path and make this
// permissive.
const fastRegExp = Cast<FastJSRegExp>(receiver)
otherwise return SlowRegExpPrototypeMatchBody(receiver, string);
// TODO(pwong): Could be optimized to remove the overhead of calling the
// builtin (at the cost of a larger builtin).
return RegExpMatchFast(fastRegExp, string);
}
}

View File

@ -6,257 +6,252 @@
namespace regexp {
extern builtin
SubString(implicit context: Context)(String, Smi, Smi): String;
extern builtin
SubString(implicit context: Context)(String, Smi, Smi): String;
extern runtime RegExpExecMultiple(implicit context: Context)(
JSRegExp, String, RegExpMatchInfo, JSArray): Null|JSArray;
extern transitioning runtime
RegExpReplaceRT(Context, JSReceiver, String, Object): String;
extern transitioning runtime
StringBuilderConcat(implicit context: Context)(JSArray, Smi, String): String;
extern transitioning runtime
StringReplaceNonGlobalRegExpWithFunction(implicit context: Context)(
String, JSRegExp, Callable): String;
extern runtime RegExpExecMultiple(implicit context: Context)(
JSRegExp, String, RegExpMatchInfo, JSArray): Null|JSArray;
extern transitioning runtime
RegExpReplaceRT(Context, JSReceiver, String, Object): String;
extern transitioning runtime
StringBuilderConcat(implicit context: Context)(JSArray, Smi, String): String;
extern transitioning runtime
StringReplaceNonGlobalRegExpWithFunction(implicit context: Context)(
String, JSRegExp, Callable): String;
transitioning macro RegExpReplaceCallableNoExplicitCaptures(implicit context:
Context)(
matchesElements: FixedArray, matchesLength: intptr, string: String,
replaceFn: Callable) {
let matchStart: Smi = 0;
for (let i: intptr = 0; i < matchesLength; i++) {
typeswitch (matchesElements.objects[i]) {
// Element represents a slice.
case (elSmi: Smi): {
// The slice's match start and end is either encoded as one or two
// smis. A positive smi indicates a single smi encoding (see
// ReplacementStringBuilder::AddSubjectSlice()).
if (elSmi > 0) {
// For single smi encoding, see
// StringBuilderSubstringLength::encode() and
// StringBuilderSubstringPosition::encode().
const elInt: intptr = Convert<intptr>(elSmi);
const newMatchStart: intptr = (elInt >> 11) + (elInt & 0x7FF);
matchStart = Convert<Smi>(newMatchStart);
} else {
// For two smi encoding, the length is negative followed by the
// match start.
const nextEl: Smi = UnsafeCast<Smi>(matchesElements.objects[++i]);
matchStart = nextEl - elSmi;
}
}
// Element represents the matched substring, which is then passed to the
// replace function.
case (elString: String): {
const replacementObj: JSAny =
Call(context, replaceFn, Undefined, elString, matchStart, string);
const replacement: String = ToString_Inline(replacementObj);
matchesElements.objects[i] = replacement;
matchStart += elString.length_smi;
}
case (Object): deferred {
unreachable;
transitioning macro RegExpReplaceCallableNoExplicitCaptures(
implicit context: Context)(
matchesElements: FixedArray, matchesLength: intptr, string: String,
replaceFn: Callable) {
let matchStart: Smi = 0;
for (let i: intptr = 0; i < matchesLength; i++) {
typeswitch (matchesElements.objects[i]) {
// Element represents a slice.
case (elSmi: Smi): {
// The slice's match start and end is either encoded as one or two
// smis. A positive smi indicates a single smi encoding (see
// ReplacementStringBuilder::AddSubjectSlice()).
if (elSmi > 0) {
// For single smi encoding, see
// StringBuilderSubstringLength::encode() and
// StringBuilderSubstringPosition::encode().
const elInt: intptr = Convert<intptr>(elSmi);
const newMatchStart: intptr = (elInt >> 11) + (elInt & 0x7FF);
matchStart = Convert<Smi>(newMatchStart);
} else {
// For two smi encoding, the length is negative followed by the
// match start.
const nextEl: Smi = UnsafeCast<Smi>(matchesElements.objects[++i]);
matchStart = nextEl - elSmi;
}
}
// Element represents the matched substring, which is then passed to the
// replace function.
case (elString: String): {
const replacementObj: JSAny =
Call(context, replaceFn, Undefined, elString, matchStart, string);
const replacement: String = ToString_Inline(replacementObj);
matchesElements.objects[i] = replacement;
matchStart += elString.length_smi;
}
case (Object): deferred {
unreachable;
}
}
}
transitioning macro
RegExpReplaceCallableWithExplicitCaptures(implicit context: Context)(
matchesElements: FixedArray, matchesLength: intptr, replaceFn: Callable) {
for (let i: intptr = 0; i < matchesLength; i++) {
const elArray =
Cast<JSArray>(matchesElements.objects[i]) otherwise continue;
// The JSArray is expanded into the function args by Reflect.apply().
// TODO(jgruber): Remove indirection through Call->ReflectApply.
const replacementObj: JSAny = Call(
context, GetReflectApply(), Undefined, replaceFn, Undefined, elArray);
// Overwrite the i'th element in the results with the string
// we got back from the callback function.
matchesElements.objects[i] = ToString_Inline(replacementObj);
}
}
transitioning macro RegExpReplaceFastGlobalCallable(implicit context:
Context)(
regexp: FastJSRegExp, string: String, replaceFn: Callable): String {
regexp.lastIndex = 0;
const kInitialCapacity: intptr = 16;
const kInitialLength: Smi = 0;
const result: Null|JSArray = RegExpExecMultiple(
regexp, string, GetRegExpLastMatchInfo(),
AllocateJSArray(
ElementsKind::PACKED_ELEMENTS, GetFastPackedElementsJSArrayMap(),
kInitialCapacity, kInitialLength));
regexp.lastIndex = 0;
// If no matches, return the subject string.
if (result == Null) return string;
const matches: JSArray = UnsafeCast<JSArray>(result);
const matchesLength: Smi = Cast<Smi>(matches.length) otherwise unreachable;
const matchesLengthInt: intptr = Convert<intptr>(matchesLength);
const matchesElements: FixedArray =
UnsafeCast<FixedArray>(matches.elements);
// Reload last match info since it might have changed.
const nofCaptures: Smi = GetRegExpLastMatchInfo().NumberOfCaptures();
// If the number of captures is two then there are no explicit captures in
// the regexp, just the implicit capture that captures the whole match. In
// this case we can simplify quite a bit and end up with something faster.
if (nofCaptures == 2) {
RegExpReplaceCallableNoExplicitCaptures(
matchesElements, matchesLengthInt, string, replaceFn);
} else {
RegExpReplaceCallableWithExplicitCaptures(
matchesElements, matchesLengthInt, replaceFn);
}
return StringBuilderConcat(matches, matchesLength, string);
}
transitioning macro RegExpReplaceFastString(implicit context: Context)(
regexp: JSRegExp, string: String, replaceString: String): String {
// The fast path is reached only if {receiver} is an unmodified JSRegExp
// instance, {replace_value} is non-callable, and ToString({replace_value})
// does not contain '$', i.e. we're doing a simple string replacement.
let result: String = kEmptyString;
let lastMatchEnd: Smi = 0;
let unicode: bool = false;
const replaceLength: Smi = replaceString.length_smi;
const fastRegexp = UnsafeCast<FastJSRegExp>(regexp);
const global: bool = fastRegexp.global;
if (global) {
unicode = fastRegexp.unicode;
fastRegexp.lastIndex = 0;
}
while (true) {
const match: RegExpMatchInfo =
RegExpPrototypeExecBodyWithoutResultFast(regexp, string)
otherwise break;
const matchStart: Smi = match.GetStartOfCapture(0);
const matchEnd: Smi = match.GetEndOfCapture(0);
// TODO(jgruber): We could skip many of the checks that using SubString
// here entails.
result = result + SubString(string, lastMatchEnd, matchStart);
lastMatchEnd = matchEnd;
if (replaceLength != 0) result = result + replaceString;
// Non-global case ends here after the first replacement.
if (!global) break;
// If match is the empty string, we have to increment lastIndex.
if (matchEnd == matchStart) {
typeswitch (regexp) {
case (fastRegexp: FastJSRegExp): {
fastRegexp.lastIndex =
AdvanceStringIndexFast(string, fastRegexp.lastIndex, unicode);
}
case (Object): {
const lastIndex: JSAny = SlowLoadLastIndex(regexp);
const thisIndex: Number = ToLength_Inline(lastIndex);
const nextIndex: Number =
AdvanceStringIndexSlow(string, thisIndex, unicode);
SlowStoreLastIndex(regexp, nextIndex);
}
}
}
}
return result + SubString(string, lastMatchEnd, string.length_smi);
}
transitioning builtin RegExpReplace(implicit context: Context)(
regexp: FastJSRegExp, string: String, replaceValue: JSAny): String {
// TODO(pwong): Remove assert when all callers (StringPrototypeReplace) are
// from Torque.
assert(Is<FastJSRegExp>(regexp));
// 2. Is {replace_value} callable?
typeswitch (replaceValue) {
case (replaceFn: Callable): {
return regexp.global ?
RegExpReplaceFastGlobalCallable(regexp, string, replaceFn) :
StringReplaceNonGlobalRegExpWithFunction(string, regexp, replaceFn);
}
case (JSAny): {
const stableRegexp: JSRegExp = regexp;
const replaceString: String = ToString_Inline(replaceValue);
try {
// ToString(replaceValue) could potentially change the shape of the
// RegExp object. Recheck that we are still on the fast path and bail
// to runtime otherwise.
const fastRegexp = Cast<FastJSRegExp>(stableRegexp) otherwise Runtime;
if (StringIndexOf(
replaceString, SingleCharacterStringConstant('$'), 0) != -1) {
goto Runtime;
}
return RegExpReplaceFastString(fastRegexp, string, replaceString);
} label Runtime deferred {
return RegExpReplaceRT(context, stableRegexp, string, replaceString);
}
}
}
}
const kRegExpReplaceCalledOnSlowRegExp: constexpr int31
generates 'v8::Isolate::kRegExpReplaceCalledOnSlowRegExp';
transitioning javascript builtin RegExpPrototypeReplace(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSAny {
const methodName: constexpr string = 'RegExp.prototype.@@replace';
// RegExpPrototypeReplace is a bit of a beast - a summary of dispatch logic:
//
// if (!IsFastRegExp(receiver)) CallRuntime(RegExpReplace)
// if (IsCallable(replace)) {
// if (IsGlobal(receiver)) {
// // Called 'fast-path' but contains several runtime calls.
// RegExpReplaceFastGlobalCallable()
// } else {
// CallRuntime(StringReplaceNonGlobalRegExpWithFunction)
// }
// } else {
// if (replace.contains("$")) {
// CallRuntime(RegExpReplace)
// } else {
// RegExpReplaceFastString()
// }
// }
const string: JSAny = arguments[0];
const replaceValue: JSAny = arguments[1];
// Let rx be the this value.
// If Type(rx) is not Object, throw a TypeError exception.
const rx = Cast<JSReceiver>(receiver)
otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, methodName);
// Let S be ? ToString(string).
const s = ToString_Inline(string);
// Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
try {
const fastRx: FastJSRegExp = Cast<FastJSRegExp>(rx) otherwise Runtime;
return RegExpReplace(fastRx, s, replaceValue);
} label Runtime deferred {
IncrementUseCounter(
context, SmiConstant(kRegExpReplaceCalledOnSlowRegExp));
return RegExpReplaceRT(context, rx, s, replaceValue);
}
}
}
transitioning macro
RegExpReplaceCallableWithExplicitCaptures(implicit context: Context)(
matchesElements: FixedArray, matchesLength: intptr, replaceFn: Callable) {
for (let i: intptr = 0; i < matchesLength; i++) {
const elArray =
Cast<JSArray>(matchesElements.objects[i]) otherwise continue;
// The JSArray is expanded into the function args by Reflect.apply().
// TODO(jgruber): Remove indirection through Call->ReflectApply.
const replacementObj: JSAny = Call(
context, GetReflectApply(), Undefined, replaceFn, Undefined, elArray);
// Overwrite the i'th element in the results with the string
// we got back from the callback function.
matchesElements.objects[i] = ToString_Inline(replacementObj);
}
}
transitioning macro RegExpReplaceFastGlobalCallable(implicit context: Context)(
regexp: FastJSRegExp, string: String, replaceFn: Callable): String {
regexp.lastIndex = 0;
const kInitialCapacity: intptr = 16;
const kInitialLength: Smi = 0;
const result: Null|JSArray = RegExpExecMultiple(
regexp, string, GetRegExpLastMatchInfo(),
AllocateJSArray(
ElementsKind::PACKED_ELEMENTS, GetFastPackedElementsJSArrayMap(),
kInitialCapacity, kInitialLength));
regexp.lastIndex = 0;
// If no matches, return the subject string.
if (result == Null) return string;
const matches: JSArray = UnsafeCast<JSArray>(result);
const matchesLength: Smi = Cast<Smi>(matches.length) otherwise unreachable;
const matchesLengthInt: intptr = Convert<intptr>(matchesLength);
const matchesElements: FixedArray = UnsafeCast<FixedArray>(matches.elements);
// Reload last match info since it might have changed.
const nofCaptures: Smi = GetRegExpLastMatchInfo().NumberOfCaptures();
// If the number of captures is two then there are no explicit captures in
// the regexp, just the implicit capture that captures the whole match. In
// this case we can simplify quite a bit and end up with something faster.
if (nofCaptures == 2) {
RegExpReplaceCallableNoExplicitCaptures(
matchesElements, matchesLengthInt, string, replaceFn);
} else {
RegExpReplaceCallableWithExplicitCaptures(
matchesElements, matchesLengthInt, replaceFn);
}
return StringBuilderConcat(matches, matchesLength, string);
}
transitioning macro RegExpReplaceFastString(implicit context: Context)(
regexp: JSRegExp, string: String, replaceString: String): String {
// The fast path is reached only if {receiver} is an unmodified JSRegExp
// instance, {replace_value} is non-callable, and ToString({replace_value})
// does not contain '$', i.e. we're doing a simple string replacement.
let result: String = kEmptyString;
let lastMatchEnd: Smi = 0;
let unicode: bool = false;
const replaceLength: Smi = replaceString.length_smi;
const fastRegexp = UnsafeCast<FastJSRegExp>(regexp);
const global: bool = fastRegexp.global;
if (global) {
unicode = fastRegexp.unicode;
fastRegexp.lastIndex = 0;
}
while (true) {
const match: RegExpMatchInfo =
RegExpPrototypeExecBodyWithoutResultFast(regexp, string)
otherwise break;
const matchStart: Smi = match.GetStartOfCapture(0);
const matchEnd: Smi = match.GetEndOfCapture(0);
// TODO(jgruber): We could skip many of the checks that using SubString
// here entails.
result = result + SubString(string, lastMatchEnd, matchStart);
lastMatchEnd = matchEnd;
if (replaceLength != 0) result = result + replaceString;
// Non-global case ends here after the first replacement.
if (!global) break;
// If match is the empty string, we have to increment lastIndex.
if (matchEnd == matchStart) {
typeswitch (regexp) {
case (fastRegexp: FastJSRegExp): {
fastRegexp.lastIndex =
AdvanceStringIndexFast(string, fastRegexp.lastIndex, unicode);
}
case (Object): {
const lastIndex: JSAny = SlowLoadLastIndex(regexp);
const thisIndex: Number = ToLength_Inline(lastIndex);
const nextIndex: Number =
AdvanceStringIndexSlow(string, thisIndex, unicode);
SlowStoreLastIndex(regexp, nextIndex);
}
}
}
}
return result + SubString(string, lastMatchEnd, string.length_smi);
}
transitioning builtin RegExpReplace(implicit context: Context)(
regexp: FastJSRegExp, string: String, replaceValue: JSAny): String {
// TODO(pwong): Remove assert when all callers (StringPrototypeReplace) are
// from Torque.
assert(Is<FastJSRegExp>(regexp));
// 2. Is {replace_value} callable?
typeswitch (replaceValue) {
case (replaceFn: Callable): {
return regexp.global ?
RegExpReplaceFastGlobalCallable(regexp, string, replaceFn) :
StringReplaceNonGlobalRegExpWithFunction(string, regexp, replaceFn);
}
case (JSAny): {
const stableRegexp: JSRegExp = regexp;
const replaceString: String = ToString_Inline(replaceValue);
try {
// ToString(replaceValue) could potentially change the shape of the
// RegExp object. Recheck that we are still on the fast path and bail
// to runtime otherwise.
const fastRegexp = Cast<FastJSRegExp>(stableRegexp) otherwise Runtime;
if (StringIndexOf(
replaceString, SingleCharacterStringConstant('$'), 0) != -1) {
goto Runtime;
}
return RegExpReplaceFastString(fastRegexp, string, replaceString);
} label Runtime deferred {
return RegExpReplaceRT(context, stableRegexp, string, replaceString);
}
}
}
}
const kRegExpReplaceCalledOnSlowRegExp: constexpr int31
generates 'v8::Isolate::kRegExpReplaceCalledOnSlowRegExp';
transitioning javascript builtin RegExpPrototypeReplace(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
const methodName: constexpr string = 'RegExp.prototype.@@replace';
// RegExpPrototypeReplace is a bit of a beast - a summary of dispatch logic:
//
// if (!IsFastRegExp(receiver)) CallRuntime(RegExpReplace)
// if (IsCallable(replace)) {
// if (IsGlobal(receiver)) {
// // Called 'fast-path' but contains several runtime calls.
// RegExpReplaceFastGlobalCallable()
// } else {
// CallRuntime(StringReplaceNonGlobalRegExpWithFunction)
// }
// } else {
// if (replace.contains("$")) {
// CallRuntime(RegExpReplace)
// } else {
// RegExpReplaceFastString()
// }
// }
const string: JSAny = arguments[0];
const replaceValue: JSAny = arguments[1];
// Let rx be the this value.
// If Type(rx) is not Object, throw a TypeError exception.
const rx = Cast<JSReceiver>(receiver)
otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, methodName);
// Let S be ? ToString(string).
const s = ToString_Inline(string);
// Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
try {
const fastRx: FastJSRegExp = Cast<FastJSRegExp>(rx) otherwise Runtime;
return RegExpReplace(fastRx, s, replaceValue);
} label Runtime deferred {
IncrementUseCounter(context, SmiConstant(kRegExpReplaceCalledOnSlowRegExp));
return RegExpReplaceRT(context, rx, s, replaceValue);
}
}
}

View File

@ -6,102 +6,101 @@
namespace regexp {
transitioning macro
RegExpPrototypeSearchBodyFast(implicit context: Context)(
regexp: JSRegExp, string: String): JSAny {
assert(IsFastRegExpPermissive(regexp));
transitioning macro
RegExpPrototypeSearchBodyFast(implicit context: Context)(
regexp: JSRegExp, string: String): JSAny {
assert(IsFastRegExpPermissive(regexp));
// Grab the initial value of last index.
const previousLastIndex: Smi = FastLoadLastIndex(regexp);
// Grab the initial value of last index.
const previousLastIndex: Smi = FastLoadLastIndex(regexp);
// Ensure last index is 0.
FastStoreLastIndex(regexp, 0);
// Ensure last index is 0.
FastStoreLastIndex(regexp, 0);
// Call exec.
try {
const matchIndices: RegExpMatchInfo =
RegExpPrototypeExecBodyWithoutResultFast(
UnsafeCast<JSRegExp>(regexp), string)
otherwise DidNotMatch;
// Call exec.
try {
const matchIndices: RegExpMatchInfo =
RegExpPrototypeExecBodyWithoutResultFast(
UnsafeCast<JSRegExp>(regexp), string)
otherwise DidNotMatch;
// Successful match.
// Reset last index.
FastStoreLastIndex(regexp, previousLastIndex);
// Return the index of the match.
return UnsafeCast<Smi>(
matchIndices.objects[kRegExpMatchInfoFirstCaptureIndex]);
} label DidNotMatch {
// Reset last index and return -1.
FastStoreLastIndex(regexp, previousLastIndex);
return SmiConstant(-1);
}
}
extern macro RegExpBuiltinsAssembler::BranchIfRegExpResult(
implicit context: Context)(Object): never labels IsUnmodified,
IsModified;
macro
IsRegExpResult(implicit context: Context)(execResult: HeapObject): bool {
BranchIfRegExpResult(execResult) otherwise return true, return false;
}
transitioning macro RegExpPrototypeSearchBodySlow(implicit context: Context)(
regexp: JSReceiver, string: String): JSAny {
// Grab the initial value of last index.
const previousLastIndex = SlowLoadLastIndex(regexp);
const smiZero: Smi = 0;
// Ensure last index is 0.
if (!SameValue(previousLastIndex, smiZero)) {
SlowStoreLastIndex(regexp, smiZero);
}
// Call exec.
const execResult = RegExpExec(regexp, string);
// Reset last index if necessary.
const currentLastIndex = SlowLoadLastIndex(regexp);
if (!SameValue(currentLastIndex, previousLastIndex)) {
SlowStoreLastIndex(regexp, previousLastIndex);
}
// Return -1 if no match was found.
if (execResult == Null) {
return SmiConstant(-1);
}
// Successful match.
// Reset last index.
FastStoreLastIndex(regexp, previousLastIndex);
// Return the index of the match.
const fastExecResult = Cast<JSRegExpResult>(execResult)
otherwise return GetProperty(execResult, 'index');
return fastExecResult.index;
}
// Helper that skips a few initial checks. and assumes...
// 1) receiver is a "fast permissive" RegExp
// 2) pattern is a string
transitioning builtin RegExpSearchFast(implicit context: Context)(
receiver: JSRegExp, string: String): JSAny {
return RegExpPrototypeSearchBodyFast(receiver, string);
}
// ES#sec-regexp.prototype-@@search
// RegExp.prototype [ @@search ] ( string )
transitioning javascript builtin RegExpPrototypeSearch(
js-implicit context: NativeContext,
receiver: JSAny)(string: JSAny): JSAny {
ThrowIfNotJSReceiver(
receiver, MessageTemplate::kIncompatibleMethodReceiver,
'RegExp.prototype.@@search');
const receiver = UnsafeCast<JSReceiver>(receiver);
const string: String = ToString_Inline(string);
if (IsFastRegExpPermissive(receiver)) {
// TODO(pwong): Could be optimized to remove the overhead of calling the
// builtin (at the cost of a larger builtin).
return RegExpSearchFast(UnsafeCast<JSRegExp>(receiver), string);
}
return RegExpPrototypeSearchBodySlow(receiver, string);
return UnsafeCast<Smi>(
matchIndices.objects[kRegExpMatchInfoFirstCaptureIndex]);
} label DidNotMatch {
// Reset last index and return -1.
FastStoreLastIndex(regexp, previousLastIndex);
return SmiConstant(-1);
}
}
extern macro RegExpBuiltinsAssembler::BranchIfRegExpResult(
implicit context: Context)(Object): never labels IsUnmodified,
IsModified;
macro
IsRegExpResult(implicit context: Context)(execResult: HeapObject): bool {
BranchIfRegExpResult(execResult) otherwise return true, return false;
}
transitioning macro RegExpPrototypeSearchBodySlow(implicit context: Context)(
regexp: JSReceiver, string: String): JSAny {
// Grab the initial value of last index.
const previousLastIndex = SlowLoadLastIndex(regexp);
const smiZero: Smi = 0;
// Ensure last index is 0.
if (!SameValue(previousLastIndex, smiZero)) {
SlowStoreLastIndex(regexp, smiZero);
}
// Call exec.
const execResult = RegExpExec(regexp, string);
// Reset last index if necessary.
const currentLastIndex = SlowLoadLastIndex(regexp);
if (!SameValue(currentLastIndex, previousLastIndex)) {
SlowStoreLastIndex(regexp, previousLastIndex);
}
// Return -1 if no match was found.
if (execResult == Null) {
return SmiConstant(-1);
}
// Return the index of the match.
const fastExecResult = Cast<JSRegExpResult>(execResult)
otherwise return GetProperty(execResult, 'index');
return fastExecResult.index;
}
// Helper that skips a few initial checks. and assumes...
// 1) receiver is a "fast permissive" RegExp
// 2) pattern is a string
transitioning builtin RegExpSearchFast(implicit context: Context)(
receiver: JSRegExp, string: String): JSAny {
return RegExpPrototypeSearchBodyFast(receiver, string);
}
// ES#sec-regexp.prototype-@@search
// RegExp.prototype [ @@search ] ( string )
transitioning javascript builtin RegExpPrototypeSearch(
js-implicit context: NativeContext, receiver: JSAny)(string: JSAny): JSAny {
ThrowIfNotJSReceiver(
receiver, MessageTemplate::kIncompatibleMethodReceiver,
'RegExp.prototype.@@search');
const receiver = UnsafeCast<JSReceiver>(receiver);
const string: String = ToString_Inline(string);
if (IsFastRegExpPermissive(receiver)) {
// TODO(pwong): Could be optimized to remove the overhead of calling the
// builtin (at the cost of a larger builtin).
return RegExpSearchFast(UnsafeCast<JSRegExp>(receiver), string);
}
return RegExpPrototypeSearchBodySlow(receiver, string);
}
}

View File

@ -6,22 +6,22 @@
namespace regexp {
// ES6 21.2.5.10.
// ES #sec-get-regexp.prototype.source
transitioning javascript builtin RegExpPrototypeSourceGetter(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
typeswitch (receiver) {
case (receiver: JSRegExp): {
return receiver.source;
}
case (Object): {
}
// ES6 21.2.5.10.
// ES #sec-get-regexp.prototype.source
transitioning javascript builtin RegExpPrototypeSourceGetter(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
typeswitch (receiver) {
case (receiver: JSRegExp): {
return receiver.source;
}
if (!IsReceiverInitialRegExpPrototype(receiver)) {
const methodName: constexpr string = 'RegExp.prototype.source';
ThrowTypeError(MessageTemplate::kRegExpNonRegExp, methodName);
case (Object): {
}
IncrementUseCounter(context, SmiConstant(kRegExpPrototypeSourceGetter));
return '(?:)';
}
if (!IsReceiverInitialRegExpPrototype(receiver)) {
const methodName: constexpr string = 'RegExp.prototype.source';
ThrowTypeError(MessageTemplate::kRegExpNonRegExp, methodName);
}
IncrementUseCounter(context, SmiConstant(kRegExpPrototypeSourceGetter));
return '(?:)';
}
}

View File

@ -5,70 +5,68 @@
#include 'src/builtins/builtins-regexp-gen.h'
namespace runtime {
extern transitioning runtime
RegExpSplit(implicit context: Context)(JSReceiver, String, Object): JSAny;
extern transitioning runtime
RegExpSplit(implicit context: Context)(JSReceiver, String, Object): JSAny;
} // namespace runtime
namespace regexp {
const kMaxValueSmi: constexpr int31
generates 'Smi::kMaxValue';
const kMaxValueSmi: constexpr int31
generates 'Smi::kMaxValue';
extern transitioning macro RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(
implicit context: Context)(JSRegExp, String, Smi): JSArray;
extern transitioning macro RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(
implicit context: Context)(JSRegExp, String, Smi): JSArray;
// Helper that skips a few initial checks.
transitioning builtin
RegExpSplit(implicit context: Context)(
regexp: FastJSRegExp, string: String, limit: JSAny): JSAny {
let sanitizedLimit: Smi;
// Helper that skips a few initial checks.
transitioning builtin
RegExpSplit(implicit context: Context)(
regexp: FastJSRegExp, string: String, limit: JSAny): JSAny {
let sanitizedLimit: Smi;
// We need to be extra-strict and require the given limit to be either
// undefined or a positive smi. We can't call ToUint32(maybe_limit) since
// that might move us onto the slow path, resulting in ordering spec
// violations (see https://crbug.com/801171).
// We need to be extra-strict and require the given limit to be either
// undefined or a positive smi. We can't call ToUint32(maybe_limit) since
// that might move us onto the slow path, resulting in ordering spec
// violations (see https://crbug.com/801171).
if (limit == Undefined) {
// TODO(jgruber): In this case, we can probably avoid generation of limit
// checks in Generate_RegExpPrototypeSplitBody.
sanitizedLimit = SmiConstant(kMaxValueSmi);
} else if (!TaggedIsPositiveSmi(limit)) {
return runtime::RegExpSplit(regexp, string, limit);
} else {
sanitizedLimit = UnsafeCast<Smi>(limit);
}
// Due to specific shortcuts we take on the fast path (specifically, we
// don't allocate a new regexp instance as specced), we need to ensure that
// the given regexp is non-sticky to avoid invalid results. See
// crbug.com/v8/6706.
if (FastFlagGetter(regexp, Flag::kSticky)) {
return runtime::RegExpSplit(regexp, string, sanitizedLimit);
}
// We're good to go on the fast path, which is inlined here.
return RegExpPrototypeSplitBody(regexp, string, sanitizedLimit);
if (limit == Undefined) {
// TODO(jgruber): In this case, we can probably avoid generation of limit
// checks in Generate_RegExpPrototypeSplitBody.
sanitizedLimit = SmiConstant(kMaxValueSmi);
} else if (!TaggedIsPositiveSmi(limit)) {
return runtime::RegExpSplit(regexp, string, limit);
} else {
sanitizedLimit = UnsafeCast<Smi>(limit);
}
// ES#sec-regexp.prototype-@@split
// RegExp.prototype [ @@split ] ( string, limit )
transitioning javascript builtin RegExpPrototypeSplit(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSAny {
ThrowIfNotJSReceiver(
receiver, MessageTemplate::kIncompatibleMethodReceiver,
'RegExp.prototype.@@split');
const receiver = UnsafeCast<JSReceiver>(receiver);
const string: String = ToString_Inline(arguments[0]);
const limit = arguments[1];
// Due to specific shortcuts we take on the fast path (specifically, we
// don't allocate a new regexp instance as specced), we need to ensure that
// the given regexp is non-sticky to avoid invalid results. See
// crbug.com/v8/6706.
// Strict: Reads the flags property.
// TODO(jgruber): Handle slow flag accesses on the fast path and make this
// permissive.
const fastRegExp = Cast<FastJSRegExp>(receiver)
otherwise return runtime::RegExpSplit(receiver, string, limit);
return RegExpSplit(fastRegExp, string, limit);
if (FastFlagGetter(regexp, Flag::kSticky)) {
return runtime::RegExpSplit(regexp, string, sanitizedLimit);
}
// We're good to go on the fast path, which is inlined here.
return RegExpPrototypeSplitBody(regexp, string, sanitizedLimit);
}
// ES#sec-regexp.prototype-@@split
// RegExp.prototype [ @@split ] ( string, limit )
transitioning javascript builtin RegExpPrototypeSplit(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
ThrowIfNotJSReceiver(
receiver, MessageTemplate::kIncompatibleMethodReceiver,
'RegExp.prototype.@@split');
const receiver = UnsafeCast<JSReceiver>(receiver);
const string: String = ToString_Inline(arguments[0]);
const limit = arguments[1];
// Strict: Reads the flags property.
// TODO(jgruber): Handle slow flag accesses on the fast path and make this
// permissive.
const fastRegExp = Cast<FastJSRegExp>(receiver)
otherwise return runtime::RegExpSplit(receiver, string, limit);
return RegExpSplit(fastRegExp, string, limit);
}
}

View File

@ -6,30 +6,29 @@
namespace regexp {
// ES#sec-regexp.prototype.test
// RegExp.prototype.test ( S )
transitioning javascript builtin RegExpPrototypeTest(
js-implicit context: NativeContext,
receiver: JSAny)(string: JSAny): JSAny {
const methodName: constexpr string = 'RegExp.prototype.test';
const receiver = Cast<JSReceiver>(receiver)
otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, methodName);
const str: String = ToString_Inline(string);
if (IsFastRegExpPermissive(receiver)) {
RegExpPrototypeExecBodyWithoutResultFast(
UnsafeCast<JSRegExp>(receiver), str)
otherwise return False;
return True;
}
const matchIndices = RegExpExec(receiver, str);
return SelectBooleanConstant(matchIndices != Null);
}
transitioning builtin RegExpPrototypeTestFast(implicit context: Context)(
receiver: JSRegExp, string: String): Object {
RegExpPrototypeExecBodyWithoutResultFast(receiver, string)
// ES#sec-regexp.prototype.test
// RegExp.prototype.test ( S )
transitioning javascript builtin RegExpPrototypeTest(
js-implicit context: NativeContext, receiver: JSAny)(string: JSAny): JSAny {
const methodName: constexpr string = 'RegExp.prototype.test';
const receiver = Cast<JSReceiver>(receiver)
otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, methodName);
const str: String = ToString_Inline(string);
if (IsFastRegExpPermissive(receiver)) {
RegExpPrototypeExecBodyWithoutResultFast(
UnsafeCast<JSRegExp>(receiver), str)
otherwise return False;
return True;
}
const matchIndices = RegExpExec(receiver, str);
return SelectBooleanConstant(matchIndices != Null);
}
transitioning builtin RegExpPrototypeTestFast(implicit context: Context)(
receiver: JSRegExp, string: String): Object {
RegExpPrototypeExecBodyWithoutResultFast(receiver, string)
otherwise return False;
return True;
}
}

View File

@ -6,417 +6,415 @@
namespace regexp {
extern macro RegExpBuiltinsAssembler::BranchIfFastRegExp_Strict(
implicit context: Context)(HeapObject): never labels IsFast,
IsSlow;
macro IsFastRegExpStrict(implicit context: Context)(o: HeapObject): bool {
BranchIfFastRegExp_Strict(o) otherwise return true, return false;
}
extern macro RegExpBuiltinsAssembler::BranchIfFastRegExp_Strict(
implicit context: Context)(HeapObject): never labels IsFast,
IsSlow;
macro IsFastRegExpStrict(implicit context: Context)(o: HeapObject): bool {
BranchIfFastRegExp_Strict(o) otherwise return true, return false;
}
extern macro RegExpBuiltinsAssembler::BranchIfFastRegExp_Permissive(
implicit context: Context)(HeapObject): never labels IsFast,
IsSlow;
extern macro RegExpBuiltinsAssembler::BranchIfFastRegExp_Permissive(
implicit context: Context)(HeapObject): never labels IsFast,
IsSlow;
@export
macro IsFastRegExpPermissive(implicit context: Context)(o: HeapObject): bool {
BranchIfFastRegExp_Permissive(o) otherwise return true, return false;
}
@export
macro IsFastRegExpPermissive(implicit context: Context)(o: HeapObject): bool {
BranchIfFastRegExp_Permissive(o) otherwise return true, return false;
}
// ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
@export
transitioning macro RegExpExec(implicit context: Context)(
receiver: JSReceiver, string: String): JSAny {
// Take the slow path of fetching the exec property, calling it, and
// verifying its return value.
// ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
@export
transitioning macro RegExpExec(implicit context: Context)(
receiver: JSReceiver, string: String): JSAny {
// Take the slow path of fetching the exec property, calling it, and
// verifying its return value.
const exec = GetProperty(receiver, 'exec');
const exec = GetProperty(receiver, 'exec');
// Is {exec} callable?
typeswitch (exec) {
case (execCallable: Callable): {
const result = Call(context, execCallable, receiver, string);
if (result != Null) {
ThrowIfNotJSReceiver(
result, MessageTemplate::kInvalidRegExpExecResult, '');
}
return result;
}
case (Object): {
const regexp = Cast<JSRegExp>(receiver) otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver,
'RegExp.prototype.exec', receiver);
return RegExpPrototypeExecSlow(regexp, string);
// Is {exec} callable?
typeswitch (exec) {
case (execCallable: Callable): {
const result = Call(context, execCallable, receiver, string);
if (result != Null) {
ThrowIfNotJSReceiver(
result, MessageTemplate::kInvalidRegExpExecResult, '');
}
return result;
}
}
extern macro RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
implicit context: Context)(JSRegExp, RegExpMatchInfo, String, Number):
JSRegExpResult;
const kGlobalOrSticky: constexpr int31
generates 'JSRegExp::kGlobal | JSRegExp::kSticky';
extern macro RegExpBuiltinsAssembler::RegExpExecInternal(
implicit context: Context)(JSRegExp, String, Number, RegExpMatchInfo):
HeapObject;
// ES#sec-regexp.prototype.exec
// RegExp.prototype.exec ( string )
// Implements the core of RegExp.prototype.exec but without actually
// constructing the JSRegExpResult. Returns a fixed array containing match
// indices as returned by RegExpExecStub on successful match, and jumps to
// IfDidNotMatch otherwise.
transitioning macro RegExpPrototypeExecBodyWithoutResult(implicit context:
Context)(
regexp: JSRegExp, string: String, regexpLastIndex: Number,
isFastPath: constexpr bool): RegExpMatchInfo labels IfDidNotMatch {
if (isFastPath) {
assert(HasInitialRegExpMap(regexp));
} else {
IncrementUseCounter(context, SmiConstant(kRegExpExecCalledOnSlowRegExp));
}
let lastIndex = regexpLastIndex;
// Check whether the regexp is global or sticky, which determines whether we
// update last index later on.
const flags = UnsafeCast<Smi>(regexp.flags);
const isGlobalOrSticky: intptr =
SmiUntag(flags) & IntPtrConstant(kGlobalOrSticky);
const shouldUpdateLastIndex: bool = isGlobalOrSticky != 0;
// Grab and possibly update last index.
if (shouldUpdateLastIndex) {
if (!TaggedIsSmi(lastIndex) || (lastIndex > string.length_smi)) {
StoreLastIndex(regexp, SmiConstant(0), isFastPath);
goto IfDidNotMatch;
}
} else {
lastIndex = SmiConstant(0);
}
const lastMatchInfo: RegExpMatchInfo = GetRegExpLastMatchInfo();
const matchIndices =
RegExpExecInternal(regexp, string, lastIndex, lastMatchInfo);
// {match_indices} is either null or the RegExpMatchInfo array.
// Return early if exec failed, possibly updating last index.
if (matchIndices != Null) {
const matchIndicesRegExpMatchInfo =
UnsafeCast<RegExpMatchInfo>(matchIndices);
if (shouldUpdateLastIndex) {
// Update the new last index from {match_indices}.
const newLastIndex: Smi =
matchIndicesRegExpMatchInfo.GetEndOfCapture(0);
StoreLastIndex(regexp, newLastIndex, isFastPath);
}
return matchIndicesRegExpMatchInfo;
}
if (shouldUpdateLastIndex) {
StoreLastIndex(regexp, SmiConstant(0), isFastPath);
}
goto IfDidNotMatch;
}
@export
transitioning macro RegExpPrototypeExecBodyWithoutResultFast(
implicit context: Context)(regexp: JSRegExp, string: String):
RegExpMatchInfo labels IfDidNotMatch {
const lastIndex = LoadLastIndexAsLength(regexp, true);
return RegExpPrototypeExecBodyWithoutResult(regexp, string, lastIndex, true)
otherwise IfDidNotMatch;
}
transitioning macro RegExpPrototypeExecBodyWithoutResultFast(
implicit context:
Context)(regexp: JSRegExp, string: String, lastIndex: Number):
RegExpMatchInfo labels IfDidNotMatch {
return RegExpPrototypeExecBodyWithoutResult(regexp, string, lastIndex, true)
otherwise IfDidNotMatch;
}
// ES#sec-regexp.prototype.exec
// RegExp.prototype.exec ( string )
transitioning macro RegExpPrototypeExecBody(implicit context: Context)(
receiver: JSReceiver, string: String, isFastPath: constexpr bool): JSAny {
let regexp: JSRegExp;
if constexpr (isFastPath) {
regexp = UnsafeCast<JSRegExp>(receiver);
} else {
regexp = Cast<JSRegExp>(receiver) otherwise ThrowTypeError(
case (Object): {
const regexp = Cast<JSRegExp>(receiver) otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, 'RegExp.prototype.exec',
receiver);
return RegExpPrototypeExecSlow(regexp, string);
}
const lastIndex = LoadLastIndexAsLength(regexp, isFastPath);
const matchIndices: RegExpMatchInfo = RegExpPrototypeExecBodyWithoutResult(
regexp, string, lastIndex, isFastPath) otherwise return Null;
return ConstructNewResultFromMatchInfo(
regexp, matchIndices, string, lastIndex);
}
macro LoadRegExpFunction(implicit context: Context)(
nativeContext: NativeContext): JSFunction {
return UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::REGEXP_FUNCTION_INDEX]);
}
// Note this doesn't guarantee const-ness of object properties, just
// unchanged object layout.
macro HasInitialRegExpMap(implicit context: Context)(o: HeapObject): bool {
const nativeContext = LoadNativeContext(context);
const function = LoadRegExpFunction(nativeContext);
const initialMap = UnsafeCast<Map>(function.prototype_or_initial_map);
return initialMap == o.map;
}
macro IsReceiverInitialRegExpPrototype(implicit context:
Context)(receiver: Object): bool {
const nativeContext = LoadNativeContext(context);
const regexpFun = LoadRegExpFunction(nativeContext);
const initialMap = UnsafeCast<Map>(regexpFun.prototype_or_initial_map);
const initialPrototype: HeapObject = initialMap.prototype;
return TaggedEqual(receiver, initialPrototype);
}
extern enum Flag constexpr 'JSRegExp::Flag' {
kNone,
kGlobal,
kIgnoreCase,
kMultiline,
kSticky,
kUnicode,
kDotAll,
kInvalid
}
const kRegExpPrototypeOldFlagGetter: constexpr int31
generates 'v8::Isolate::kRegExpPrototypeOldFlagGetter';
const kRegExpPrototypeStickyGetter: constexpr int31
generates 'v8::Isolate::kRegExpPrototypeStickyGetter';
const kRegExpPrototypeUnicodeGetter: constexpr int31
generates 'v8::Isolate::kRegExpPrototypeUnicodeGetter';
extern macro RegExpBuiltinsAssembler::FastFlagGetter(
JSRegExp, constexpr Flag): bool;
extern runtime IncrementUseCounter(Context, Smi): void;
macro FlagGetter(implicit context: Context)(
receiver: Object, flag: constexpr Flag, counter: constexpr int31,
methodName: constexpr string): JSAny {
typeswitch (receiver) {
case (receiver: JSRegExp): {
return SelectBooleanConstant(FastFlagGetter(receiver, flag));
}
case (Object): {
}
}
if (!IsReceiverInitialRegExpPrototype(receiver)) {
ThrowTypeError(MessageTemplate::kRegExpNonRegExp, methodName);
}
if constexpr (counter != -1) {
IncrementUseCounter(context, SmiConstant(counter));
}
return Undefined;
}
// ES6 21.2.5.4.
// ES #sec-get-regexp.prototype.global
transitioning javascript builtin RegExpPrototypeGlobalGetter(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
return FlagGetter(
receiver, Flag::kGlobal, kRegExpPrototypeOldFlagGetter,
'RegExp.prototype.global');
}
// ES6 21.2.5.5.
// ES #sec-get-regexp.prototype.ignorecase
transitioning javascript builtin RegExpPrototypeIgnoreCaseGetter(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
return FlagGetter(
receiver, Flag::kIgnoreCase, kRegExpPrototypeOldFlagGetter,
'RegExp.prototype.ignoreCase');
}
// ES6 21.2.5.7.
// ES #sec-get-regexp.prototype.multiline
transitioning javascript builtin RegExpPrototypeMultilineGetter(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
return FlagGetter(
receiver, Flag::kMultiline, kRegExpPrototypeOldFlagGetter,
'RegExp.prototype.multiline');
}
// ES #sec-get-regexp.prototype.dotAll
transitioning javascript builtin RegExpPrototypeDotAllGetter(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
const kNoCounter: constexpr int31 = -1;
return FlagGetter(
receiver, Flag::kDotAll, kNoCounter, 'RegExp.prototype.dotAll');
}
// ES6 21.2.5.12.
// ES #sec-get-regexp.prototype.sticky
transitioning javascript builtin RegExpPrototypeStickyGetter(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
return FlagGetter(
receiver, Flag::kSticky, kRegExpPrototypeStickyGetter,
'RegExp.prototype.sticky');
}
// ES6 21.2.5.15.
// ES #sec-get-regexp.prototype.unicode
transitioning javascript builtin RegExpPrototypeUnicodeGetter(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
return FlagGetter(
receiver, Flag::kUnicode, kRegExpPrototypeUnicodeGetter,
'RegExp.prototype.unicode');
}
extern transitioning macro
RegExpBuiltinsAssembler::FlagsGetter(implicit context: Context)(
Object, constexpr bool): String;
transitioning macro
FastFlagsGetter(implicit context: Context)(receiver: FastJSRegExp): String {
return FlagsGetter(receiver, true);
}
transitioning macro SlowFlagsGetter(implicit context:
Context)(receiver: JSAny): String {
return FlagsGetter(receiver, false);
}
// ES #sec-get-regexp.prototype.flags
// TFJ(RegExpPrototypeFlagsGetter, 0, kReceiver) \
transitioning javascript builtin RegExpPrototypeFlagsGetter(
js-implicit context: NativeContext, receiver: JSAny)(): String {
ThrowIfNotJSReceiver(
receiver, MessageTemplate::kRegExpNonObject, 'RegExp.prototype.flags');
// The check is strict because the following code relies on individual flag
// getters on the regexp prototype (e.g.: global, sticky, ...). We don't
// bother to check these individually.
const fastRegexp = Cast<FastJSRegExp>(receiver)
otherwise return SlowFlagsGetter(receiver);
return FastFlagsGetter(fastRegexp);
}
extern transitioning macro RegExpBuiltinsAssembler::SlowLoadLastIndex(
implicit context: Context)(JSAny): JSAny;
extern transitioning macro RegExpBuiltinsAssembler::SlowStoreLastIndex(
implicit context: Context)(JSAny, JSAny): void;
extern macro RegExpBuiltinsAssembler::FastLoadLastIndex(JSRegExp): Smi;
extern macro RegExpBuiltinsAssembler::FastStoreLastIndex(JSRegExp, Smi): void;
@export
transitioning macro LoadLastIndex(implicit context: Context)(
regexp: JSAny, isFastPath: constexpr bool): JSAny {
return isFastPath ? FastLoadLastIndex(UnsafeCast<JSRegExp>(regexp)) :
SlowLoadLastIndex(regexp);
}
@export
transitioning macro LoadLastIndexAsLength(implicit context: Context)(
regexp: JSRegExp, isFastPath: constexpr bool): Number {
const lastIndex = LoadLastIndex(regexp, isFastPath);
if (isFastPath) {
// ToLength on a positive smi is a nop and can be skipped.
return UnsafeCast<PositiveSmi>(lastIndex);
} else {
// Omit ToLength if last_index is a non-negative smi.
typeswitch (lastIndex) {
case (i: PositiveSmi): {
return i;
}
case (o: JSAny): {
return ToLength_Inline(o);
}
}
}
}
@export
transitioning macro StoreLastIndex(implicit context: Context)(
regexp: JSAny, value: Number, isFastPath: constexpr bool): void {
if (isFastPath) {
FastStoreLastIndex(UnsafeCast<JSRegExp>(regexp), UnsafeCast<Smi>(value));
} else {
SlowStoreLastIndex(regexp, value);
}
}
extern builtin
StringIndexOf(implicit context: Context)(String, String, Smi): Smi;
extern macro RegExpBuiltinsAssembler::AdvanceStringIndex(
String, Number, bool, constexpr bool): Number;
extern macro
RegExpBuiltinsAssembler::AdvanceStringIndexFast(String, Smi, bool): Smi;
extern macro
RegExpBuiltinsAssembler::AdvanceStringIndexSlow(String, Number, bool): Smi;
type UseCounterFeature extends int31
constexpr 'v8::Isolate::UseCounterFeature';
const kRegExpMatchIsTrueishOnNonJSRegExp: constexpr UseCounterFeature
generates 'v8::Isolate::kRegExpMatchIsTrueishOnNonJSRegExp';
const kRegExpMatchIsFalseishOnJSRegExp: constexpr UseCounterFeature
generates 'v8::Isolate::kRegExpMatchIsFalseishOnJSRegExp';
const kRegExpPrototypeSourceGetter: constexpr UseCounterFeature
generates 'v8::Isolate::kRegExpPrototypeSourceGetter';
const kRegExpExecCalledOnSlowRegExp: constexpr UseCounterFeature
generates 'v8::Isolate::kRegExpExecCalledOnSlowRegExp';
// ES#sec-isregexp IsRegExp ( argument )
@export
transitioning macro IsRegExp(implicit context: Context)(obj: JSAny): bool {
const receiver = Cast<JSReceiver>(obj) otherwise return false;
// Check @match.
const value = GetProperty(receiver, MatchSymbolConstant());
if (value == Undefined) {
return Is<JSRegExp>(receiver);
}
assert(value != Undefined);
// The common path. Symbol.match exists, equals the RegExpPrototypeMatch
// function (and is thus trueish), and the receiver is a JSRegExp.
if (ToBoolean(value)) {
if (!Is<JSRegExp>(receiver)) {
IncrementUseCounter(
context, SmiConstant(kRegExpMatchIsTrueishOnNonJSRegExp));
}
return true;
}
assert(!ToBoolean(value));
if (Is<JSRegExp>(receiver)) {
IncrementUseCounter(
context, SmiConstant(kRegExpMatchIsFalseishOnJSRegExp));
}
return false;
}
extern runtime RegExpInitializeAndCompile(Context, JSRegExp, String, String):
JSAny;
@export
transitioning macro RegExpCreate(implicit context: Context)(
nativeContext: NativeContext, maybeString: JSAny, flags: String): JSAny {
const regexpFun = LoadRegExpFunction(nativeContext);
const initialMap = UnsafeCast<Map>(regexpFun.prototype_or_initial_map);
return RegExpCreate(initialMap, maybeString, flags);
}
@export
transitioning macro RegExpCreate(implicit context: Context)(
initialMap: Map, maybeString: JSAny, flags: String): JSAny {
const pattern: String =
maybeString == Undefined ? kEmptyString : ToString_Inline(maybeString);
const regexp =
UnsafeCast<JSRegExp>(AllocateFastOrSlowJSObjectFromMap(initialMap));
return RegExpInitializeAndCompile(context, regexp, pattern, flags);
}
}
extern macro RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
implicit context: Context)(
JSRegExp, RegExpMatchInfo, String, Number): JSRegExpResult;
const kGlobalOrSticky: constexpr int31
generates 'JSRegExp::kGlobal | JSRegExp::kSticky';
extern macro RegExpBuiltinsAssembler::RegExpExecInternal(
implicit context: Context)(
JSRegExp, String, Number, RegExpMatchInfo): HeapObject;
// ES#sec-regexp.prototype.exec
// RegExp.prototype.exec ( string )
// Implements the core of RegExp.prototype.exec but without actually
// constructing the JSRegExpResult. Returns a fixed array containing match
// indices as returned by RegExpExecStub on successful match, and jumps to
// IfDidNotMatch otherwise.
transitioning macro RegExpPrototypeExecBodyWithoutResult(
implicit context: Context)(
regexp: JSRegExp, string: String, regexpLastIndex: Number,
isFastPath: constexpr bool): RegExpMatchInfo labels IfDidNotMatch {
if (isFastPath) {
assert(HasInitialRegExpMap(regexp));
} else {
IncrementUseCounter(context, SmiConstant(kRegExpExecCalledOnSlowRegExp));
}
let lastIndex = regexpLastIndex;
// Check whether the regexp is global or sticky, which determines whether we
// update last index later on.
const flags = UnsafeCast<Smi>(regexp.flags);
const isGlobalOrSticky: intptr =
SmiUntag(flags) & IntPtrConstant(kGlobalOrSticky);
const shouldUpdateLastIndex: bool = isGlobalOrSticky != 0;
// Grab and possibly update last index.
if (shouldUpdateLastIndex) {
if (!TaggedIsSmi(lastIndex) || (lastIndex > string.length_smi)) {
StoreLastIndex(regexp, SmiConstant(0), isFastPath);
goto IfDidNotMatch;
}
} else {
lastIndex = SmiConstant(0);
}
const lastMatchInfo: RegExpMatchInfo = GetRegExpLastMatchInfo();
const matchIndices =
RegExpExecInternal(regexp, string, lastIndex, lastMatchInfo);
// {match_indices} is either null or the RegExpMatchInfo array.
// Return early if exec failed, possibly updating last index.
if (matchIndices != Null) {
const matchIndicesRegExpMatchInfo =
UnsafeCast<RegExpMatchInfo>(matchIndices);
if (shouldUpdateLastIndex) {
// Update the new last index from {match_indices}.
const newLastIndex: Smi = matchIndicesRegExpMatchInfo.GetEndOfCapture(0);
StoreLastIndex(regexp, newLastIndex, isFastPath);
}
return matchIndicesRegExpMatchInfo;
}
if (shouldUpdateLastIndex) {
StoreLastIndex(regexp, SmiConstant(0), isFastPath);
}
goto IfDidNotMatch;
}
@export
transitioning macro RegExpPrototypeExecBodyWithoutResultFast(
implicit context: Context)(regexp: JSRegExp, string: String):
RegExpMatchInfo labels IfDidNotMatch {
const lastIndex = LoadLastIndexAsLength(regexp, true);
return RegExpPrototypeExecBodyWithoutResult(regexp, string, lastIndex, true)
otherwise IfDidNotMatch;
}
transitioning macro RegExpPrototypeExecBodyWithoutResultFast(
implicit context: Context)(
regexp: JSRegExp, string: String,
lastIndex: Number): RegExpMatchInfo labels IfDidNotMatch {
return RegExpPrototypeExecBodyWithoutResult(regexp, string, lastIndex, true)
otherwise IfDidNotMatch;
}
// ES#sec-regexp.prototype.exec
// RegExp.prototype.exec ( string )
transitioning macro RegExpPrototypeExecBody(implicit context: Context)(
receiver: JSReceiver, string: String, isFastPath: constexpr bool): JSAny {
let regexp: JSRegExp;
if constexpr (isFastPath) {
regexp = UnsafeCast<JSRegExp>(receiver);
} else {
regexp = Cast<JSRegExp>(receiver) otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, 'RegExp.prototype.exec',
receiver);
}
const lastIndex = LoadLastIndexAsLength(regexp, isFastPath);
const matchIndices: RegExpMatchInfo = RegExpPrototypeExecBodyWithoutResult(
regexp, string, lastIndex, isFastPath) otherwise return Null;
return ConstructNewResultFromMatchInfo(
regexp, matchIndices, string, lastIndex);
}
macro LoadRegExpFunction(implicit context: Context)(
nativeContext: NativeContext): JSFunction {
return UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::REGEXP_FUNCTION_INDEX]);
}
// Note this doesn't guarantee const-ness of object properties, just
// unchanged object layout.
macro HasInitialRegExpMap(implicit context: Context)(o: HeapObject): bool {
const nativeContext = LoadNativeContext(context);
const function = LoadRegExpFunction(nativeContext);
const initialMap = UnsafeCast<Map>(function.prototype_or_initial_map);
return initialMap == o.map;
}
macro IsReceiverInitialRegExpPrototype(implicit context: Context)(
receiver: Object): bool {
const nativeContext = LoadNativeContext(context);
const regexpFun = LoadRegExpFunction(nativeContext);
const initialMap = UnsafeCast<Map>(regexpFun.prototype_or_initial_map);
const initialPrototype: HeapObject = initialMap.prototype;
return TaggedEqual(receiver, initialPrototype);
}
extern enum Flag constexpr 'JSRegExp::Flag' {
kNone,
kGlobal,
kIgnoreCase,
kMultiline,
kSticky,
kUnicode,
kDotAll,
kInvalid
}
const kRegExpPrototypeOldFlagGetter: constexpr int31
generates 'v8::Isolate::kRegExpPrototypeOldFlagGetter';
const kRegExpPrototypeStickyGetter: constexpr int31
generates 'v8::Isolate::kRegExpPrototypeStickyGetter';
const kRegExpPrototypeUnicodeGetter: constexpr int31
generates 'v8::Isolate::kRegExpPrototypeUnicodeGetter';
extern macro RegExpBuiltinsAssembler::FastFlagGetter(
JSRegExp, constexpr Flag): bool;
extern runtime IncrementUseCounter(Context, Smi): void;
macro FlagGetter(implicit context: Context)(
receiver: Object, flag: constexpr Flag, counter: constexpr int31,
methodName: constexpr string): JSAny {
typeswitch (receiver) {
case (receiver: JSRegExp): {
return SelectBooleanConstant(FastFlagGetter(receiver, flag));
}
case (Object): {
}
}
if (!IsReceiverInitialRegExpPrototype(receiver)) {
ThrowTypeError(MessageTemplate::kRegExpNonRegExp, methodName);
}
if constexpr (counter != -1) {
IncrementUseCounter(context, SmiConstant(counter));
}
return Undefined;
}
// ES6 21.2.5.4.
// ES #sec-get-regexp.prototype.global
transitioning javascript builtin RegExpPrototypeGlobalGetter(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
return FlagGetter(
receiver, Flag::kGlobal, kRegExpPrototypeOldFlagGetter,
'RegExp.prototype.global');
}
// ES6 21.2.5.5.
// ES #sec-get-regexp.prototype.ignorecase
transitioning javascript builtin RegExpPrototypeIgnoreCaseGetter(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
return FlagGetter(
receiver, Flag::kIgnoreCase, kRegExpPrototypeOldFlagGetter,
'RegExp.prototype.ignoreCase');
}
// ES6 21.2.5.7.
// ES #sec-get-regexp.prototype.multiline
transitioning javascript builtin RegExpPrototypeMultilineGetter(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
return FlagGetter(
receiver, Flag::kMultiline, kRegExpPrototypeOldFlagGetter,
'RegExp.prototype.multiline');
}
// ES #sec-get-regexp.prototype.dotAll
transitioning javascript builtin RegExpPrototypeDotAllGetter(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
const kNoCounter: constexpr int31 = -1;
return FlagGetter(
receiver, Flag::kDotAll, kNoCounter, 'RegExp.prototype.dotAll');
}
// ES6 21.2.5.12.
// ES #sec-get-regexp.prototype.sticky
transitioning javascript builtin RegExpPrototypeStickyGetter(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
return FlagGetter(
receiver, Flag::kSticky, kRegExpPrototypeStickyGetter,
'RegExp.prototype.sticky');
}
// ES6 21.2.5.15.
// ES #sec-get-regexp.prototype.unicode
transitioning javascript builtin RegExpPrototypeUnicodeGetter(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
return FlagGetter(
receiver, Flag::kUnicode, kRegExpPrototypeUnicodeGetter,
'RegExp.prototype.unicode');
}
extern transitioning macro
RegExpBuiltinsAssembler::FlagsGetter(implicit context: Context)(
Object, constexpr bool): String;
transitioning macro
FastFlagsGetter(implicit context: Context)(receiver: FastJSRegExp): String {
return FlagsGetter(receiver, true);
}
transitioning macro SlowFlagsGetter(implicit context: Context)(receiver: JSAny):
String {
return FlagsGetter(receiver, false);
}
// ES #sec-get-regexp.prototype.flags
// TFJ(RegExpPrototypeFlagsGetter, 0, kReceiver) \
transitioning javascript builtin RegExpPrototypeFlagsGetter(
js-implicit context: NativeContext, receiver: JSAny)(): String {
ThrowIfNotJSReceiver(
receiver, MessageTemplate::kRegExpNonObject, 'RegExp.prototype.flags');
// The check is strict because the following code relies on individual flag
// getters on the regexp prototype (e.g.: global, sticky, ...). We don't
// bother to check these individually.
const fastRegexp = Cast<FastJSRegExp>(receiver)
otherwise return SlowFlagsGetter(receiver);
return FastFlagsGetter(fastRegexp);
}
extern transitioning macro RegExpBuiltinsAssembler::SlowLoadLastIndex(
implicit context: Context)(JSAny): JSAny;
extern transitioning macro RegExpBuiltinsAssembler::SlowStoreLastIndex(
implicit context: Context)(JSAny, JSAny): void;
extern macro RegExpBuiltinsAssembler::FastLoadLastIndex(JSRegExp): Smi;
extern macro RegExpBuiltinsAssembler::FastStoreLastIndex(JSRegExp, Smi): void;
@export
transitioning macro LoadLastIndex(implicit context: Context)(
regexp: JSAny, isFastPath: constexpr bool): JSAny {
return isFastPath ? FastLoadLastIndex(UnsafeCast<JSRegExp>(regexp)) :
SlowLoadLastIndex(regexp);
}
@export
transitioning macro LoadLastIndexAsLength(implicit context: Context)(
regexp: JSRegExp, isFastPath: constexpr bool): Number {
const lastIndex = LoadLastIndex(regexp, isFastPath);
if (isFastPath) {
// ToLength on a positive smi is a nop and can be skipped.
return UnsafeCast<PositiveSmi>(lastIndex);
} else {
// Omit ToLength if last_index is a non-negative smi.
typeswitch (lastIndex) {
case (i: PositiveSmi): {
return i;
}
case (o: JSAny): {
return ToLength_Inline(o);
}
}
}
}
@export
transitioning macro StoreLastIndex(implicit context: Context)(
regexp: JSAny, value: Number, isFastPath: constexpr bool): void {
if (isFastPath) {
FastStoreLastIndex(UnsafeCast<JSRegExp>(regexp), UnsafeCast<Smi>(value));
} else {
SlowStoreLastIndex(regexp, value);
}
}
extern builtin
StringIndexOf(implicit context: Context)(String, String, Smi): Smi;
extern macro RegExpBuiltinsAssembler::AdvanceStringIndex(
String, Number, bool, constexpr bool): Number;
extern macro
RegExpBuiltinsAssembler::AdvanceStringIndexFast(String, Smi, bool): Smi;
extern macro
RegExpBuiltinsAssembler::AdvanceStringIndexSlow(String, Number, bool): Smi;
type UseCounterFeature extends int31
constexpr 'v8::Isolate::UseCounterFeature';
const kRegExpMatchIsTrueishOnNonJSRegExp: constexpr UseCounterFeature
generates 'v8::Isolate::kRegExpMatchIsTrueishOnNonJSRegExp';
const kRegExpMatchIsFalseishOnJSRegExp: constexpr UseCounterFeature
generates 'v8::Isolate::kRegExpMatchIsFalseishOnJSRegExp';
const kRegExpPrototypeSourceGetter: constexpr UseCounterFeature
generates 'v8::Isolate::kRegExpPrototypeSourceGetter';
const kRegExpExecCalledOnSlowRegExp: constexpr UseCounterFeature
generates 'v8::Isolate::kRegExpExecCalledOnSlowRegExp';
// ES#sec-isregexp IsRegExp ( argument )
@export
transitioning macro IsRegExp(implicit context: Context)(obj: JSAny): bool {
const receiver = Cast<JSReceiver>(obj) otherwise return false;
// Check @match.
const value = GetProperty(receiver, MatchSymbolConstant());
if (value == Undefined) {
return Is<JSRegExp>(receiver);
}
assert(value != Undefined);
// The common path. Symbol.match exists, equals the RegExpPrototypeMatch
// function (and is thus trueish), and the receiver is a JSRegExp.
if (ToBoolean(value)) {
if (!Is<JSRegExp>(receiver)) {
IncrementUseCounter(
context, SmiConstant(kRegExpMatchIsTrueishOnNonJSRegExp));
}
return true;
}
assert(!ToBoolean(value));
if (Is<JSRegExp>(receiver)) {
IncrementUseCounter(context, SmiConstant(kRegExpMatchIsFalseishOnJSRegExp));
}
return false;
}
extern runtime RegExpInitializeAndCompile(
Context, JSRegExp, String, String): JSAny;
@export
transitioning macro RegExpCreate(implicit context: Context)(
nativeContext: NativeContext, maybeString: JSAny, flags: String): JSAny {
const regexpFun = LoadRegExpFunction(nativeContext);
const initialMap = UnsafeCast<Map>(regexpFun.prototype_or_initial_map);
return RegExpCreate(initialMap, maybeString, flags);
}
@export
transitioning macro RegExpCreate(implicit context: Context)(
initialMap: Map, maybeString: JSAny, flags: String): JSAny {
const pattern: String =
maybeString == Undefined ? kEmptyString : ToString_Inline(maybeString);
const regexp =
UnsafeCast<JSRegExp>(AllocateFastOrSlowJSObjectFromMap(initialMap));
return RegExpInitializeAndCompile(context, regexp, pattern, flags);
}
}

View File

@ -3,78 +3,78 @@
// found in the LICENSE file.
namespace string {
macro TryFastStringCompareSequence(
string: String, searchStr: String, start: uintptr,
searchLength: uintptr): Boolean labels Slow {
const directString = Cast<DirectString>(string) otherwise Slow;
const directSearchStr = Cast<DirectString>(searchStr) otherwise Slow;
macro TryFastStringCompareSequence(
string: String, searchStr: String, start: uintptr,
searchLength: uintptr): Boolean labels Slow {
const directString = Cast<DirectString>(string) otherwise Slow;
const directSearchStr = Cast<DirectString>(searchStr) otherwise Slow;
let searchIndex: uintptr = 0;
let stringIndex: uintptr = start;
let searchIndex: uintptr = 0;
let stringIndex: uintptr = start;
while (searchIndex < searchLength) {
if (StringCharCodeAt(directSearchStr, searchIndex) !=
StringCharCodeAt(directString, stringIndex)) {
return False;
}
searchIndex++;
stringIndex++;
while (searchIndex < searchLength) {
if (StringCharCodeAt(directSearchStr, searchIndex) !=
StringCharCodeAt(directString, stringIndex)) {
return False;
}
return True;
searchIndex++;
stringIndex++;
}
return True;
}
// https://tc39.github.io/ecma262/#sec-string.prototype.endswith
transitioning javascript builtin StringPrototypeEndsWith(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): Boolean {
const searchString: JSAny = arguments[0];
const endPosition: JSAny = arguments[1];
const kBuiltinName: constexpr string = 'String.prototype.endsWith';
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
const string: String = ToThisString(receiver, kBuiltinName);
// 3. Let isRegExp be ? IsRegExp(searchString).
// 4. If isRegExp is true, throw a TypeError exception.
if (regexp::IsRegExp(searchString)) {
ThrowTypeError(MessageTemplate::kFirstArgumentNotRegExp, kBuiltinName);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.endswith
transitioning javascript builtin StringPrototypeEndsWith(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): Boolean {
const searchString: JSAny = arguments[0];
const endPosition: JSAny = arguments[1];
const kBuiltinName: constexpr string = 'String.prototype.endsWith';
// 5. Let searchStr be ? ToString(searchString).
const searchStr: String = ToString_Inline(searchString);
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
const string: String = ToThisString(receiver, kBuiltinName);
// 6. Let len be the length of S.
const len: uintptr = string.length_uintptr;
// 3. Let isRegExp be ? IsRegExp(searchString).
// 4. If isRegExp is true, throw a TypeError exception.
if (regexp::IsRegExp(searchString)) {
ThrowTypeError(MessageTemplate::kFirstArgumentNotRegExp, kBuiltinName);
}
// 7. If endPosition is undefined, let pos be len,
// else let pos be ? ToInteger(endPosition).
// 8. Let end be min(max(pos, 0), len).
const end: uintptr =
(endPosition != Undefined) ? ClampToIndexRange(endPosition, len) : len;
// 5. Let searchStr be ? ToString(searchString).
const searchStr: String = ToString_Inline(searchString);
// 9. Let searchLength be the length of searchStr.
const searchLength: uintptr = searchStr.length_uintptr;
// 6. Let len be the length of S.
const len: uintptr = string.length_uintptr;
// 10. Let start be end - searchLength.
const start: uintptr = end - searchLength;
// 7. If endPosition is undefined, let pos be len,
// else let pos be ? ToInteger(endPosition).
// 8. Let end be min(max(pos, 0), len).
const end: uintptr =
(endPosition != Undefined) ? ClampToIndexRange(endPosition, len) : len;
// 11. If start is less than 0, return false.
if (Signed(start) < 0) return False;
// 9. Let searchLength be the length of searchStr.
const searchLength: uintptr = searchStr.length_uintptr;
// 10. Let start be end - searchLength.
const start: uintptr = end - searchLength;
// 11. If start is less than 0, return false.
if (Signed(start) < 0) return False;
// 12. If the sequence of code units of S starting at start of length
// searchLength is the same as the full code unit sequence of searchStr,
// return true.
// 13. Otherwise, return false.
try {
// Fast Path: If both strings are direct and relevant indices are Smis.
return TryFastStringCompareSequence(
string, searchStr, start, searchLength) otherwise Slow;
} label Slow {
// Slow Path: If either of the string is indirect, bail into runtime.
return StringCompareSequence(
context, string, searchStr, Convert<Number>(start));
}
// 12. If the sequence of code units of S starting at start of length
// searchLength is the same as the full code unit sequence of searchStr,
// return true.
// 13. Otherwise, return false.
try {
// Fast Path: If both strings are direct and relevant indices are Smis.
return TryFastStringCompareSequence(string, searchStr, start, searchLength)
otherwise Slow;
} label Slow {
// Slow Path: If either of the string is indirect, bail into runtime.
return StringCompareSequence(
context, string, searchStr, Convert<Number>(start));
}
}
}

View File

@ -3,127 +3,124 @@
// found in the LICENSE file.
namespace string {
extern runtime StringEscapeQuotes(Context, String): String;
extern runtime StringEscapeQuotes(Context, String): String;
// https://tc39.github.io/ecma262/#sec-createhtml
transitioning builtin CreateHTML(implicit context: Context)(
receiver: JSAny, methodName: String, tagName: String, attr: String,
attrValue: JSAny): String {
const tagContents: String = ToThisString(receiver, methodName);
let result = '<' + tagName;
if (attr != kEmptyString) {
const attrStringValue: String =
StringEscapeQuotes(context, ToString_Inline(attrValue));
result = result + ' ' + attr + '=\"' + attrStringValue + '\"';
}
return result + '>' + tagContents + '</' + tagName + '>';
// https://tc39.github.io/ecma262/#sec-createhtml
transitioning builtin CreateHTML(implicit context: Context)(
receiver: JSAny, methodName: String, tagName: String, attr: String,
attrValue: JSAny): String {
const tagContents: String = ToThisString(receiver, methodName);
let result = '<' + tagName;
if (attr != kEmptyString) {
const attrStringValue: String =
StringEscapeQuotes(context, ToString_Inline(attrValue));
result = result + ' ' + attr + '=\"' + attrStringValue + '\"';
}
// https://tc39.github.io/ecma262/#sec-string.prototype.anchor
transitioning javascript builtin StringPrototypeAnchor(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): String {
return CreateHTML(
receiver, 'String.prototype.anchor', 'a', 'name', arguments[0]);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.big
transitioning javascript builtin
StringPrototypeBig(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): String {
return CreateHTML(
receiver, 'String.prototype.big', 'big', kEmptyString, kEmptyString);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.blink
transitioning javascript builtin
StringPrototypeBlink(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): String {
return CreateHTML(
receiver, 'String.prototype.blink', 'blink', kEmptyString,
kEmptyString);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.bold
transitioning javascript builtin
StringPrototypeBold(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): String {
return CreateHTML(
receiver, 'String.prototype.bold', 'b', kEmptyString, kEmptyString);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.fontcolor
transitioning javascript builtin
StringPrototypeFontcolor(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): String {
return CreateHTML(
receiver, 'String.prototype.fontcolor', 'font', 'color', arguments[0]);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.fontsize
transitioning javascript builtin
StringPrototypeFontsize(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): String {
return CreateHTML(
receiver, 'String.prototype.fontsize', 'font', 'size', arguments[0]);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.fixed
transitioning javascript builtin
StringPrototypeFixed(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): String {
return CreateHTML(
receiver, 'String.prototype.fixed', 'tt', kEmptyString, kEmptyString);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.italics
transitioning javascript builtin
StringPrototypeItalics(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): String {
return CreateHTML(
receiver, 'String.prototype.italics', 'i', kEmptyString, kEmptyString);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.link
transitioning javascript builtin
StringPrototypeLink(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): String {
return CreateHTML(
receiver, 'String.prototype.link', 'a', 'href', arguments[0]);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.small
transitioning javascript builtin
StringPrototypeSmall(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): String {
return CreateHTML(
receiver, 'String.prototype.small', 'small', kEmptyString,
kEmptyString);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.strike
transitioning javascript builtin
StringPrototypeStrike(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): String {
return CreateHTML(
receiver, 'String.prototype.strike', 'strike', kEmptyString,
kEmptyString);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.sub
transitioning javascript builtin
StringPrototypeSub(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): String {
return CreateHTML(
receiver, 'String.prototype.sub', 'sub', kEmptyString, kEmptyString);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.sup
transitioning javascript builtin
StringPrototypeSup(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): String {
return CreateHTML(
receiver, 'String.prototype.sup', 'sup', kEmptyString, kEmptyString);
}
return result + '>' + tagContents + '</' + tagName + '>';
}
// https://tc39.github.io/ecma262/#sec-string.prototype.anchor
transitioning javascript builtin StringPrototypeAnchor(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
return CreateHTML(
receiver, 'String.prototype.anchor', 'a', 'name', arguments[0]);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.big
transitioning javascript builtin
StringPrototypeBig(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
return CreateHTML(
receiver, 'String.prototype.big', 'big', kEmptyString, kEmptyString);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.blink
transitioning javascript builtin
StringPrototypeBlink(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
return CreateHTML(
receiver, 'String.prototype.blink', 'blink', kEmptyString, kEmptyString);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.bold
transitioning javascript builtin
StringPrototypeBold(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
return CreateHTML(
receiver, 'String.prototype.bold', 'b', kEmptyString, kEmptyString);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.fontcolor
transitioning javascript builtin
StringPrototypeFontcolor(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
return CreateHTML(
receiver, 'String.prototype.fontcolor', 'font', 'color', arguments[0]);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.fontsize
transitioning javascript builtin
StringPrototypeFontsize(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
return CreateHTML(
receiver, 'String.prototype.fontsize', 'font', 'size', arguments[0]);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.fixed
transitioning javascript builtin
StringPrototypeFixed(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
return CreateHTML(
receiver, 'String.prototype.fixed', 'tt', kEmptyString, kEmptyString);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.italics
transitioning javascript builtin
StringPrototypeItalics(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
return CreateHTML(
receiver, 'String.prototype.italics', 'i', kEmptyString, kEmptyString);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.link
transitioning javascript builtin
StringPrototypeLink(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
return CreateHTML(
receiver, 'String.prototype.link', 'a', 'href', arguments[0]);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.small
transitioning javascript builtin
StringPrototypeSmall(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
return CreateHTML(
receiver, 'String.prototype.small', 'small', kEmptyString, kEmptyString);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.strike
transitioning javascript builtin
StringPrototypeStrike(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
return CreateHTML(
receiver, 'String.prototype.strike', 'strike', kEmptyString,
kEmptyString);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.sub
transitioning javascript builtin
StringPrototypeSub(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
return CreateHTML(
receiver, 'String.prototype.sub', 'sub', kEmptyString, kEmptyString);
}
// https://tc39.github.io/ecma262/#sec-string.prototype.sup
transitioning javascript builtin
StringPrototypeSup(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
return CreateHTML(
receiver, 'String.prototype.sup', 'sup', kEmptyString, kEmptyString);
}
}

View File

@ -4,43 +4,43 @@
namespace string {
macro NewJSStringIterator(implicit context: Context)(
string: String, nextIndex: Smi): JSStringIterator {
return new JSStringIterator{
map: GetInitialStringIteratorMap(),
properties_or_hash: kEmptyFixedArray,
elements: kEmptyFixedArray,
string: string,
index: nextIndex
};
}
// ES6 #sec-string.prototype-@@iterator
transitioning javascript builtin StringPrototypeIterator(
js-implicit context: NativeContext, receiver: JSAny)(): JSStringIterator {
const name: String =
ToThisString(receiver, 'String.prototype[Symbol.iterator]');
const index: Smi = 0;
return NewJSStringIterator(name, index);
}
// ES6 #sec-%stringiteratorprototype%.next
transitioning javascript builtin StringIteratorPrototypeNext(
js-implicit context: NativeContext, receiver: JSAny)(): JSObject {
const iterator = Cast<JSStringIterator>(receiver) otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver,
'String Iterator.prototype.next', receiver);
const string = iterator.string;
const position: intptr = SmiUntag(iterator.index);
const length: intptr = string.length_intptr;
if (position >= length) {
return AllocateJSIteratorResult(Undefined, True);
}
// Move to next codepoint.
const encoding = UnicodeEncoding::UTF16;
const ch = string::LoadSurrogatePairAt(string, length, position, encoding);
const value: String = string::StringFromSingleUTF16EncodedCodePoint(ch);
iterator.index = SmiTag(position + value.length_intptr);
return AllocateJSIteratorResult(value, False);
}
macro NewJSStringIterator(implicit context: Context)(
string: String, nextIndex: Smi): JSStringIterator {
return new JSStringIterator{
map: GetInitialStringIteratorMap(),
properties_or_hash: kEmptyFixedArray,
elements: kEmptyFixedArray,
string: string,
index: nextIndex
};
}
// ES6 #sec-string.prototype-@@iterator
transitioning javascript builtin StringPrototypeIterator(
js-implicit context: NativeContext, receiver: JSAny)(): JSStringIterator {
const name: String =
ToThisString(receiver, 'String.prototype[Symbol.iterator]');
const index: Smi = 0;
return NewJSStringIterator(name, index);
}
// ES6 #sec-%stringiteratorprototype%.next
transitioning javascript builtin StringIteratorPrototypeNext(
js-implicit context: NativeContext, receiver: JSAny)(): JSObject {
const iterator = Cast<JSStringIterator>(receiver) otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver,
'String Iterator.prototype.next', receiver);
const string = iterator.string;
const position: intptr = SmiUntag(iterator.index);
const length: intptr = string.length_intptr;
if (position >= length) {
return AllocateJSIteratorResult(Undefined, True);
}
// Move to next codepoint.
const encoding = UnicodeEncoding::UTF16;
const ch = string::LoadSurrogatePairAt(string, length, position, encoding);
const value: String = string::StringFromSingleUTF16EncodedCodePoint(ch);
iterator.index = SmiTag(position + value.length_intptr);
return AllocateJSIteratorResult(value, False);
}
}

View File

@ -6,106 +6,106 @@
namespace string {
extern transitioning builtin
StringSubstring(implicit context: Context)(String, intptr, intptr): String;
extern transitioning builtin
StringSubstring(implicit context: Context)(String, intptr, intptr): String;
const kStringPadStart: constexpr int31 = 0;
const kStringPadEnd: constexpr int31 = 1;
const kStringPadStart: constexpr int31 = 0;
const kStringPadEnd: constexpr int31 = 1;
transitioning macro StringPad(implicit context: Context)(
receiver: JSAny, arguments: Arguments, methodName: constexpr string,
variant: constexpr int31): String {
const receiverString: String = ToThisString(receiver, methodName);
const stringLength: Smi = receiverString.length_smi;
transitioning macro StringPad(implicit context: Context)(
receiver: JSAny, arguments: Arguments, methodName: constexpr string,
variant: constexpr int31): String {
const receiverString: String = ToThisString(receiver, methodName);
const stringLength: Smi = receiverString.length_smi;
if (arguments.length == 0) {
return receiverString;
}
const maxLength: Number = ToLength_Inline(arguments[0]);
assert(IsNumberNormalized(maxLength));
if (arguments.length == 0) {
return receiverString;
}
const maxLength: Number = ToLength_Inline(arguments[0]);
assert(IsNumberNormalized(maxLength));
typeswitch (maxLength) {
case (smiMaxLength: Smi): {
if (smiMaxLength <= stringLength) {
return receiverString;
}
}
case (Number): {
typeswitch (maxLength) {
case (smiMaxLength: Smi): {
if (smiMaxLength <= stringLength) {
return receiverString;
}
}
let fillString: String = ' ';
let fillLength: intptr = 1;
if (arguments.length != 1) {
const fill = arguments[1];
if (fill != Undefined) {
fillString = ToString_Inline(fill);
fillLength = fillString.length_intptr;
if (fillLength == 0) {
return receiverString;
}
}
case (Number): {
}
// Pad.
assert(fillLength > 0);
// Throw if max_length is greater than String::kMaxLength.
if (!TaggedIsSmi(maxLength)) {
ThrowInvalidStringLength(context);
}
const smiMaxLength: Smi = UnsafeCast<Smi>(maxLength);
if (smiMaxLength > SmiConstant(kStringMaxLength)) {
ThrowInvalidStringLength(context);
}
assert(smiMaxLength > stringLength);
const padLength: Smi = smiMaxLength - stringLength;
let padding: String;
if (fillLength == 1) {
// Single char fill.
// Fast path for a single character fill. No need to calculate number of
// repetitions or remainder.
padding = StringRepeat(context, fillString, padLength);
} else {
// Multi char fill.
const fillLengthWord32: int32 = TruncateIntPtrToInt32(fillLength);
const padLengthWord32: int32 = Convert<int32>(padLength);
const repetitionsWord32: int32 = padLengthWord32 / fillLengthWord32;
const remainingWord32: int32 = padLengthWord32 % fillLengthWord32;
padding =
StringRepeat(context, fillString, Convert<Smi>(repetitionsWord32));
if (remainingWord32 != 0) {
const remainderString =
StringSubstring(fillString, 0, Convert<intptr>(remainingWord32));
padding = padding + remainderString;
}
}
// Return result.
assert(padLength == padding.length_smi);
if (variant == kStringPadStart) {
return padding + receiverString;
}
assert(variant == kStringPadEnd);
return receiverString + padding;
}
// ES6 #sec-string.prototype.padstart
transitioning javascript builtin
StringPrototypePadStart(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): String {
const methodName: constexpr string = 'String.prototype.padStart';
return StringPad(receiver, arguments, methodName, kStringPadStart);
let fillString: String = ' ';
let fillLength: intptr = 1;
if (arguments.length != 1) {
const fill = arguments[1];
if (fill != Undefined) {
fillString = ToString_Inline(fill);
fillLength = fillString.length_intptr;
if (fillLength == 0) {
return receiverString;
}
}
}
// ES6 #sec-string.prototype.padend
transitioning javascript builtin
StringPrototypePadEnd(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): String {
const methodName: constexpr string = 'String.prototype.padEnd';
return StringPad(receiver, arguments, methodName, kStringPadEnd);
// Pad.
assert(fillLength > 0);
// Throw if max_length is greater than String::kMaxLength.
if (!TaggedIsSmi(maxLength)) {
ThrowInvalidStringLength(context);
}
const smiMaxLength: Smi = UnsafeCast<Smi>(maxLength);
if (smiMaxLength > SmiConstant(kStringMaxLength)) {
ThrowInvalidStringLength(context);
}
assert(smiMaxLength > stringLength);
const padLength: Smi = smiMaxLength - stringLength;
let padding: String;
if (fillLength == 1) {
// Single char fill.
// Fast path for a single character fill. No need to calculate number of
// repetitions or remainder.
padding = StringRepeat(context, fillString, padLength);
} else {
// Multi char fill.
const fillLengthWord32: int32 = TruncateIntPtrToInt32(fillLength);
const padLengthWord32: int32 = Convert<int32>(padLength);
const repetitionsWord32: int32 = padLengthWord32 / fillLengthWord32;
const remainingWord32: int32 = padLengthWord32 % fillLengthWord32;
padding =
StringRepeat(context, fillString, Convert<Smi>(repetitionsWord32));
if (remainingWord32 != 0) {
const remainderString =
StringSubstring(fillString, 0, Convert<intptr>(remainingWord32));
padding = padding + remainderString;
}
}
// Return result.
assert(padLength == padding.length_smi);
if (variant == kStringPadStart) {
return padding + receiverString;
}
assert(variant == kStringPadEnd);
return receiverString + padding;
}
// ES6 #sec-string.prototype.padstart
transitioning javascript builtin
StringPrototypePadStart(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
const methodName: constexpr string = 'String.prototype.padStart';
return StringPad(receiver, arguments, methodName, kStringPadStart);
}
// ES6 #sec-string.prototype.padend
transitioning javascript builtin
StringPrototypePadEnd(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
const methodName: constexpr string = 'String.prototype.padEnd';
return StringPad(receiver, arguments, methodName, kStringPadEnd);
}
}

View File

@ -3,73 +3,72 @@
// found in the LICENSE file.
namespace string {
const kBuiltinName: constexpr string = 'String.prototype.repeat';
const kBuiltinName: constexpr string = 'String.prototype.repeat';
builtin StringRepeat(implicit context: Context)(string: String, count: Smi):
String {
assert(count >= 0);
assert(string != kEmptyString);
builtin StringRepeat(implicit context: Context)(
string: String, count: Smi): String {
assert(count >= 0);
assert(string != kEmptyString);
let result: String = kEmptyString;
let powerOfTwoRepeats: String = string;
let n: intptr = Convert<intptr>(count);
let result: String = kEmptyString;
let powerOfTwoRepeats: String = string;
let n: intptr = Convert<intptr>(count);
while (true) {
if ((n & 1) == 1) result = result + powerOfTwoRepeats;
while (true) {
if ((n & 1) == 1) result = result + powerOfTwoRepeats;
n = n >> 1;
if (n == 0) break;
n = n >> 1;
if (n == 0) break;
powerOfTwoRepeats = powerOfTwoRepeats + powerOfTwoRepeats;
}
return result;
powerOfTwoRepeats = powerOfTwoRepeats + powerOfTwoRepeats;
}
// https://tc39.github.io/ecma262/#sec-string.prototype.repeat
transitioning javascript builtin StringPrototypeRepeat(
js-implicit context: NativeContext,
receiver: JSAny)(count: JSAny): String {
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
const s: String = ToThisString(receiver, kBuiltinName);
return result;
}
try {
// 3. Let n be ? ToInteger(count).
typeswitch (ToInteger_Inline(count)) {
case (n: Smi): {
// 4. If n < 0, throw a RangeError exception.
if (n < 0) goto InvalidCount;
// https://tc39.github.io/ecma262/#sec-string.prototype.repeat
transitioning javascript builtin StringPrototypeRepeat(
js-implicit context: NativeContext, receiver: JSAny)(count: JSAny): String {
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
const s: String = ToThisString(receiver, kBuiltinName);
// 6. If n is 0, return the empty String.
if (n == 0 || s.length_uint32 == 0) goto EmptyString;
try {
// 3. Let n be ? ToInteger(count).
typeswitch (ToInteger_Inline(count)) {
case (n: Smi): {
// 4. If n < 0, throw a RangeError exception.
if (n < 0) goto InvalidCount;
if (n > kStringMaxLength) goto InvalidStringLength;
// 6. If n is 0, return the empty String.
if (n == 0 || s.length_uint32 == 0) goto EmptyString;
// 7. Return the String value that is made from n copies of S appended
// together.
return StringRepeat(s, n);
}
case (heapNum: HeapNumber): deferred {
assert(IsNumberNormalized(heapNum));
const n = LoadHeapNumberValue(heapNum);
if (n > kStringMaxLength) goto InvalidStringLength;
// 4. If n < 0, throw a RangeError exception.
// 5. If n is +∞, throw a RangeError exception.
if (n == V8_INFINITY || n < 0.0) goto InvalidCount;
// 6. If n is 0, return the empty String.
if (s.length_uint32 == 0) goto EmptyString;
goto InvalidStringLength;
}
// 7. Return the String value that is made from n copies of S appended
// together.
return StringRepeat(s, n);
}
case (heapNum: HeapNumber): deferred {
assert(IsNumberNormalized(heapNum));
const n = LoadHeapNumberValue(heapNum);
// 4. If n < 0, throw a RangeError exception.
// 5. If n is +∞, throw a RangeError exception.
if (n == V8_INFINITY || n < 0.0) goto InvalidCount;
// 6. If n is 0, return the empty String.
if (s.length_uint32 == 0) goto EmptyString;
goto InvalidStringLength;
}
} label EmptyString {
return kEmptyString;
} label InvalidCount deferred {
ThrowRangeError(MessageTemplate::kInvalidCountValue, count);
} label InvalidStringLength deferred {
ThrowInvalidStringLength(context);
}
} label EmptyString {
return kEmptyString;
} label InvalidCount deferred {
ThrowRangeError(MessageTemplate::kInvalidCountValue, count);
} label InvalidStringLength deferred {
ThrowInvalidStringLength(context);
}
}
}

View File

@ -5,216 +5,216 @@
#include 'src/builtins/builtins-string-gen.h'
namespace string {
extern macro ReplaceSymbolConstant(): Symbol;
extern macro ReplaceSymbolConstant(): Symbol;
extern macro StringBuiltinsAssembler::GetSubstitution(
implicit context: Context)(String, Smi, Smi, String): String;
extern macro StringBuiltinsAssembler::GetSubstitution(
implicit context: Context)(String, Smi, Smi, String): String;
extern builtin
StringIndexOf(implicit context: Context)(String, String, Smi): Smi;
extern builtin
StringIndexOf(implicit context: Context)(String, String, Smi): Smi;
macro TryFastAbstractStringIndexOf(implicit context: Context)(
string: String, searchString: String, fromIndex: Smi): Smi labels Slow {
const stringLen = string.length_uintptr;
const searchLen = searchString.length_uintptr;
const directString = Cast<DirectString>(string) otherwise Slow;
const directSearchStr = Cast<DirectString>(searchString) otherwise Slow;
const fromIndexUint = Unsigned(SmiUntag(fromIndex));
macro TryFastAbstractStringIndexOf(implicit context: Context)(
string: String, searchString: String, fromIndex: Smi): Smi labels Slow {
const stringLen = string.length_uintptr;
const searchLen = searchString.length_uintptr;
const directString = Cast<DirectString>(string) otherwise Slow;
const directSearchStr = Cast<DirectString>(searchString) otherwise Slow;
const fromIndexUint = Unsigned(SmiUntag(fromIndex));
for (let i: uintptr = fromIndexUint; i < stringLen; i++) {
let j = i;
let k: uintptr = 0;
while (j < stringLen && k < searchLen &&
StringCharCodeAt(directString, j) ==
StringCharCodeAt(directSearchStr, k)) {
j++;
k++;
}
if (k == searchLen) {
return SmiTag(Signed(i));
for (let i: uintptr = fromIndexUint; i < stringLen; i++) {
let j = i;
let k: uintptr = 0;
while (j < stringLen && k < searchLen &&
StringCharCodeAt(directString, j) ==
StringCharCodeAt(directSearchStr, k)) {
j++;
k++;
}
if (k == searchLen) {
return SmiTag(Signed(i));
}
}
return -1;
}
macro AbstractStringIndexOf(implicit context: Context)(
string: String, searchString: String, fromIndex: Smi): Smi {
// Special case the empty string.
const searchStringLength = searchString.length_intptr;
const stringLength = string.length_intptr;
if (searchStringLength == 0 && SmiUntag(fromIndex) <= stringLength) {
return fromIndex;
}
// Don't bother to search if the searchString would go past the end
// of the string. This is actually necessary because of runtime
// checks.
if (SmiUntag(fromIndex) + searchStringLength > stringLength) {
return -1;
}
try {
return TryFastAbstractStringIndexOf(string, searchString, fromIndex)
otherwise Slow;
} label Slow {
for (let i: intptr = SmiUntag(fromIndex);
i + searchStringLength <= stringLength; i++) {
if (StringCompareSequence(
context, string, searchString, Convert<Number>(SmiTag(i))) ==
True) {
return SmiTag(i);
}
}
return -1;
}
}
macro AbstractStringIndexOf(implicit context: Context)(
string: String, searchString: String, fromIndex: Smi): Smi {
// Special case the empty string.
const searchStringLength = searchString.length_intptr;
const stringLength = string.length_intptr;
if (searchStringLength == 0 && SmiUntag(fromIndex) <= stringLength) {
return fromIndex;
transitioning macro
ThrowIfNotGlobal(implicit context: Context)(searchValue: JSAny): void {
let shouldThrow: bool;
typeswitch (searchValue) {
case (fastRegExp: FastJSRegExp): {
shouldThrow = !fastRegExp.global;
}
// Don't bother to search if the searchString would go past the end
// of the string. This is actually necessary because of runtime
// checks.
if (SmiUntag(fromIndex) + searchStringLength > stringLength) {
return -1;
}
try {
return TryFastAbstractStringIndexOf(string, searchString, fromIndex)
otherwise Slow;
} label Slow {
for (let i: intptr = SmiUntag(fromIndex);
i + searchStringLength <= stringLength; i++) {
if (StringCompareSequence(
context, string, searchString, Convert<Number>(SmiTag(i))) ==
True) {
return SmiTag(i);
}
}
return -1;
case (Object): {
const flags = GetProperty(searchValue, 'flags');
RequireObjectCoercible(flags, 'String.prototype.replaceAll');
shouldThrow =
StringIndexOf(ToString_Inline(flags), StringConstant('g'), 0) == -1;
}
}
transitioning macro
ThrowIfNotGlobal(implicit context: Context)(searchValue: JSAny): void {
let shouldThrow: bool;
typeswitch (searchValue) {
case (fastRegExp: FastJSRegExp): {
shouldThrow = !fastRegExp.global;
}
case (Object): {
const flags = GetProperty(searchValue, 'flags');
RequireObjectCoercible(flags, 'String.prototype.replaceAll');
shouldThrow =
StringIndexOf(ToString_Inline(flags), StringConstant('g'), 0) == -1;
}
}
if (shouldThrow) {
ThrowTypeError(
MessageTemplate::kRegExpGlobalInvokedOnNonGlobal,
'String.prototype.replaceAll');
}
}
// https://tc39.es/ecma262/#sec-string.prototype.replaceall
transitioning javascript builtin StringPrototypeReplaceAll(
js-implicit context: NativeContext,
receiver: JSAny)(searchValue: JSAny, replaceValue: JSAny): JSAny {
// 1. Let O be ? RequireObjectCoercible(this value).
RequireObjectCoercible(receiver, 'String.prototype.replaceAll');
// 2. If searchValue is neither undefined nor null, then
if (searchValue != Undefined && searchValue != Null) {
// a. Let isRegExp be ? IsRegExp(searchString).
// b. If isRegExp is true, then
// i. Let flags be ? Get(searchValue, "flags").
// ii. Perform ? RequireObjectCoercible(flags).
// iii. If ? ToString(flags) does not contain "g", throw a
// TypeError exception.
if (regexp::IsRegExp(searchValue)) {
ThrowIfNotGlobal(searchValue);
}
// TODO(joshualitt): We could easily add fast paths for string
// searchValues and potential FastRegExps.
// c. Let replacer be ? GetMethod(searchValue, @@replace).
// d. If replacer is not undefined, then
// i. Return ? Call(replacer, searchValue, « O, replaceValue »).
try {
const replacer = GetMethod(searchValue, ReplaceSymbolConstant())
otherwise ReplaceSymbolIsNullOrUndefined;
return Call(context, replacer, searchValue, receiver, replaceValue);
} label ReplaceSymbolIsNullOrUndefined {}
}
// 3. Let string be ? ToString(O).
const string = ToString_Inline(receiver);
// 4. Let searchString be ? ToString(searchValue).
const searchString = ToString_Inline(searchValue);
// 5. Let functionalReplace be IsCallable(replaceValue).
let replaceValueArg = replaceValue;
const functionalReplace = Is<Callable>(replaceValue);
// 6. If functionalReplace is false, then
if (!functionalReplace) {
// a. Let replaceValue be ? ToString(replaceValue).
replaceValueArg = ToString_Inline(replaceValue);
}
// 7. Let searchLength be the length of searchString.
const searchLength = searchString.length_smi;
// 8. Let advanceBy be max(1, searchLength).
const advanceBy = SmiMax(1, searchLength);
// We combine the two loops from the spec into one to avoid
// needing a growable array.
//
// 9. Let matchPositions be a new empty List.
// 10. Let position be ! StringIndexOf(string, searchString, 0).
// 11. Repeat, while position is not -1
// a. Append position to the end of matchPositions.
// b. Let position be ! StringIndexOf(string, searchString,
// position + advanceBy).
// 12. Let endOfLastMatch be 0.
// 13. Let result be the empty string value.
// 14. For each position in matchPositions, do
let endOfLastMatch: Smi = 0;
let result: String = kEmptyString;
let position = AbstractStringIndexOf(string, searchString, 0);
while (position != -1) {
// a. If functionalReplace is true, then
// b. Else,
let replacement: String;
if (functionalReplace) {
// i. Let replacement be ? ToString(? Call(replaceValue, undefined,
// « searchString, position,
// string »).
replacement = ToString_Inline(Call(
context, UnsafeCast<Callable>(replaceValueArg), Undefined,
searchString, position, string));
} else {
// i. Assert: Type(replaceValue) is String.
const replaceValueString = UnsafeCast<String>(replaceValueArg);
// ii. Let captures be a new empty List.
// iii. Let replacement be GetSubstitution(searchString,
// string, position, captures,
// undefined, replaceValue).
// Note: Instead we just call a simpler GetSubstitution primitive.
const matchEndPosition = position + searchLength;
replacement = GetSubstitution(
string, position, matchEndPosition, replaceValueString);
}
// c. Let stringSlice be the substring of string consisting of the code
// units from endOfLastMatch (inclusive) up through position
// (exclusive).
const stringSlice = string::SubString(
string, Unsigned(SmiUntag(endOfLastMatch)),
Unsigned(SmiUntag(position)));
// d. Let result be the string-concatenation of result, stringSlice,
// and replacement.
// TODO(joshualitt): This leaves a completely degenerate ConsString tree.
// We could be smarter here.
result = result + stringSlice + replacement;
// e. Let endOfLastMatch be position + searchLength.
endOfLastMatch = position + searchLength;
position =
AbstractStringIndexOf(string, searchString, position + advanceBy);
}
// 15. If endOfLastMatch < the length of string, then
if (endOfLastMatch < string.length_smi) {
// a. Let result be the string-concatenation of result and the substring
// of string consisting of the code units from endOfLastMatch
// (inclusive) up through the final code unit of string (inclusive).
result = result +
string::SubString(
string, Unsigned(SmiUntag(endOfLastMatch)),
Unsigned(string.length_intptr));
}
// 16. Return result.
return result;
if (shouldThrow) {
ThrowTypeError(
MessageTemplate::kRegExpGlobalInvokedOnNonGlobal,
'String.prototype.replaceAll');
}
}
// https://tc39.es/ecma262/#sec-string.prototype.replaceall
transitioning javascript builtin StringPrototypeReplaceAll(
js-implicit context: NativeContext, receiver: JSAny)(
searchValue: JSAny, replaceValue: JSAny): JSAny {
// 1. Let O be ? RequireObjectCoercible(this value).
RequireObjectCoercible(receiver, 'String.prototype.replaceAll');
// 2. If searchValue is neither undefined nor null, then
if (searchValue != Undefined && searchValue != Null) {
// a. Let isRegExp be ? IsRegExp(searchString).
// b. If isRegExp is true, then
// i. Let flags be ? Get(searchValue, "flags").
// ii. Perform ? RequireObjectCoercible(flags).
// iii. If ? ToString(flags) does not contain "g", throw a
// TypeError exception.
if (regexp::IsRegExp(searchValue)) {
ThrowIfNotGlobal(searchValue);
}
// TODO(joshualitt): We could easily add fast paths for string
// searchValues and potential FastRegExps.
// c. Let replacer be ? GetMethod(searchValue, @@replace).
// d. If replacer is not undefined, then
// i. Return ? Call(replacer, searchValue, « O, replaceValue »).
try {
const replacer = GetMethod(searchValue, ReplaceSymbolConstant())
otherwise ReplaceSymbolIsNullOrUndefined;
return Call(context, replacer, searchValue, receiver, replaceValue);
} label ReplaceSymbolIsNullOrUndefined {}
}
// 3. Let string be ? ToString(O).
const string = ToString_Inline(receiver);
// 4. Let searchString be ? ToString(searchValue).
const searchString = ToString_Inline(searchValue);
// 5. Let functionalReplace be IsCallable(replaceValue).
let replaceValueArg = replaceValue;
const functionalReplace = Is<Callable>(replaceValue);
// 6. If functionalReplace is false, then
if (!functionalReplace) {
// a. Let replaceValue be ? ToString(replaceValue).
replaceValueArg = ToString_Inline(replaceValue);
}
// 7. Let searchLength be the length of searchString.
const searchLength = searchString.length_smi;
// 8. Let advanceBy be max(1, searchLength).
const advanceBy = SmiMax(1, searchLength);
// We combine the two loops from the spec into one to avoid
// needing a growable array.
//
// 9. Let matchPositions be a new empty List.
// 10. Let position be ! StringIndexOf(string, searchString, 0).
// 11. Repeat, while position is not -1
// a. Append position to the end of matchPositions.
// b. Let position be ! StringIndexOf(string, searchString,
// position + advanceBy).
// 12. Let endOfLastMatch be 0.
// 13. Let result be the empty string value.
// 14. For each position in matchPositions, do
let endOfLastMatch: Smi = 0;
let result: String = kEmptyString;
let position = AbstractStringIndexOf(string, searchString, 0);
while (position != -1) {
// a. If functionalReplace is true, then
// b. Else,
let replacement: String;
if (functionalReplace) {
// i. Let replacement be ? ToString(? Call(replaceValue, undefined,
// « searchString, position,
// string »).
replacement = ToString_Inline(Call(
context, UnsafeCast<Callable>(replaceValueArg), Undefined,
searchString, position, string));
} else {
// i. Assert: Type(replaceValue) is String.
const replaceValueString = UnsafeCast<String>(replaceValueArg);
// ii. Let captures be a new empty List.
// iii. Let replacement be GetSubstitution(searchString,
// string, position, captures,
// undefined, replaceValue).
// Note: Instead we just call a simpler GetSubstitution primitive.
const matchEndPosition = position + searchLength;
replacement = GetSubstitution(
string, position, matchEndPosition, replaceValueString);
}
// c. Let stringSlice be the substring of string consisting of the code
// units from endOfLastMatch (inclusive) up through position
// (exclusive).
const stringSlice = string::SubString(
string, Unsigned(SmiUntag(endOfLastMatch)),
Unsigned(SmiUntag(position)));
// d. Let result be the string-concatenation of result, stringSlice,
// and replacement.
// TODO(joshualitt): This leaves a completely degenerate ConsString tree.
// We could be smarter here.
result = result + stringSlice + replacement;
// e. Let endOfLastMatch be position + searchLength.
endOfLastMatch = position + searchLength;
position =
AbstractStringIndexOf(string, searchString, position + advanceBy);
}
// 15. If endOfLastMatch < the length of string, then
if (endOfLastMatch < string.length_smi) {
// a. Let result be the string-concatenation of result and the substring
// of string consisting of the code units from endOfLastMatch
// (inclusive) up through the final code unit of string (inclusive).
result = result +
string::SubString(
string, Unsigned(SmiUntag(endOfLastMatch)),
Unsigned(string.length_intptr));
}
// 16. Return result.
return result;
}
}

View File

@ -3,33 +3,32 @@
// found in the LICENSE file.
namespace string {
// ES6 #sec-string.prototype.slice ( start, end )
// https://tc39.github.io/ecma262/#sec-string.prototype.slice
transitioning javascript builtin StringPrototypeSlice(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): String {
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
const string: String = ToThisString(receiver, 'String.prototype.slice');
// ES6 #sec-string.prototype.slice ( start, end )
// https://tc39.github.io/ecma262/#sec-string.prototype.slice
transitioning javascript builtin StringPrototypeSlice(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
const string: String = ToThisString(receiver, 'String.prototype.slice');
// 3. Let len be the number of elements in S.
const length: uintptr = string.length_uintptr;
// 3. Let len be the number of elements in S.
const length: uintptr = string.length_uintptr;
// Convert {start} to a relative index.
const arg0 = arguments[0];
const start: uintptr =
arg0 != Undefined ? ConvertToRelativeIndex(arg0, length) : 0;
// Convert {start} to a relative index.
const arg0 = arguments[0];
const start: uintptr =
arg0 != Undefined ? ConvertToRelativeIndex(arg0, length) : 0;
// 5. If end is undefined, let intEnd be len;
// else Convert {end} to a relative index.
const arg1 = arguments[1];
const end: uintptr =
arg1 != Undefined ? ConvertToRelativeIndex(arg1, length) : length;
// 5. If end is undefined, let intEnd be len;
// else Convert {end} to a relative index.
const arg1 = arguments[1];
const end: uintptr =
arg1 != Undefined ? ConvertToRelativeIndex(arg1, length) : length;
if (end <= start) {
return kEmptyString;
}
return SubString(string, start, end);
if (end <= start) {
return kEmptyString;
}
return SubString(string, start, end);
}
}

View File

@ -5,56 +5,56 @@
#include 'src/builtins/builtins-regexp-gen.h'
namespace string {
// https://tc39.github.io/ecma262/#sec-string.prototype.startswith
transitioning javascript builtin StringPrototypeStartsWith(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): Boolean {
const searchString: JSAny = arguments[0];
const position: JSAny = arguments[1];
const kBuiltinName: constexpr string = 'String.prototype.startsWith';
// https://tc39.github.io/ecma262/#sec-string.prototype.startswith
transitioning javascript builtin StringPrototypeStartsWith(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): Boolean {
const searchString: JSAny = arguments[0];
const position: JSAny = arguments[1];
const kBuiltinName: constexpr string = 'String.prototype.startsWith';
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
const string: String = ToThisString(receiver, kBuiltinName);
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
const string: String = ToThisString(receiver, kBuiltinName);
// 3. Let isRegExp be ? IsRegExp(searchString).
// 4. If isRegExp is true, throw a TypeError exception.
if (regexp::IsRegExp(searchString)) {
ThrowTypeError(MessageTemplate::kFirstArgumentNotRegExp, kBuiltinName);
}
// 3. Let isRegExp be ? IsRegExp(searchString).
// 4. If isRegExp is true, throw a TypeError exception.
if (regexp::IsRegExp(searchString)) {
ThrowTypeError(MessageTemplate::kFirstArgumentNotRegExp, kBuiltinName);
}
// 5. Let searchStr be ? ToString(searchString).
const searchStr: String = ToString_Inline(searchString);
// 5. Let searchStr be ? ToString(searchString).
const searchStr: String = ToString_Inline(searchString);
// 8. Let len be the length of S.
const len: uintptr = string.length_uintptr;
// 8. Let len be the length of S.
const len: uintptr = string.length_uintptr;
// 6. Let pos be ? ToInteger(position).
// 7. Assert: If position is undefined, then pos is 0.
// 9. Let start be min(max(pos, 0), len).
const start: uintptr =
(position != Undefined) ? ClampToIndexRange(position, len) : 0;
// 6. Let pos be ? ToInteger(position).
// 7. Assert: If position is undefined, then pos is 0.
// 9. Let start be min(max(pos, 0), len).
const start: uintptr =
(position != Undefined) ? ClampToIndexRange(position, len) : 0;
// 10. Let searchLength be the length of searchStr.
const searchLength: uintptr = searchStr.length_uintptr;
// 10. Let searchLength be the length of searchStr.
const searchLength: uintptr = searchStr.length_uintptr;
// 11. If searchLength + start is greater than len, return false.
// The comparison is rephrased to be overflow-friendly with unsigned
// indices.
if (searchLength > len - start) return False;
// 11. If searchLength + start is greater than len, return false.
// The comparison is rephrased to be overflow-friendly with unsigned
// indices.
if (searchLength > len - start) return False;
// 12. If the sequence of code units of S starting at start of length
// searchLength is the same as the full code unit sequence of searchStr,
// return true.
// 13. Otherwise, return false.
try {
// Fast Path: If both strings are direct and relevant indices are Smis.
return TryFastStringCompareSequence(
string, searchStr, start, searchLength) otherwise Slow;
} label Slow {
// Slow Path: If either of the string is indirect, bail into runtime.
return StringCompareSequence(
context, string, searchStr, Convert<Number>(start));
}
// 12. If the sequence of code units of S starting at start of length
// searchLength is the same as the full code unit sequence of searchStr,
// return true.
// 13. Otherwise, return false.
try {
// Fast Path: If both strings are direct and relevant indices are Smis.
return TryFastStringCompareSequence(string, searchStr, start, searchLength)
otherwise Slow;
} label Slow {
// Slow Path: If either of the string is indirect, bail into runtime.
return StringCompareSequence(
context, string, searchStr, Convert<Number>(start));
}
}
}

View File

@ -4,40 +4,39 @@
namespace string {
// String.prototype.substr ( start, length )
// ES6 #sec-string.prototype.substr
transitioning javascript builtin StringPrototypeSubstr(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): String {
const methodName: constexpr string = 'String.prototype.substr';
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
const string: String = ToThisString(receiver, methodName);
// String.prototype.substr ( start, length )
// ES6 #sec-string.prototype.substr
transitioning javascript builtin StringPrototypeSubstr(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
const methodName: constexpr string = 'String.prototype.substr';
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
const string: String = ToThisString(receiver, methodName);
// 5. Let size be the number of code units in S.
const size: uintptr = string.length_uintptr;
// 5. Let size be the number of code units in S.
const size: uintptr = string.length_uintptr;
// 3. Let intStart be ? ToInteger(start).
// 6. If intStart < 0, set intStart to max(size + intStart, 0).
const start = arguments[0];
const initStart: uintptr =
start != Undefined ? ConvertToRelativeIndex(start, size) : 0;
// 3. Let intStart be ? ToInteger(start).
// 6. If intStart < 0, set intStart to max(size + intStart, 0).
const start = arguments[0];
const initStart: uintptr =
start != Undefined ? ConvertToRelativeIndex(start, size) : 0;
// 4. If length is undefined,
// let end be +∞; otherwise let end be ? ToInteger(length).
// 7. Let resultLength be min(max(end, 0), size - intStart).
const length = arguments[1];
const lengthLimit = size - initStart;
assert(lengthLimit <= size);
const resultLength: uintptr = length != Undefined ?
ClampToIndexRange(length, lengthLimit) :
lengthLimit;
// 4. If length is undefined,
// let end be +∞; otherwise let end be ? ToInteger(length).
// 7. Let resultLength be min(max(end, 0), size - intStart).
const length = arguments[1];
const lengthLimit = size - initStart;
assert(lengthLimit <= size);
const resultLength: uintptr = length != Undefined ?
ClampToIndexRange(length, lengthLimit) :
lengthLimit;
// 8. If resultLength ≤ 0, return the empty String "".
if (resultLength == 0) return EmptyStringConstant();
// 8. If resultLength ≤ 0, return the empty String "".
if (resultLength == 0) return EmptyStringConstant();
// 9. Return the String value containing resultLength consecutive code units
// from S beginning with the code unit at index intStart.
return SubString(string, initStart, initStart + resultLength);
}
// 9. Return the String value containing resultLength consecutive code units
// from S beginning with the code unit at index intStart.
return SubString(string, initStart, initStart + resultLength);
}
}

View File

@ -4,28 +4,26 @@
namespace string {
// ES6 #sec-string.prototype.substring
transitioning javascript builtin StringPrototypeSubstring(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): String {
// Check that {receiver} is coercible to Object and convert it to a String.
const string: String = ToThisString(receiver, 'String.prototype.substring');
const length: uintptr = string.length_uintptr;
// ES6 #sec-string.prototype.substring
transitioning javascript builtin StringPrototypeSubstring(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
// Check that {receiver} is coercible to Object and convert it to a String.
const string: String = ToThisString(receiver, 'String.prototype.substring');
const length: uintptr = string.length_uintptr;
// Conversion and bounds-checks for {start}.
const arg0 = arguments[0];
let start: uintptr =
arg0 != Undefined ? ClampToIndexRange(arg0, length) : 0;
// Conversion and bounds-checks for {start}.
const arg0 = arguments[0];
let start: uintptr = arg0 != Undefined ? ClampToIndexRange(arg0, length) : 0;
// Conversion and bounds-checks for {end}.
const arg1 = arguments[1];
let end: uintptr =
arg1 != Undefined ? ClampToIndexRange(arg1, length) : length;
if (end < start) {
const tmp: uintptr = end;
end = start;
start = tmp;
}
return SubString(string, start, end);
// Conversion and bounds-checks for {end}.
const arg1 = arguments[1];
let end: uintptr =
arg1 != Undefined ? ClampToIndexRange(arg1, length) : length;
if (end < start) {
const tmp: uintptr = end;
end = start;
start = tmp;
}
return SubString(string, start, end);
}
}

View File

@ -3,47 +3,45 @@
// found in the LICENSE file.
namespace symbol {
extern runtime SymbolDescriptiveString(implicit context: Context)(Symbol):
String;
extern runtime SymbolDescriptiveString(implicit context: Context)(Symbol):
String;
transitioning macro ThisSymbolValue(implicit context: Context)(
receiver: JSAny, method: constexpr string): Symbol {
return UnsafeCast<Symbol>(
ToThisValue(receiver, PrimitiveType::kSymbol, method));
}
// ES #sec-symbol.prototype.description
transitioning javascript builtin SymbolPrototypeDescriptionGetter(
js-implicit context: NativeContext, receiver: JSAny)(): String|Undefined {
// 1. Let s be the this value.
// 2. Let sym be ? thisSymbolValue(s).
const sym: Symbol =
ThisSymbolValue(receiver, 'Symbol.prototype.description');
// 3. Return sym.[[Description]].
return sym.description;
}
// ES6 #sec-symbol.prototype-@@toprimitive
transitioning javascript builtin SymbolPrototypeToPrimitive(
js-implicit context: NativeContext,
receiver: JSAny)(_hint: JSAny): JSAny {
// 1. Return ? thisSymbolValue(this value).
return ThisSymbolValue(receiver, 'Symbol.prototype [ @@toPrimitive ]');
}
// ES6 #sec-symbol.prototype.tostring
transitioning javascript builtin SymbolPrototypeToString(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
// 1. Let sym be ? thisSymbolValue(this value).
const sym: Symbol = ThisSymbolValue(receiver, 'Symbol.prototype.toString');
// 2. Return SymbolDescriptiveString(sym).
return SymbolDescriptiveString(sym);
}
// ES6 #sec-symbol.prototype.valueof
transitioning javascript builtin SymbolPrototypeValueOf(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
// 1. Return ? thisSymbolValue(this value).
return ThisSymbolValue(receiver, 'Symbol.prototype.valueOf');
}
transitioning macro ThisSymbolValue(implicit context: Context)(
receiver: JSAny, method: constexpr string): Symbol {
return UnsafeCast<Symbol>(
ToThisValue(receiver, PrimitiveType::kSymbol, method));
}
// ES #sec-symbol.prototype.description
transitioning javascript builtin SymbolPrototypeDescriptionGetter(
js-implicit context: NativeContext, receiver: JSAny)(): String|Undefined {
// 1. Let s be the this value.
// 2. Let sym be ? thisSymbolValue(s).
const sym: Symbol = ThisSymbolValue(receiver, 'Symbol.prototype.description');
// 3. Return sym.[[Description]].
return sym.description;
}
// ES6 #sec-symbol.prototype-@@toprimitive
transitioning javascript builtin SymbolPrototypeToPrimitive(
js-implicit context: NativeContext, receiver: JSAny)(_hint: JSAny): JSAny {
// 1. Return ? thisSymbolValue(this value).
return ThisSymbolValue(receiver, 'Symbol.prototype [ @@toPrimitive ]');
}
// ES6 #sec-symbol.prototype.tostring
transitioning javascript builtin SymbolPrototypeToString(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
// 1. Let sym be ? thisSymbolValue(this value).
const sym: Symbol = ThisSymbolValue(receiver, 'Symbol.prototype.toString');
// 2. Return SymbolDescriptiveString(sym).
return SymbolDescriptiveString(sym);
}
// ES6 #sec-symbol.prototype.valueof
transitioning javascript builtin SymbolPrototypeValueOf(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
// 1. Return ? thisSymbolValue(this value).
return ThisSymbolValue(receiver, 'Symbol.prototype.valueOf');
}
}

View File

@ -3,188 +3,187 @@
// found in the LICENSE file.
namespace torque_internal {
// Unsafe is a marker that we require to be passed when calling internal APIs
// that might lead to unsoundness when used incorrectly. Unsafe markers should
// therefore not be instantiated anywhere outside of this namespace.
struct Unsafe {}
// Unsafe is a marker that we require to be passed when calling internal APIs
// that might lead to unsoundness when used incorrectly. Unsafe markers should
// therefore not be instantiated anywhere outside of this namespace.
struct Unsafe {}
// Size of a type in memory (on the heap). For class types, this is the size
// of the pointer, not of the instance.
intrinsic %SizeOf<T: type>(): constexpr int31;
// Size of a type in memory (on the heap). For class types, this is the size
// of the pointer, not of the instance.
intrinsic %SizeOf<T: type>(): constexpr int31;
struct Reference<T: type> {
const object: HeapObject;
const offset: intptr;
unsafeMarker: Unsafe;
}
type ConstReference<T: type> extends Reference<T>;
type MutableReference<T: type> extends ConstReference<T>;
struct Reference<T: type> {
const object: HeapObject;
const offset: intptr;
unsafeMarker: Unsafe;
}
type ConstReference<T: type> extends Reference<T>;
type MutableReference<T: type> extends ConstReference<T>;
macro UnsafeNewReference<T: type>(object: HeapObject, offset: intptr):&T {
return %RawDownCast<&T>(
Reference<T>{object: object, offset: offset, unsafeMarker: Unsafe {}});
macro UnsafeNewReference<T: type>(object: HeapObject, offset: intptr):&T {
return %RawDownCast<&T>(
Reference<T>{object: object, offset: offset, unsafeMarker: Unsafe {}});
}
struct Slice<T: type> {
macro TryAtIndex(index: intptr):&T labels OutOfBounds {
if (Convert<uintptr>(index) < Convert<uintptr>(this.length)) {
return UnsafeNewReference<T>(
this.object, this.offset + index * %SizeOf<T>());
} else {
goto OutOfBounds;
}
}
struct Slice<T: type> {
macro TryAtIndex(index: intptr):&T labels OutOfBounds {
if (Convert<uintptr>(index) < Convert<uintptr>(this.length)) {
return UnsafeNewReference<T>(
this.object, this.offset + index * %SizeOf<T>());
} else {
goto OutOfBounds;
}
}
macro AtIndex(index: intptr):&T {
return this.TryAtIndex(index) otherwise unreachable;
}
macro AtIndex(index: uintptr):&T {
return this.TryAtIndex(Convert<intptr>(index)) otherwise unreachable;
}
macro AtIndex(index: constexpr int31):&T {
const i: intptr = Convert<intptr>(index);
return this.TryAtIndex(i) otherwise unreachable;
}
macro AtIndex(index: Smi):&T {
const i: intptr = Convert<intptr>(index);
return this.TryAtIndex(i) otherwise unreachable;
}
macro Iterator(): SliceIterator<T> {
const end = this.offset + this.length * %SizeOf<T>();
return SliceIterator<T>{
object: this.object,
start: this.offset,
end: end,
unsafeMarker: Unsafe {}
};
}
macro Iterator(startIndex: intptr, endIndex: intptr): SliceIterator<T> {
check(
Convert<uintptr>(endIndex) <= Convert<uintptr>(this.length) &&
Convert<uintptr>(startIndex) <= Convert<uintptr>(endIndex));
const start = this.offset + startIndex * %SizeOf<T>();
const end = this.offset + endIndex * %SizeOf<T>();
return SliceIterator<T>{
object: this.object,
start,
end,
unsafeMarker: Unsafe {}
};
}
const object: HeapObject;
const offset: intptr;
const length: intptr;
unsafeMarker: Unsafe;
macro AtIndex(index: intptr):&T {
return this.TryAtIndex(index) otherwise unreachable;
}
macro UnsafeNewSlice<T: type>(
object: HeapObject, offset: intptr, length: intptr): Slice<T> {
return Slice<T>{
object: object,
offset: offset,
length: length,
macro AtIndex(index: uintptr):&T {
return this.TryAtIndex(Convert<intptr>(index)) otherwise unreachable;
}
macro AtIndex(index: constexpr int31):&T {
const i: intptr = Convert<intptr>(index);
return this.TryAtIndex(i) otherwise unreachable;
}
macro AtIndex(index: Smi):&T {
const i: intptr = Convert<intptr>(index);
return this.TryAtIndex(i) otherwise unreachable;
}
macro Iterator(): SliceIterator<T> {
const end = this.offset + this.length * %SizeOf<T>();
return SliceIterator<T>{
object: this.object,
start: this.offset,
end: end,
unsafeMarker: Unsafe {}
};
}
macro Iterator(startIndex: intptr, endIndex: intptr): SliceIterator<T> {
check(
Convert<uintptr>(endIndex) <= Convert<uintptr>(this.length) &&
Convert<uintptr>(startIndex) <= Convert<uintptr>(endIndex));
const start = this.offset + startIndex * %SizeOf<T>();
const end = this.offset + endIndex * %SizeOf<T>();
return SliceIterator<T>{
object: this.object,
start,
end,
unsafeMarker: Unsafe {}
};
}
struct SliceIterator<T: type> {
macro Empty(): bool {
return this.start == this.end;
}
const object: HeapObject;
const offset: intptr;
const length: intptr;
unsafeMarker: Unsafe;
}
macro Next(): T labels NoMore {
return * this.NextReference() otherwise NoMore;
}
macro UnsafeNewSlice<T: type>(
object: HeapObject, offset: intptr, length: intptr): Slice<T> {
return Slice<T>{
object: object,
offset: offset,
length: length,
unsafeMarker: Unsafe {}
};
}
macro NextReference():&T labels NoMore {
if (this.Empty()) {
goto NoMore;
} else {
const result = UnsafeNewReference<T>(this.object, this.start);
this.start += %SizeOf<T>();
return result;
}
}
object: HeapObject;
start: intptr;
end: intptr;
unsafeMarker: Unsafe;
struct SliceIterator<T: type> {
macro Empty(): bool {
return this.start == this.end;
}
macro AddIndexedFieldSizeToObjectSize(
baseSize: intptr, arrayLength: intptr,
fieldSize: constexpr int32): intptr {
const arrayLength = Convert<int32>(arrayLength);
const byteLength = TryInt32Mul(arrayLength, fieldSize)
otherwise unreachable;
return TryIntPtrAdd(baseSize, Convert<intptr>(byteLength))
otherwise unreachable;
macro Next(): T labels NoMore {
return * this.NextReference() otherwise NoMore;
}
macro AlignTagged(x: intptr): intptr {
// Round up to a multiple of kTaggedSize.
return (x + kObjectAlignmentMask) & ~kObjectAlignmentMask;
}
macro IsTaggedAligned(x: intptr): bool {
return (x & kObjectAlignmentMask) == 0;
}
macro ValidAllocationSize(sizeInBytes: intptr, map: Map): bool {
if (sizeInBytes <= 0) return false;
if (!IsTaggedAligned(sizeInBytes)) return false;
const instanceSizeInWords = Convert<intptr>(map.instance_size_in_words);
return instanceSizeInWords == kVariableSizeSentinel ||
instanceSizeInWords * kTaggedSize == sizeInBytes;
}
type UninitializedHeapObject extends HeapObject;
extern macro AllocateAllowLOS(intptr): UninitializedHeapObject;
extern macro GetInstanceTypeMap(constexpr InstanceType): Map;
macro Allocate(sizeInBytes: intptr, map: Map): UninitializedHeapObject {
assert(ValidAllocationSize(sizeInBytes, map));
return AllocateAllowLOS(sizeInBytes);
}
macro InitializeFieldsFromIterator<T: type, Iterator: type>(
target: Slice<T>, originIterator: Iterator) {
let targetIterator = target.Iterator();
let originIterator = originIterator;
while (true) {
const ref:&T = targetIterator.NextReference() otherwise break;
* ref = originIterator.Next() otherwise unreachable;
}
}
// Dummy implementations: do not initialize for UninitializedIterator.
InitializeFieldsFromIterator<char8, UninitializedIterator>(
_target: Slice<char8>, _originIterator: UninitializedIterator) {}
InitializeFieldsFromIterator<char16, UninitializedIterator>(
_target: Slice<char16>, _originIterator: UninitializedIterator) {}
extern macro IsDoubleHole(HeapObject, intptr): bool;
extern macro StoreDoubleHole(HeapObject, intptr);
macro LoadFloat64OrHole(r:&float64_or_hole): float64_or_hole {
return float64_or_hole{
is_hole: IsDoubleHole(r.object, r.offset - kHeapObjectTag),
value: * UnsafeNewReference<float64>(r.object, r.offset)
};
}
macro StoreFloat64OrHole(r:&float64_or_hole, value: float64_or_hole) {
if (value.is_hole) {
StoreDoubleHole(r.object, r.offset - kHeapObjectTag);
macro NextReference():&T labels NoMore {
if (this.Empty()) {
goto NoMore;
} else {
* UnsafeNewReference<float64>(r.object, r.offset) = value.value;
const result = UnsafeNewReference<T>(this.object, this.start);
this.start += %SizeOf<T>();
return result;
}
}
object: HeapObject;
start: intptr;
end: intptr;
unsafeMarker: Unsafe;
}
macro AddIndexedFieldSizeToObjectSize(
baseSize: intptr, arrayLength: intptr, fieldSize: constexpr int32): intptr {
const arrayLength = Convert<int32>(arrayLength);
const byteLength = TryInt32Mul(arrayLength, fieldSize)
otherwise unreachable;
return TryIntPtrAdd(baseSize, Convert<intptr>(byteLength))
otherwise unreachable;
}
macro AlignTagged(x: intptr): intptr {
// Round up to a multiple of kTaggedSize.
return (x + kObjectAlignmentMask) & ~kObjectAlignmentMask;
}
macro IsTaggedAligned(x: intptr): bool {
return (x & kObjectAlignmentMask) == 0;
}
macro ValidAllocationSize(sizeInBytes: intptr, map: Map): bool {
if (sizeInBytes <= 0) return false;
if (!IsTaggedAligned(sizeInBytes)) return false;
const instanceSizeInWords = Convert<intptr>(map.instance_size_in_words);
return instanceSizeInWords == kVariableSizeSentinel ||
instanceSizeInWords * kTaggedSize == sizeInBytes;
}
type UninitializedHeapObject extends HeapObject;
extern macro AllocateAllowLOS(intptr): UninitializedHeapObject;
extern macro GetInstanceTypeMap(constexpr InstanceType): Map;
macro Allocate(sizeInBytes: intptr, map: Map): UninitializedHeapObject {
assert(ValidAllocationSize(sizeInBytes, map));
return AllocateAllowLOS(sizeInBytes);
}
macro InitializeFieldsFromIterator<T: type, Iterator: type>(
target: Slice<T>, originIterator: Iterator) {
let targetIterator = target.Iterator();
let originIterator = originIterator;
while (true) {
const ref:&T = targetIterator.NextReference() otherwise break;
* ref = originIterator.Next() otherwise unreachable;
}
}
// Dummy implementations: do not initialize for UninitializedIterator.
InitializeFieldsFromIterator<char8, UninitializedIterator>(
_target: Slice<char8>, _originIterator: UninitializedIterator) {}
InitializeFieldsFromIterator<char16, UninitializedIterator>(
_target: Slice<char16>, _originIterator: UninitializedIterator) {}
extern macro IsDoubleHole(HeapObject, intptr): bool;
extern macro StoreDoubleHole(HeapObject, intptr);
macro LoadFloat64OrHole(r:&float64_or_hole): float64_or_hole {
return float64_or_hole{
is_hole: IsDoubleHole(r.object, r.offset - kHeapObjectTag),
value: * UnsafeNewReference<float64>(r.object, r.offset)
};
}
macro StoreFloat64OrHole(r:&float64_or_hole, value: float64_or_hole) {
if (value.is_hole) {
StoreDoubleHole(r.object, r.offset - kHeapObjectTag);
} else {
* UnsafeNewReference<float64>(r.object, r.offset) = value.value;
}
}
} // namespace torque_internal
// Indicates that an array-field should not be initialized.

View File

@ -5,438 +5,431 @@
#include 'src/builtins/builtins-constructor-gen.h'
namespace typed_array {
extern builtin IterableToListMayPreserveHoles(Context, Object, Callable):
JSArray;
extern builtin IterableToListMayPreserveHoles(
Context, Object, Callable): JSArray;
extern macro TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer(
implicit context: Context)(uintptr): JSArrayBuffer;
extern macro CodeStubAssembler::AllocateByteArray(uintptr): ByteArray;
extern macro TypedArrayBuiltinsAssembler::GetDefaultConstructor(
implicit context: Context)(JSTypedArray): JSFunction;
extern macro TypedArrayBuiltinsAssembler::SetupTypedArrayEmbedderFields(
JSTypedArray): void;
extern macro TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer(
implicit context: Context)(uintptr): JSArrayBuffer;
extern macro CodeStubAssembler::AllocateByteArray(uintptr): ByteArray;
extern macro TypedArrayBuiltinsAssembler::GetDefaultConstructor(
implicit context: Context)(JSTypedArray): JSFunction;
extern macro TypedArrayBuiltinsAssembler::SetupTypedArrayEmbedderFields(
JSTypedArray): void;
extern runtime ThrowInvalidTypedArrayAlignment(implicit context: Context)(
Map, String): never;
extern runtime ThrowInvalidTypedArrayAlignment(implicit context: Context)(
Map, String): never;
transitioning macro AllocateTypedArray(implicit context: Context)(
isOnHeap: constexpr bool, map: Map, buffer: JSArrayBuffer,
byteOffset: uintptr, byteLength: uintptr, length: uintptr): JSTypedArray {
let elements: ByteArray;
if constexpr (isOnHeap) {
elements = AllocateByteArray(byteLength);
transitioning macro AllocateTypedArray(implicit context: Context)(
isOnHeap: constexpr bool, map: Map, buffer: JSArrayBuffer,
byteOffset: uintptr, byteLength: uintptr, length: uintptr): JSTypedArray {
let elements: ByteArray;
if constexpr (isOnHeap) {
elements = AllocateByteArray(byteLength);
} else {
elements = kEmptyByteArray;
// The max byteOffset is 8 * MaxSmi on the particular platform. 32 bit
// platforms are self-limiting, because we can't allocate an array bigger
// than our 32-bit arithmetic range anyway. 64 bit platforms could
// theoretically have an offset up to 2^35 - 1.
const backingStore: uintptr = Convert<uintptr>(buffer.backing_store_ptr);
// Assert no overflow has occurred. Only assert if the mock array buffer
// allocator is NOT used. When the mock array buffer is used, impossibly
// large allocations are allowed that would erroneously cause an overflow
// and this assertion to fail.
assert(
IsMockArrayBufferAllocatorFlag() ||
(backingStore + byteOffset) >= backingStore);
}
// We can't just build the new object with "new JSTypedArray" here because
// Torque doesn't know its full size including embedder fields, so use CSA
// for the allocation step.
const typedArray =
UnsafeCast<JSTypedArray>(AllocateFastOrSlowJSObjectFromMap(map));
typedArray.elements = elements;
typedArray.buffer = buffer;
typedArray.byte_offset = byteOffset;
typedArray.byte_length = byteLength;
typedArray.length = length;
if constexpr (isOnHeap) {
typed_array::SetJSTypedArrayOnHeapDataPtr(typedArray, elements, byteOffset);
} else {
typed_array::SetJSTypedArrayOffHeapDataPtr(
typedArray, buffer.backing_store_ptr, byteOffset);
assert(
typedArray.data_ptr ==
(buffer.backing_store_ptr + Convert<intptr>(byteOffset)));
}
SetupTypedArrayEmbedderFields(typedArray);
return typedArray;
}
transitioning macro TypedArrayInitialize(implicit context: Context)(
initialize: constexpr bool, map: Map, length: uintptr,
elementsInfo: typed_array::TypedArrayElementsInfo,
bufferConstructor: JSReceiver): JSTypedArray labels IfRangeError {
const byteLength = elementsInfo.CalculateByteLength(length)
otherwise IfRangeError;
const byteLengthNum = Convert<Number>(byteLength);
const defaultConstructor = GetArrayBufferFunction();
const byteOffset: uintptr = 0;
try {
if (bufferConstructor != defaultConstructor) {
goto AttachOffHeapBuffer(ConstructWithTarget(
defaultConstructor, bufferConstructor, byteLengthNum));
}
if (byteLength > kMaxTypedArrayInHeap) goto AllocateOffHeap;
const buffer = AllocateEmptyOnHeapBuffer(byteLength);
const isOnHeap: constexpr bool = true;
const typedArray = AllocateTypedArray(
isOnHeap, map, buffer, byteOffset, byteLength, length);
if constexpr (initialize) {
const backingStore = typedArray.data_ptr;
typed_array::CallCMemset(backingStore, 0, byteLength);
}
return typedArray;
} label AllocateOffHeap {
if constexpr (initialize) {
goto AttachOffHeapBuffer(Construct(defaultConstructor, byteLengthNum));
} else {
elements = kEmptyByteArray;
// The max byteOffset is 8 * MaxSmi on the particular platform. 32 bit
// platforms are self-limiting, because we can't allocate an array bigger
// than our 32-bit arithmetic range anyway. 64 bit platforms could
// theoretically have an offset up to 2^35 - 1.
const backingStore: uintptr = Convert<uintptr>(buffer.backing_store_ptr);
// Assert no overflow has occurred. Only assert if the mock array buffer
// allocator is NOT used. When the mock array buffer is used, impossibly
// large allocations are allowed that would erroneously cause an overflow
// and this assertion to fail.
assert(
IsMockArrayBufferAllocatorFlag() ||
(backingStore + byteOffset) >= backingStore);
goto AttachOffHeapBuffer(Call(
context, GetArrayBufferNoInitFunction(), Undefined, byteLengthNum));
}
// We can't just build the new object with "new JSTypedArray" here because
// Torque doesn't know its full size including embedder fields, so use CSA
// for the allocation step.
const typedArray =
UnsafeCast<JSTypedArray>(AllocateFastOrSlowJSObjectFromMap(map));
typedArray.elements = elements;
typedArray.buffer = buffer;
typedArray.byte_offset = byteOffset;
typedArray.byte_length = byteLength;
typedArray.length = length;
if constexpr (isOnHeap) {
typed_array::SetJSTypedArrayOnHeapDataPtr(
typedArray, elements, byteOffset);
} else {
typed_array::SetJSTypedArrayOffHeapDataPtr(
typedArray, buffer.backing_store_ptr, byteOffset);
assert(
typedArray.data_ptr ==
(buffer.backing_store_ptr + Convert<intptr>(byteOffset)));
}
SetupTypedArrayEmbedderFields(typedArray);
return typedArray;
}
transitioning macro TypedArrayInitialize(implicit context: Context)(
initialize: constexpr bool, map: Map, length: uintptr,
elementsInfo: typed_array::TypedArrayElementsInfo,
bufferConstructor: JSReceiver): JSTypedArray labels IfRangeError {
const byteLength = elementsInfo.CalculateByteLength(length)
otherwise IfRangeError;
const byteLengthNum = Convert<Number>(byteLength);
const defaultConstructor = GetArrayBufferFunction();
const byteOffset: uintptr = 0;
try {
if (bufferConstructor != defaultConstructor) {
goto AttachOffHeapBuffer(ConstructWithTarget(
defaultConstructor, bufferConstructor, byteLengthNum));
}
if (byteLength > kMaxTypedArrayInHeap) goto AllocateOffHeap;
const buffer = AllocateEmptyOnHeapBuffer(byteLength);
const isOnHeap: constexpr bool = true;
const typedArray = AllocateTypedArray(
isOnHeap, map, buffer, byteOffset, byteLength, length);
if constexpr (initialize) {
const backingStore = typedArray.data_ptr;
typed_array::CallCMemset(backingStore, 0, byteLength);
}
return typedArray;
} label AllocateOffHeap {
if constexpr (initialize) {
goto AttachOffHeapBuffer(Construct(defaultConstructor, byteLengthNum));
} else {
goto AttachOffHeapBuffer(Call(
context, GetArrayBufferNoInitFunction(), Undefined, byteLengthNum));
}
} label AttachOffHeapBuffer(bufferObj: Object) {
const buffer = Cast<JSArrayBuffer>(bufferObj) otherwise unreachable;
const isOnHeap: constexpr bool = false;
return AllocateTypedArray(
isOnHeap, map, buffer, byteOffset, byteLength, length);
}
}
// 22.2.4.2 TypedArray ( length )
// ES #sec-typedarray-length
transitioning macro ConstructByLength(implicit context: Context)(
map: Map, lengthObj: JSAny,
elementsInfo: typed_array::TypedArrayElementsInfo): JSTypedArray {
try {
const length: uintptr = ToIndex(lengthObj) otherwise RangeError;
const defaultConstructor: Constructor = GetArrayBufferFunction();
const initialize: constexpr bool = true;
return TypedArrayInitialize(
initialize, map, length, elementsInfo, defaultConstructor)
otherwise RangeError;
} label RangeError deferred {
ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, lengthObj);
}
}
// 22.2.4.4 TypedArray ( object )
// ES #sec-typedarray-object
transitioning macro ConstructByArrayLike(implicit context: Context)(
map: Map, arrayLike: HeapObject, length: uintptr,
elementsInfo: typed_array::TypedArrayElementsInfo,
bufferConstructor: JSReceiver): JSTypedArray {
try {
const initialize: constexpr bool = false;
const typedArray = TypedArrayInitialize(
initialize, map, length, elementsInfo, bufferConstructor)
otherwise RangeError;
try {
const src: JSTypedArray =
Cast<JSTypedArray>(arrayLike) otherwise IfSlow;
if (IsDetachedBuffer(src.buffer)) {
ThrowTypeError(MessageTemplate::kDetachedOperation, 'Construct');
} else if (src.elements_kind != elementsInfo.kind) {
goto IfSlow;
} else if (length > 0) {
const byteLength = typedArray.byte_length;
assert(byteLength <= kArrayBufferMaxByteLength);
typed_array::CallCMemcpy(
typedArray.data_ptr, src.data_ptr, byteLength);
}
} label IfSlow deferred {
if (length > 0) {
TypedArrayCopyElements(
context, typedArray, arrayLike, Convert<Number>(length));
}
}
return typedArray;
} label RangeError deferred {
ThrowRangeError(
MessageTemplate::kInvalidTypedArrayLength, Convert<Number>(length));
}
}
// 22.2.4.4 TypedArray ( object )
// ES #sec-typedarray-object
transitioning macro ConstructByIterable(implicit context: Context)(
iterable: JSReceiver, iteratorFn: Callable): never
labels IfConstructByArrayLike(JSArray, uintptr, JSReceiver) {
const array: JSArray =
IterableToListMayPreserveHoles(context, iterable, iteratorFn);
// Max JSArray length is a valid JSTypedArray length so we just use it.
goto IfConstructByArrayLike(
array, array.length_uintptr, GetArrayBufferFunction());
}
// 22.2.4.3 TypedArray ( typedArray )
// ES #sec-typedarray-typedarray
transitioning macro ConstructByTypedArray(implicit context: Context)(
srcTypedArray: JSTypedArray): never
labels IfConstructByArrayLike(JSTypedArray, uintptr, JSReceiver) {
let bufferConstructor: JSReceiver = GetArrayBufferFunction();
const srcBuffer: JSArrayBuffer = srcTypedArray.buffer;
// TODO(petermarshall): Throw on detached typedArray.
let length: uintptr =
IsDetachedBuffer(srcBuffer) ? 0 : srcTypedArray.length;
// The spec requires that constructing a typed array using a SAB-backed
// typed array use the ArrayBuffer constructor, not the species constructor.
// See https://tc39.github.io/ecma262/#sec-typedarray-typedarray.
if (!IsSharedArrayBuffer(srcBuffer)) {
bufferConstructor = SpeciesConstructor(srcBuffer, bufferConstructor);
// TODO(petermarshall): Throw on detached typedArray.
if (IsDetachedBuffer(srcBuffer)) length = 0;
}
goto IfConstructByArrayLike(srcTypedArray, length, bufferConstructor);
}
// 22.2.4.5 TypedArray ( buffer, byteOffset, length )
// ES #sec-typedarray-buffer-byteoffset-length
transitioning macro ConstructByArrayBuffer(implicit context: Context)(
map: Map, buffer: JSArrayBuffer, byteOffset: JSAny, length: JSAny,
elementsInfo: typed_array::TypedArrayElementsInfo): JSTypedArray {
try {
// 6. Let offset be ? ToIndex(byteOffset).
const offset: uintptr = ToIndex(byteOffset) otherwise IfInvalidOffset;
// 7. If offset modulo elementSize ≠ 0, throw a RangeError exception.
if (elementsInfo.IsUnaligned(offset)) {
goto IfInvalidAlignment('start offset');
}
// 8. If length is present and length is not undefined, then
// a. Let newLength be ? ToIndex(length).
let newLength: uintptr = ToIndex(length) otherwise IfInvalidLength;
let newByteLength: uintptr;
// 9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(buffer)) {
ThrowTypeError(MessageTemplate::kDetachedOperation, 'Construct');
}
// 10. Let bufferByteLength be buffer.[[ArrayBufferByteLength]].
const bufferByteLength: uintptr = buffer.byte_length;
// 11. If length is either not present or undefined, then
if (length == Undefined) {
// a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError
// exception.
if (elementsInfo.IsUnaligned(bufferByteLength)) {
goto IfInvalidAlignment('byte length');
}
// b. Let newByteLength be bufferByteLength - offset.
// c. If newByteLength < 0, throw a RangeError exception.
if (bufferByteLength < offset) goto IfInvalidOffset;
// Spec step 16 length calculated here to avoid recalculating the length
// in the step 12 branch.
newByteLength = bufferByteLength - offset;
newLength = elementsInfo.CalculateLength(newByteLength)
otherwise IfInvalidOffset;
// 12. Else,
} else {
// a. Let newByteLength be newLength × elementSize.
newByteLength = elementsInfo.CalculateByteLength(newLength)
otherwise IfInvalidLength;
// b. If offset + newByteLength > bufferByteLength, throw a RangeError
// exception.
if ((bufferByteLength < newByteLength) ||
(offset > bufferByteLength - newByteLength))
goto IfInvalidLength;
}
const isOnHeap: constexpr bool = false;
return AllocateTypedArray(
isOnHeap, map, buffer, offset, newByteLength, newLength);
} label IfInvalidAlignment(problemString: String) deferred {
ThrowInvalidTypedArrayAlignment(map, problemString);
} label IfInvalidLength deferred {
ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, length);
} label IfInvalidOffset deferred {
ThrowRangeError(MessageTemplate::kInvalidOffset, byteOffset);
}
}
// 22.2.4.6 TypedArrayCreate ( constructor, argumentList )
// ES #typedarray-create
@export
transitioning macro TypedArrayCreateByLength(implicit context: Context)(
constructor: Constructor, length: Number, methodName: constexpr string):
JSTypedArray {
assert(IsSafeInteger(length));
// 1. Let newTypedArray be ? Construct(constructor, argumentList).
const newTypedArrayObj = Construct(constructor, length);
// 2. Perform ? ValidateTypedArray(newTypedArray).
// ValidateTypedArray currently returns the array, not the ViewBuffer.
const newTypedArray: JSTypedArray =
ValidateTypedArray(context, newTypedArrayObj, methodName);
if (IsDetachedBuffer(newTypedArray.buffer)) deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, methodName);
}
// 3. If argumentList is a List of a single Number, then
// a. If newTypedArray.[[ArrayLength]] < argumentList[0], throw a
// TypeError exception.
if (newTypedArray.length < Convert<uintptr>(length)) deferred {
ThrowTypeError(MessageTemplate::kTypedArrayTooShort);
}
// 4. Return newTypedArray.
return newTypedArray;
}
transitioning macro ConstructByJSReceiver(implicit context:
Context)(obj: JSReceiver): never
labels IfConstructByArrayLike(JSReceiver, uintptr, JSReceiver) {
try {
// TODO(v8:8906): Use iterator::GetIteratorMethod() once it supports
// labels.
const iteratorMethod = GetMethod(obj, IteratorSymbolConstant())
otherwise IfIteratorUndefined, IfIteratorNotCallable;
ConstructByIterable(obj, iteratorMethod)
otherwise IfConstructByArrayLike;
} label IfIteratorUndefined {
const lengthObj: JSAny = GetProperty(obj, kLengthString);
const lengthNumber: Number = ToLength_Inline(lengthObj);
// Throw RangeError here if the length does not fit in uintptr because
// such a length will not pass bounds checks in ConstructByArrayLike()
// anyway.
const length: uintptr = ChangeSafeIntegerNumberToUintPtr(lengthNumber)
otherwise goto IfInvalidLength(lengthNumber);
goto IfConstructByArrayLike(obj, length, GetArrayBufferFunction());
} label IfInvalidLength(length: Number) {
ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, length);
} label IfIteratorNotCallable(_value: JSAny) deferred {
ThrowTypeError(MessageTemplate::kIteratorSymbolNonCallable);
}
}
// 22.2.4 The TypedArray Constructors
// ES #sec-typedarray-constructors
transitioning builtin CreateTypedArray(
context: Context, target: JSFunction, newTarget: JSReceiver, arg1: JSAny,
arg2: JSAny, arg3: JSAny): JSTypedArray {
assert(IsConstructor(target));
// 4. Let O be ? AllocateTypedArray(constructorName, NewTarget,
// "%TypedArrayPrototype%").
const map = GetDerivedMap(target, newTarget);
// 5. Let elementSize be the Number value of the Element Size value in Table
// 56 for constructorName.
const elementsInfo = GetTypedArrayElementsInfo(map);
try {
typeswitch (arg1) {
case (length: Smi): {
goto IfConstructByLength(length);
}
case (buffer: JSArrayBuffer): {
return ConstructByArrayBuffer(map, buffer, arg2, arg3, elementsInfo);
}
case (typedArray: JSTypedArray): {
ConstructByTypedArray(typedArray) otherwise IfConstructByArrayLike;
}
case (obj: JSReceiver): {
ConstructByJSReceiver(obj) otherwise IfConstructByArrayLike;
}
// The first argument was a number or fell through and is treated as
// a number. https://tc39.github.io/ecma262/#sec-typedarray-length
case (lengthObj: JSAny): {
goto IfConstructByLength(lengthObj);
}
}
} label IfConstructByLength(length: JSAny) {
return ConstructByLength(map, length, elementsInfo);
} label IfConstructByArrayLike(
arrayLike: JSReceiver, length: uintptr, bufferConstructor: JSReceiver) {
return ConstructByArrayLike(
map, arrayLike, length, elementsInfo, bufferConstructor);
}
}
transitioning macro TypedArraySpeciesCreate(implicit context: Context)(
methodName: constexpr string, numArgs: constexpr int31,
exemplar: JSTypedArray, arg0: JSAny, arg1: JSAny,
arg2: JSAny): JSTypedArray {
const defaultConstructor = GetDefaultConstructor(exemplar);
try {
if (!IsPrototypeTypedArrayPrototype(exemplar.map)) goto IfSlow;
if (IsTypedArraySpeciesProtectorCellInvalid()) goto IfSlow;
const typedArray = CreateTypedArray(
context, defaultConstructor, defaultConstructor, arg0, arg1, arg2);
// It is assumed that the CreateTypedArray builtin does not produce a
// typed array that fails ValidateTypedArray
assert(!IsDetachedBuffer(typedArray.buffer));
return typedArray;
} label IfSlow deferred {
const constructor =
Cast<Constructor>(SpeciesConstructor(exemplar, defaultConstructor))
otherwise unreachable;
// TODO(pwong): Simplify and remove numArgs when varargs are supported in
// macros.
let newObj: JSAny = Undefined;
if constexpr (numArgs == 1) {
newObj = Construct(constructor, arg0);
} else {
assert(numArgs == 3);
newObj = Construct(constructor, arg0, arg1, arg2);
}
return ValidateTypedArray(context, newObj, methodName);
}
}
@export
transitioning macro TypedArraySpeciesCreateByLength(implicit context:
Context)(
methodName: constexpr string, exemplar: JSTypedArray, length: uintptr):
JSTypedArray {
const numArgs: constexpr int31 = 1;
// TODO(v8:4153): pass length further as uintptr.
const typedArray: JSTypedArray = TypedArraySpeciesCreate(
methodName, numArgs, exemplar, Convert<Number>(length), Undefined,
Undefined);
if (typedArray.length < length) deferred {
ThrowTypeError(MessageTemplate::kTypedArrayTooShort);
}
return typedArray;
}
transitioning macro TypedArraySpeciesCreateByBuffer(implicit context:
Context)(
methodName: constexpr string, exemplar: JSTypedArray,
buffer: JSArrayBuffer, beginByteOffset: uintptr,
newLength: uintptr): JSTypedArray {
const numArgs: constexpr int31 = 3;
// TODO(v8:4153): pass length further as uintptr.
const typedArray: JSTypedArray = TypedArraySpeciesCreate(
methodName, numArgs, exemplar, buffer, Convert<Number>(beginByteOffset),
Convert<Number>(newLength));
return typedArray;
} label AttachOffHeapBuffer(bufferObj: Object) {
const buffer = Cast<JSArrayBuffer>(bufferObj) otherwise unreachable;
const isOnHeap: constexpr bool = false;
return AllocateTypedArray(
isOnHeap, map, buffer, byteOffset, byteLength, length);
}
}
// 22.2.4.2 TypedArray ( length )
// ES #sec-typedarray-length
transitioning macro ConstructByLength(implicit context: Context)(
map: Map, lengthObj: JSAny,
elementsInfo: typed_array::TypedArrayElementsInfo): JSTypedArray {
try {
const length: uintptr = ToIndex(lengthObj) otherwise RangeError;
const defaultConstructor: Constructor = GetArrayBufferFunction();
const initialize: constexpr bool = true;
return TypedArrayInitialize(
initialize, map, length, elementsInfo, defaultConstructor)
otherwise RangeError;
} label RangeError deferred {
ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, lengthObj);
}
}
// 22.2.4.4 TypedArray ( object )
// ES #sec-typedarray-object
transitioning macro ConstructByArrayLike(implicit context: Context)(
map: Map, arrayLike: HeapObject, length: uintptr,
elementsInfo: typed_array::TypedArrayElementsInfo,
bufferConstructor: JSReceiver): JSTypedArray {
try {
const initialize: constexpr bool = false;
const typedArray = TypedArrayInitialize(
initialize, map, length, elementsInfo, bufferConstructor)
otherwise RangeError;
try {
const src: JSTypedArray = Cast<JSTypedArray>(arrayLike) otherwise IfSlow;
if (IsDetachedBuffer(src.buffer)) {
ThrowTypeError(MessageTemplate::kDetachedOperation, 'Construct');
} else if (src.elements_kind != elementsInfo.kind) {
goto IfSlow;
} else if (length > 0) {
const byteLength = typedArray.byte_length;
assert(byteLength <= kArrayBufferMaxByteLength);
typed_array::CallCMemcpy(typedArray.data_ptr, src.data_ptr, byteLength);
}
} label IfSlow deferred {
if (length > 0) {
TypedArrayCopyElements(
context, typedArray, arrayLike, Convert<Number>(length));
}
}
return typedArray;
} label RangeError deferred {
ThrowRangeError(
MessageTemplate::kInvalidTypedArrayLength, Convert<Number>(length));
}
}
// 22.2.4.4 TypedArray ( object )
// ES #sec-typedarray-object
transitioning macro ConstructByIterable(implicit context: Context)(
iterable: JSReceiver, iteratorFn: Callable): never
labels IfConstructByArrayLike(JSArray, uintptr, JSReceiver) {
const array: JSArray =
IterableToListMayPreserveHoles(context, iterable, iteratorFn);
// Max JSArray length is a valid JSTypedArray length so we just use it.
goto IfConstructByArrayLike(
array, array.length_uintptr, GetArrayBufferFunction());
}
// 22.2.4.3 TypedArray ( typedArray )
// ES #sec-typedarray-typedarray
transitioning macro ConstructByTypedArray(implicit context: Context)(
srcTypedArray: JSTypedArray): never
labels IfConstructByArrayLike(JSTypedArray, uintptr, JSReceiver) {
let bufferConstructor: JSReceiver = GetArrayBufferFunction();
const srcBuffer: JSArrayBuffer = srcTypedArray.buffer;
// TODO(petermarshall): Throw on detached typedArray.
let length: uintptr = IsDetachedBuffer(srcBuffer) ? 0 : srcTypedArray.length;
// The spec requires that constructing a typed array using a SAB-backed
// typed array use the ArrayBuffer constructor, not the species constructor.
// See https://tc39.github.io/ecma262/#sec-typedarray-typedarray.
if (!IsSharedArrayBuffer(srcBuffer)) {
bufferConstructor = SpeciesConstructor(srcBuffer, bufferConstructor);
// TODO(petermarshall): Throw on detached typedArray.
if (IsDetachedBuffer(srcBuffer)) length = 0;
}
goto IfConstructByArrayLike(srcTypedArray, length, bufferConstructor);
}
// 22.2.4.5 TypedArray ( buffer, byteOffset, length )
// ES #sec-typedarray-buffer-byteoffset-length
transitioning macro ConstructByArrayBuffer(implicit context: Context)(
map: Map, buffer: JSArrayBuffer, byteOffset: JSAny, length: JSAny,
elementsInfo: typed_array::TypedArrayElementsInfo): JSTypedArray {
try {
// 6. Let offset be ? ToIndex(byteOffset).
const offset: uintptr = ToIndex(byteOffset) otherwise IfInvalidOffset;
// 7. If offset modulo elementSize ≠ 0, throw a RangeError exception.
if (elementsInfo.IsUnaligned(offset)) {
goto IfInvalidAlignment('start offset');
}
// 8. If length is present and length is not undefined, then
// a. Let newLength be ? ToIndex(length).
let newLength: uintptr = ToIndex(length) otherwise IfInvalidLength;
let newByteLength: uintptr;
// 9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(buffer)) {
ThrowTypeError(MessageTemplate::kDetachedOperation, 'Construct');
}
// 10. Let bufferByteLength be buffer.[[ArrayBufferByteLength]].
const bufferByteLength: uintptr = buffer.byte_length;
// 11. If length is either not present or undefined, then
if (length == Undefined) {
// a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError
// exception.
if (elementsInfo.IsUnaligned(bufferByteLength)) {
goto IfInvalidAlignment('byte length');
}
// b. Let newByteLength be bufferByteLength - offset.
// c. If newByteLength < 0, throw a RangeError exception.
if (bufferByteLength < offset) goto IfInvalidOffset;
// Spec step 16 length calculated here to avoid recalculating the length
// in the step 12 branch.
newByteLength = bufferByteLength - offset;
newLength = elementsInfo.CalculateLength(newByteLength)
otherwise IfInvalidOffset;
// 12. Else,
} else {
// a. Let newByteLength be newLength × elementSize.
newByteLength = elementsInfo.CalculateByteLength(newLength)
otherwise IfInvalidLength;
// b. If offset + newByteLength > bufferByteLength, throw a RangeError
// exception.
if ((bufferByteLength < newByteLength) ||
(offset > bufferByteLength - newByteLength))
goto IfInvalidLength;
}
const isOnHeap: constexpr bool = false;
return AllocateTypedArray(
isOnHeap, map, buffer, offset, newByteLength, newLength);
} label IfInvalidAlignment(problemString: String) deferred {
ThrowInvalidTypedArrayAlignment(map, problemString);
} label IfInvalidLength deferred {
ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, length);
} label IfInvalidOffset deferred {
ThrowRangeError(MessageTemplate::kInvalidOffset, byteOffset);
}
}
// 22.2.4.6 TypedArrayCreate ( constructor, argumentList )
// ES #typedarray-create
@export
transitioning macro TypedArrayCreateByLength(implicit context: Context)(
constructor: Constructor, length: Number, methodName: constexpr string):
JSTypedArray {
assert(IsSafeInteger(length));
// 1. Let newTypedArray be ? Construct(constructor, argumentList).
const newTypedArrayObj = Construct(constructor, length);
// 2. Perform ? ValidateTypedArray(newTypedArray).
// ValidateTypedArray currently returns the array, not the ViewBuffer.
const newTypedArray: JSTypedArray =
ValidateTypedArray(context, newTypedArrayObj, methodName);
if (IsDetachedBuffer(newTypedArray.buffer)) deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, methodName);
}
// 3. If argumentList is a List of a single Number, then
// a. If newTypedArray.[[ArrayLength]] < argumentList[0], throw a
// TypeError exception.
if (newTypedArray.length < Convert<uintptr>(length)) deferred {
ThrowTypeError(MessageTemplate::kTypedArrayTooShort);
}
// 4. Return newTypedArray.
return newTypedArray;
}
transitioning macro ConstructByJSReceiver(implicit context: Context)(
obj: JSReceiver): never
labels IfConstructByArrayLike(JSReceiver, uintptr, JSReceiver) {
try {
// TODO(v8:8906): Use iterator::GetIteratorMethod() once it supports
// labels.
const iteratorMethod = GetMethod(obj, IteratorSymbolConstant())
otherwise IfIteratorUndefined, IfIteratorNotCallable;
ConstructByIterable(obj, iteratorMethod)
otherwise IfConstructByArrayLike;
} label IfIteratorUndefined {
const lengthObj: JSAny = GetProperty(obj, kLengthString);
const lengthNumber: Number = ToLength_Inline(lengthObj);
// Throw RangeError here if the length does not fit in uintptr because
// such a length will not pass bounds checks in ConstructByArrayLike()
// anyway.
const length: uintptr = ChangeSafeIntegerNumberToUintPtr(lengthNumber)
otherwise goto IfInvalidLength(lengthNumber);
goto IfConstructByArrayLike(obj, length, GetArrayBufferFunction());
} label IfInvalidLength(length: Number) {
ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, length);
} label IfIteratorNotCallable(_value: JSAny) deferred {
ThrowTypeError(MessageTemplate::kIteratorSymbolNonCallable);
}
}
// 22.2.4 The TypedArray Constructors
// ES #sec-typedarray-constructors
transitioning builtin CreateTypedArray(
context: Context, target: JSFunction, newTarget: JSReceiver, arg1: JSAny,
arg2: JSAny, arg3: JSAny): JSTypedArray {
assert(IsConstructor(target));
// 4. Let O be ? AllocateTypedArray(constructorName, NewTarget,
// "%TypedArrayPrototype%").
const map = GetDerivedMap(target, newTarget);
// 5. Let elementSize be the Number value of the Element Size value in Table
// 56 for constructorName.
const elementsInfo = GetTypedArrayElementsInfo(map);
try {
typeswitch (arg1) {
case (length: Smi): {
goto IfConstructByLength(length);
}
case (buffer: JSArrayBuffer): {
return ConstructByArrayBuffer(map, buffer, arg2, arg3, elementsInfo);
}
case (typedArray: JSTypedArray): {
ConstructByTypedArray(typedArray) otherwise IfConstructByArrayLike;
}
case (obj: JSReceiver): {
ConstructByJSReceiver(obj) otherwise IfConstructByArrayLike;
}
// The first argument was a number or fell through and is treated as
// a number. https://tc39.github.io/ecma262/#sec-typedarray-length
case (lengthObj: JSAny): {
goto IfConstructByLength(lengthObj);
}
}
} label IfConstructByLength(length: JSAny) {
return ConstructByLength(map, length, elementsInfo);
} label IfConstructByArrayLike(
arrayLike: JSReceiver, length: uintptr, bufferConstructor: JSReceiver) {
return ConstructByArrayLike(
map, arrayLike, length, elementsInfo, bufferConstructor);
}
}
transitioning macro TypedArraySpeciesCreate(implicit context: Context)(
methodName: constexpr string, numArgs: constexpr int31,
exemplar: JSTypedArray, arg0: JSAny, arg1: JSAny,
arg2: JSAny): JSTypedArray {
const defaultConstructor = GetDefaultConstructor(exemplar);
try {
if (!IsPrototypeTypedArrayPrototype(exemplar.map)) goto IfSlow;
if (IsTypedArraySpeciesProtectorCellInvalid()) goto IfSlow;
const typedArray = CreateTypedArray(
context, defaultConstructor, defaultConstructor, arg0, arg1, arg2);
// It is assumed that the CreateTypedArray builtin does not produce a
// typed array that fails ValidateTypedArray
assert(!IsDetachedBuffer(typedArray.buffer));
return typedArray;
} label IfSlow deferred {
const constructor =
Cast<Constructor>(SpeciesConstructor(exemplar, defaultConstructor))
otherwise unreachable;
// TODO(pwong): Simplify and remove numArgs when varargs are supported in
// macros.
let newObj: JSAny = Undefined;
if constexpr (numArgs == 1) {
newObj = Construct(constructor, arg0);
} else {
assert(numArgs == 3);
newObj = Construct(constructor, arg0, arg1, arg2);
}
return ValidateTypedArray(context, newObj, methodName);
}
}
@export
transitioning macro TypedArraySpeciesCreateByLength(implicit context: Context)(
methodName: constexpr string, exemplar: JSTypedArray, length: uintptr):
JSTypedArray {
const numArgs: constexpr int31 = 1;
// TODO(v8:4153): pass length further as uintptr.
const typedArray: JSTypedArray = TypedArraySpeciesCreate(
methodName, numArgs, exemplar, Convert<Number>(length), Undefined,
Undefined);
if (typedArray.length < length) deferred {
ThrowTypeError(MessageTemplate::kTypedArrayTooShort);
}
return typedArray;
}
transitioning macro TypedArraySpeciesCreateByBuffer(implicit context: Context)(
methodName: constexpr string, exemplar: JSTypedArray, buffer: JSArrayBuffer,
beginByteOffset: uintptr, newLength: uintptr): JSTypedArray {
const numArgs: constexpr int31 = 3;
// TODO(v8:4153): pass length further as uintptr.
const typedArray: JSTypedArray = TypedArraySpeciesCreate(
methodName, numArgs, exemplar, buffer, Convert<Number>(beginByteOffset),
Convert<Number>(newLength));
return typedArray;
}
}

View File

@ -5,49 +5,49 @@
#include 'src/builtins/builtins-typed-array-gen.h'
namespace typed_array {
const kBuiltinNameEvery: constexpr string = '%TypedArray%.prototype.every';
const kBuiltinNameEvery: constexpr string = '%TypedArray%.prototype.every';
transitioning macro EveryAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): Boolean {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const result = Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
if (!ToBoolean(result)) {
return False;
}
transitioning macro EveryAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): Boolean {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const result = Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
if (!ToBoolean(result)) {
return False;
}
return True;
}
return True;
}
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.every
transitioning javascript builtin
TypedArrayPrototypeEvery(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): JSAny {
// arguments[0] = callback
// arguments[1] = thisArg
try {
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise NotTypedArray;
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.every
transitioning javascript builtin
TypedArrayPrototypeEvery(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// arguments[0] = callback
// arguments[1] = thisArg
try {
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise NotTypedArray;
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const thisArg = arguments[1];
return EveryAllElements(uarray, callbackfn, thisArg);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameEvery);
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameEvery);
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const thisArg = arguments[1];
return EveryAllElements(uarray, callbackfn, thisArg);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameEvery);
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameEvery);
}
}
}

View File

@ -3,84 +3,81 @@
// found in the LICENSE file.
namespace typed_array {
const kBuiltinNameFilter: constexpr string = '%TypedArray%.prototype.filter';
const kBuiltinNameFilter: constexpr string = '%TypedArray%.prototype.filter';
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.filter
transitioning javascript builtin TypedArrayPrototypeFilter(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSAny {
// arguments[0] = callback
// arguments[1] = thisArg
try {
// 1. Let O be the this value.
// 2. Perform ? ValidateTypedArray(O).
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise ThrowTypeError(
MessageTemplate::kNotTypedArray, kBuiltinNameFilter);
const src = typed_array::EnsureAttached(array) otherwise IsDetached;
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.filter
transitioning javascript builtin TypedArrayPrototypeFilter(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// arguments[0] = callback
// arguments[1] = thisArg
try {
// 1. Let O be the this value.
// 2. Perform ? ValidateTypedArray(O).
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise ThrowTypeError(
MessageTemplate::kNotTypedArray, kBuiltinNameFilter);
const src = typed_array::EnsureAttached(array) otherwise IsDetached;
// 3. Let len be O.[[ArrayLength]].
const len: uintptr = src.length;
// 3. Let len be O.[[ArrayLength]].
const len: uintptr = src.length;
// 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
const callbackfn = Cast<Callable>(arguments[0])
otherwise ThrowTypeError(
MessageTemplate::kCalledNonCallable, arguments[0]);
// 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
const callbackfn = Cast<Callable>(arguments[0])
otherwise ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
// 5. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: JSAny = arguments[1];
// 5. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: JSAny = arguments[1];
// 6. Let kept be a new empty List.
// TODO(v8:4153): Support huge TypedArrays here. (growable fixed arrays
// can't be longer than kMaxSmiValue).
let kept = growable_fixed_array::NewGrowableFixedArray();
let witness = typed_array::NewAttachedJSTypedArrayWitness(src);
// 6. Let kept be a new empty List.
// TODO(v8:4153): Support huge TypedArrays here. (growable fixed arrays
// can't be longer than kMaxSmiValue).
let kept = growable_fixed_array::NewGrowableFixedArray();
let witness = typed_array::NewAttachedJSTypedArrayWitness(src);
// 7. Let k be 0.
// 8. Let captured be 0.
// 9. Repeat, while k < len
for (let k: uintptr = 0; k < len; k++) {
witness.Recheck() otherwise IsDetached;
// 7. Let k be 0.
// 8. Let captured be 0.
// 9. Repeat, while k < len
for (let k: uintptr = 0; k < len; k++) {
witness.Recheck() otherwise IsDetached;
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(O, Pk).
const value: JSAny = witness.Load(k);
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(O, Pk).
const value: JSAny = witness.Load(k);
// c. Let selected be ToBoolean(? Call(callbackfn, T, « kValue, k, O
// »)).
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const selected: JSAny = Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
// c. Let selected be ToBoolean(? Call(callbackfn, T, « kValue, k, O
// »)).
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const selected: JSAny = Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
// d. If selected is true, then
// i. Append kValue to the end of kept.
// ii. Increase captured by 1.
if (ToBoolean(selected)) kept.Push(value);
// d. If selected is true, then
// i. Append kValue to the end of kept.
// ii. Increase captured by 1.
if (ToBoolean(selected)) kept.Push(value);
// e.Increase k by 1.
}
// 10. Let A be ? TypedArraySpeciesCreate(O, captured).
const typedArray: JSTypedArray = TypedArraySpeciesCreateByLength(
kBuiltinNameFilter, array, Unsigned(kept.length));
// 11. Let n be 0.
// 12. For each element e of kept, do
// a. Perform ! Set(A, ! ToString(n), e, true).
// b. Increment n by 1.
// TODO(v8:4153): Consider passing growable typed array directly to
// TypedArrayCopyElements() to avoid JSArray materialization. Or collect
// indices instead of values the loop above.
const lengthNumber = Convert<Number>(Unsigned(kept.length));
TypedArrayCopyElements(
context, typedArray, kept.ToJSArray(), lengthNumber);
// 13. Return A.
return typedArray;
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameFilter);
// e.Increase k by 1.
}
// 10. Let A be ? TypedArraySpeciesCreate(O, captured).
const typedArray: JSTypedArray = TypedArraySpeciesCreateByLength(
kBuiltinNameFilter, array, Unsigned(kept.length));
// 11. Let n be 0.
// 12. For each element e of kept, do
// a. Perform ! Set(A, ! ToString(n), e, true).
// b. Increment n by 1.
// TODO(v8:4153): Consider passing growable typed array directly to
// TypedArrayCopyElements() to avoid JSArray materialization. Or collect
// indices instead of values the loop above.
const lengthNumber = Convert<Number>(Unsigned(kept.length));
TypedArrayCopyElements(context, typedArray, kept.ToJSArray(), lengthNumber);
// 13. Return A.
return typedArray;
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameFilter);
}
}
}

View File

@ -5,49 +5,49 @@
#include 'src/builtins/builtins-typed-array-gen.h'
namespace typed_array {
const kBuiltinNameFind: constexpr string = '%TypedArray%.prototype.find';
const kBuiltinNameFind: constexpr string = '%TypedArray%.prototype.find';
transitioning macro FindAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): JSAny {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const result = Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
if (ToBoolean(result)) {
return value;
}
transitioning macro FindAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): JSAny {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const result = Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
if (ToBoolean(result)) {
return value;
}
return Undefined;
}
return Undefined;
}
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.find
transitioning javascript builtin
TypedArrayPrototypeFind(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): JSAny {
// arguments[0] = callback
// arguments[1] = thisArg
try {
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise NotTypedArray;
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.find
transitioning javascript builtin
TypedArrayPrototypeFind(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// arguments[0] = callback
// arguments[1] = thisArg
try {
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise NotTypedArray;
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const thisArg = arguments[1];
return FindAllElements(uarray, callbackfn, thisArg);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameFind);
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameFind);
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const thisArg = arguments[1];
return FindAllElements(uarray, callbackfn, thisArg);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameFind);
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameFind);
}
}
}

View File

@ -5,53 +5,50 @@
#include 'src/builtins/builtins-typed-array-gen.h'
namespace typed_array {
const kBuiltinNameFindIndex: constexpr string =
'%TypedArray%.prototype.findIndex';
const kBuiltinNameFindIndex: constexpr string =
'%TypedArray%.prototype.findIndex';
transitioning macro FindIndexAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): Number {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const indexNumber: Number = Convert<Number>(k);
const result = Call(
context, callbackfn, thisArg, value, indexNumber,
witness.GetStable());
if (ToBoolean(result)) {
return indexNumber;
}
transitioning macro FindIndexAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): Number {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const indexNumber: Number = Convert<Number>(k);
const result = Call(
context, callbackfn, thisArg, value, indexNumber, witness.GetStable());
if (ToBoolean(result)) {
return indexNumber;
}
return -1;
}
return -1;
}
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.findIndex
transitioning javascript builtin
TypedArrayPrototypeFindIndex(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSAny {
// arguments[0] = callback
// arguments[1] = thisArg.
try {
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise NotTypedArray;
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.findIndex
transitioning javascript builtin
TypedArrayPrototypeFindIndex(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// arguments[0] = callback
// arguments[1] = thisArg.
try {
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise NotTypedArray;
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const thisArg = arguments[1];
return FindIndexAllElements(uarray, callbackfn, thisArg);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameFindIndex);
} label IsDetached deferred {
ThrowTypeError(
MessageTemplate::kDetachedOperation, kBuiltinNameFindIndex);
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const thisArg = arguments[1];
return FindIndexAllElements(uarray, callbackfn, thisArg);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameFindIndex);
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameFindIndex);
}
}
}

View File

@ -5,49 +5,47 @@
#include 'src/builtins/builtins-typed-array-gen.h'
namespace typed_array {
const kBuiltinNameForEach: constexpr string =
'%TypedArray%.prototype.forEach';
const kBuiltinNameForEach: constexpr string = '%TypedArray%.prototype.forEach';
transitioning macro ForEachAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): Undefined {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
}
return Undefined;
transitioning macro ForEachAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): Undefined {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
}
return Undefined;
}
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.every
transitioning javascript builtin
TypedArrayPrototypeForEach(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): Undefined {
// arguments[0] = callback
// arguments[1] = this_arg.
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.every
transitioning javascript builtin
TypedArrayPrototypeForEach(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): Undefined {
// arguments[0] = callback
// arguments[1] = this_arg.
try {
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise NotTypedArray;
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
try {
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise NotTypedArray;
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const thisArg = arguments[1];
return ForEachAllElements(uarray, callbackfn, thisArg);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameForEach);
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameForEach);
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const thisArg = arguments[1];
return ForEachAllElements(uarray, callbackfn, thisArg);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameForEach);
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameForEach);
}
}
}

View File

@ -5,182 +5,179 @@
#include 'src/builtins/builtins-typed-array-gen.h'
namespace typed_array {
const kBuiltinNameFrom: constexpr string = '%TypedArray%.from';
const kBuiltinNameFrom: constexpr string = '%TypedArray%.from';
type BuiltinsName extends int31 constexpr 'Builtins::Name';
const kTypedArrayPrototypeValues: constexpr BuiltinsName
generates 'Builtins::kTypedArrayPrototypeValues';
type BuiltinsName extends int31 constexpr 'Builtins::Name';
const kTypedArrayPrototypeValues: constexpr BuiltinsName
generates 'Builtins::kTypedArrayPrototypeValues';
extern builtin IterableToList(implicit context: Context)(JSAny, JSAny):
JSArray;
extern builtin IterableToList(implicit context: Context)(JSAny, JSAny): JSArray;
// %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] )
// https://tc39.github.io/ecma262/#sec-%typedarray%.from
transitioning javascript builtin
TypedArrayFrom(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): JSTypedArray {
try {
const source: JSAny = arguments[0];
const mapfnObj: JSAny = arguments[1];
const thisArg = arguments[2];
// 1. Let C be the this value.
// 2. If IsConstructor(C) is false, throw a TypeError exception.
const constructor = Cast<Constructor>(receiver) otherwise NotConstructor;
// 3. If mapfn is undefined, then let mapping be false.
// 4. Else,
// a. If IsCallable(mapfn) is false, throw a TypeError exception.
// b. Let mapping be true.
const mapping: bool = mapfnObj != Undefined;
if (mapping && !Is<Callable>(mapfnObj)) deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, mapfnObj);
}
// We split up this builtin differently to the way it is written in the
// spec. We already have great code in the elements accessor for copying
// from a JSArray into a TypedArray, so we use that when possible. We only
// avoid calling into the elements accessor when we have a mapping
// function, because we can't handle that. Here, presence of a mapping
// function is the slow path. We also combine the two different loops in
// the specification (starting at 7.e and 13) because they are essentially
// identical. We also save on code-size this way.
let finalLength: uintptr;
let finalSource: JSAny;
// %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] )
// https://tc39.github.io/ecma262/#sec-%typedarray%.from
transitioning javascript builtin
TypedArrayFrom(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): JSTypedArray {
try {
const source: JSAny = arguments[0];
const mapfnObj: JSAny = arguments[1];
const thisArg = arguments[2];
// 1. Let C be the this value.
// 2. If IsConstructor(C) is false, throw a TypeError exception.
const constructor = Cast<Constructor>(receiver) otherwise NotConstructor;
// 3. If mapfn is undefined, then let mapping be false.
// 4. Else,
// a. If IsCallable(mapfn) is false, throw a TypeError exception.
// b. Let mapping be true.
const mapping: bool = mapfnObj != Undefined;
if (mapping && !Is<Callable>(mapfnObj)) deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, mapfnObj);
}
// We split up this builtin differently to the way it is written in the
// spec. We already have great code in the elements accessor for copying
// from a JSArray into a TypedArray, so we use that when possible. We only
// avoid calling into the elements accessor when we have a mapping
// function, because we can't handle that. Here, presence of a mapping
// function is the slow path. We also combine the two different loops in
// the specification (starting at 7.e and 13) because they are essentially
// identical. We also save on code-size this way.
let finalLength: uintptr;
let finalSource: JSAny;
// 5. Let usingIterator be ? GetMethod(source, @@iterator).
// TODO(v8:8906): Use iterator::GetIteratorMethod() once it supports
// labels.
const usingIterator = GetMethod(source, IteratorSymbolConstant())
otherwise IteratorIsUndefined, IteratorNotCallable;
try {
// 5. Let usingIterator be ? GetMethod(source, @@iterator).
// TODO(v8:8906): Use iterator::GetIteratorMethod() once it supports
// labels.
const usingIterator = GetMethod(source, IteratorSymbolConstant())
otherwise IteratorIsUndefined, IteratorNotCallable;
// TypedArrays have iterators, so normally we would go through the
// IterableToList case below, which would convert the TypedArray to a
// JSArray (boxing the values if they won't fit in a Smi).
//
// However, if we can guarantee that the source object has the
// built-in iterator and that the %ArrayIteratorPrototype%.next method
// has not been overridden, then we know the behavior of the iterator:
// returning the values in the TypedArray sequentially from index 0 to
// length-1.
//
// In this case, we can avoid creating the intermediate array and the
// associated HeapNumbers, and use the fast path in
// TypedArrayCopyElements which uses the same ordering as the default
// iterator.
//
// Drop through to the default check_iterator behavior if any of these
// checks fail.
const sourceTypedArray =
Cast<JSTypedArray>(source) otherwise UseUserProvidedIterator;
const sourceBuffer = sourceTypedArray.buffer;
if (IsDetachedBuffer(sourceBuffer)) goto UseUserProvidedIterator;
try {
// TypedArrays have iterators, so normally we would go through the
// IterableToList case below, which would convert the TypedArray to a
// JSArray (boxing the values if they won't fit in a Smi).
//
// However, if we can guarantee that the source object has the
// built-in iterator and that the %ArrayIteratorPrototype%.next method
// has not been overridden, then we know the behavior of the iterator:
// returning the values in the TypedArray sequentially from index 0 to
// length-1.
//
// In this case, we can avoid creating the intermediate array and the
// associated HeapNumbers, and use the fast path in
// TypedArrayCopyElements which uses the same ordering as the default
// iterator.
//
// Drop through to the default check_iterator behavior if any of these
// checks fail.
const sourceTypedArray =
Cast<JSTypedArray>(source) otherwise UseUserProvidedIterator;
const sourceBuffer = sourceTypedArray.buffer;
if (IsDetachedBuffer(sourceBuffer)) goto UseUserProvidedIterator;
// Check that the iterator function is exactly
// Builtins::kTypedArrayPrototypeValues.
const iteratorFn =
Cast<JSFunction>(usingIterator) otherwise UseUserProvidedIterator;
if (!TaggedEqual(
iteratorFn.shared_function_info.function_data,
SmiConstant(kTypedArrayPrototypeValues)))
goto UseUserProvidedIterator;
// Check that the iterator function is exactly
// Builtins::kTypedArrayPrototypeValues.
const iteratorFn =
Cast<JSFunction>(usingIterator) otherwise UseUserProvidedIterator;
if (!TaggedEqual(
iteratorFn.shared_function_info.function_data,
SmiConstant(kTypedArrayPrototypeValues)))
goto UseUserProvidedIterator;
// Check that the ArrayIterator prototype's "next" method hasn't been
// overridden.
if (IsArrayIteratorProtectorCellInvalid()) goto UseUserProvidedIterator;
// Check that the ArrayIterator prototype's "next" method hasn't been
// overridden.
if (IsArrayIteratorProtectorCellInvalid())
goto UseUserProvidedIterator;
// Source is a TypedArray with unmodified iterator behavior. Use the
// source object directly, taking advantage of the special-case code
// in TypedArrayCopyElements
finalLength = sourceTypedArray.length;
finalSource = source;
} label UseUserProvidedIterator {
// 6. If usingIterator is not undefined, then
// a. Let values be ? IterableToList(source, usingIterator).
// b. Let len be the number of elements in values.
const values: JSArray = IterableToList(source, usingIterator);
// Source is a TypedArray with unmodified iterator behavior. Use the
// source object directly, taking advantage of the special-case code
// in TypedArrayCopyElements
finalLength = sourceTypedArray.length;
finalSource = source;
} label UseUserProvidedIterator {
// 6. If usingIterator is not undefined, then
// a. Let values be ? IterableToList(source, usingIterator).
// b. Let len be the number of elements in values.
const values: JSArray = IterableToList(source, usingIterator);
finalLength = Convert<uintptr>(values.length);
finalSource = values;
}
} label IteratorIsUndefined {
// 7. NOTE: source is not an Iterable so assume it is already an
// array-like object.
// 8. Let arrayLike be ! ToObject(source).
const arrayLike: JSReceiver = ToObject_Inline(context, source);
// 9. Let len be ? LengthOfArrayLike(arrayLike).
const length = GetLengthProperty(arrayLike);
try {
finalLength = ChangeSafeIntegerNumberToUintPtr(length)
otherwise IfInvalidLength;
finalSource = arrayLike;
} label IfInvalidLength deferred {
ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, length);
}
} label IteratorNotCallable(_value: JSAny) deferred {
ThrowTypeError(MessageTemplate::kIteratorSymbolNonCallable);
finalLength = Convert<uintptr>(values.length);
finalSource = values;
}
} label IteratorIsUndefined {
// 7. NOTE: source is not an Iterable so assume it is already an
// array-like object.
const finalLengthNum = Convert<Number>(finalLength);
// 8. Let arrayLike be ! ToObject(source).
const arrayLike: JSReceiver = ToObject_Inline(context, source);
// 6c/10. Let targetObj be ? TypedArrayCreate(C, «len»).
const targetObj = TypedArrayCreateByLength(
constructor, finalLengthNum, kBuiltinNameFrom);
// 9. Let len be ? LengthOfArrayLike(arrayLike).
const length = GetLengthProperty(arrayLike);
if (!mapping) {
// Fast path.
if (finalLength != 0) {
// Call runtime.
TypedArrayCopyElements(
context, targetObj, finalSource, finalLengthNum);
}
return targetObj;
try {
finalLength = ChangeSafeIntegerNumberToUintPtr(length)
otherwise IfInvalidLength;
finalSource = arrayLike;
} label IfInvalidLength deferred {
ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, length);
}
// Slow path.
} label IteratorNotCallable(_value: JSAny) deferred {
ThrowTypeError(MessageTemplate::kIteratorSymbolNonCallable);
}
const mapfn: Callable = Cast<Callable>(mapfnObj) otherwise unreachable;
const accessor: TypedArrayAccessor =
GetTypedArrayAccessor(targetObj.elements_kind);
const finalLengthNum = Convert<Number>(finalLength);
// 6d-6e and 11-12.
// 11. Let k be 0.
// 12. Repeat, while k < len
for (let k: uintptr = 0; k < finalLength; k++) {
// 12a. Let Pk be ! ToString(k).
const kNum = Convert<Number>(k);
// 6c/10. Let targetObj be ? TypedArrayCreate(C, «len»).
const targetObj =
TypedArrayCreateByLength(constructor, finalLengthNum, kBuiltinNameFrom);
// 12b. Let kValue be ? Get(arrayLike, Pk).
const kValue: JSAny = GetProperty(finalSource, kNum);
let mappedValue: JSAny;
// 12c. If mapping is true, then
if (mapping) {
// i. Let mappedValue be ? Call(mapfn, T, « kValue, k »).
mappedValue = Call(context, mapfn, thisArg, kValue, kNum);
} else {
// 12d. Else, let mappedValue be kValue.
mappedValue = kValue;
}
// 12e. Perform ? Set(targetObj, Pk, mappedValue, true).
// Buffer may be detached during executing ToNumber/ToBigInt.
accessor.StoreJSAny(context, targetObj, k, mappedValue)
otherwise IfDetached;
// 12f. Set k to k + 1. (done by the loop).
if (!mapping) {
// Fast path.
if (finalLength != 0) {
// Call runtime.
TypedArrayCopyElements(context, targetObj, finalSource, finalLengthNum);
}
return targetObj;
} label NotConstructor deferred {
ThrowTypeError(MessageTemplate::kNotConstructor, receiver);
} label IfDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameFrom);
}
// Slow path.
const mapfn: Callable = Cast<Callable>(mapfnObj) otherwise unreachable;
const accessor: TypedArrayAccessor =
GetTypedArrayAccessor(targetObj.elements_kind);
// 6d-6e and 11-12.
// 11. Let k be 0.
// 12. Repeat, while k < len
for (let k: uintptr = 0; k < finalLength; k++) {
// 12a. Let Pk be ! ToString(k).
const kNum = Convert<Number>(k);
// 12b. Let kValue be ? Get(arrayLike, Pk).
const kValue: JSAny = GetProperty(finalSource, kNum);
let mappedValue: JSAny;
// 12c. If mapping is true, then
if (mapping) {
// i. Let mappedValue be ? Call(mapfn, T, « kValue, k »).
mappedValue = Call(context, mapfn, thisArg, kValue, kNum);
} else {
// 12d. Else, let mappedValue be kValue.
mappedValue = kValue;
}
// 12e. Perform ? Set(targetObj, Pk, mappedValue, true).
// Buffer may be detached during executing ToNumber/ToBigInt.
accessor.StoreJSAny(context, targetObj, k, mappedValue)
otherwise IfDetached;
// 12f. Set k to k + 1. (done by the loop).
}
return targetObj;
} label NotConstructor deferred {
ThrowTypeError(MessageTemplate::kNotConstructor, receiver);
} label IfDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameFrom);
}
}
}

View File

@ -5,50 +5,50 @@
#include 'src/builtins/builtins-typed-array-gen.h'
namespace typed_array {
const kBuiltinNameOf: constexpr string = '%TypedArray%.of';
const kBuiltinNameOf: constexpr string = '%TypedArray%.of';
// %TypedArray%.of ( ...items )
// https://tc39.github.io/ecma262/#sec-%typedarray%.of
transitioning javascript builtin
TypedArrayOf(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): JSTypedArray {
try {
// 1. Let len be the actual number of arguments passed to this function.
const len: uintptr = Unsigned(arguments.length);
// %TypedArray%.of ( ...items )
// https://tc39.github.io/ecma262/#sec-%typedarray%.of
transitioning javascript builtin
TypedArrayOf(js-implicit context: NativeContext, receiver: JSAny)(...arguments):
JSTypedArray {
try {
// 1. Let len be the actual number of arguments passed to this function.
const len: uintptr = Unsigned(arguments.length);
// 2. Let items be the List of arguments passed to this function.
// 2. Let items be the List of arguments passed to this function.
// 3. Let C be the this value.
// 4. If IsConstructor(C) is false, throw a TypeError exception.
const constructor = Cast<Constructor>(receiver) otherwise NotConstructor;
// 3. Let C be the this value.
// 4. If IsConstructor(C) is false, throw a TypeError exception.
const constructor = Cast<Constructor>(receiver) otherwise NotConstructor;
// 5. Let newObj be ? TypedArrayCreate(C, len).
const newObj = TypedArrayCreateByLength(
constructor, Convert<Number>(len), kBuiltinNameOf);
// 5. Let newObj be ? TypedArrayCreate(C, len).
const newObj = TypedArrayCreateByLength(
constructor, Convert<Number>(len), kBuiltinNameOf);
const accessor: TypedArrayAccessor =
GetTypedArrayAccessor(newObj.elements_kind);
const accessor: TypedArrayAccessor =
GetTypedArrayAccessor(newObj.elements_kind);
// 6. Let k be 0.
// 7. Repeat, while k < len
for (let k: uintptr = 0; k < len; k++) {
// 7a. Let kValue be items[k].
const kValue: JSAny = arguments[Signed(k)];
// 6. Let k be 0.
// 7. Repeat, while k < len
for (let k: uintptr = 0; k < len; k++) {
// 7a. Let kValue be items[k].
const kValue: JSAny = arguments[Signed(k)];
// 7b. Let Pk be ! ToString(k).
// 7c. Perform ? Set(newObj, Pk, kValue, true).
// Buffer may be detached during executing ToNumber/ToBigInt.
accessor.StoreJSAny(context, newObj, k, kValue) otherwise IfDetached;
// 7b. Let Pk be ! ToString(k).
// 7c. Perform ? Set(newObj, Pk, kValue, true).
// Buffer may be detached during executing ToNumber/ToBigInt.
accessor.StoreJSAny(context, newObj, k, kValue) otherwise IfDetached;
// 7d. Increase k by 1. (done by the loop).
}
// 8. Return newObj.
return newObj;
} label NotConstructor deferred {
ThrowTypeError(MessageTemplate::kNotConstructor, receiver);
} label IfDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameOf);
// 7d. Increase k by 1. (done by the loop).
}
// 8. Return newObj.
return newObj;
} label NotConstructor deferred {
ThrowTypeError(MessageTemplate::kNotConstructor, receiver);
} label IfDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameOf);
}
}
}

View File

@ -5,62 +5,61 @@
#include 'src/builtins/builtins-typed-array-gen.h'
namespace typed_array {
const kBuiltinNameReduce: constexpr string = '%TypedArray%.prototype.reduce';
const kBuiltinNameReduce: constexpr string = '%TypedArray%.prototype.reduce';
transitioning macro ReduceAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
initialValue: JSAny|TheHole): JSAny {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
let accumulator = initialValue;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
typeswitch (accumulator) {
case (TheHole): {
accumulator = value;
}
case (accumulatorNotHole: JSAny): {
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
accumulator = Call(
context, callbackfn, Undefined, accumulatorNotHole, value,
Convert<Number>(k), witness.GetStable());
}
}
}
transitioning macro ReduceAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
initialValue: JSAny|TheHole): JSAny {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
let accumulator = initialValue;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
typeswitch (accumulator) {
case (TheHole): {
ThrowTypeError(MessageTemplate::kReduceNoInitial, kBuiltinNameReduce);
accumulator = value;
}
case (accumulator: JSAny): {
return accumulator;
case (accumulatorNotHole: JSAny): {
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
accumulator = Call(
context, callbackfn, Undefined, accumulatorNotHole, value,
Convert<Number>(k), witness.GetStable());
}
}
}
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.reduce
transitioning javascript builtin
TypedArrayPrototypeReduce(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSAny {
// arguments[0] = callback
// arguments[1] = initialValue.
try {
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise NotTypedArray;
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const initialValue = arguments.length >= 2 ? arguments[1] : TheHole;
return ReduceAllElements(uarray, callbackfn, initialValue);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameReduce);
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameReduce);
typeswitch (accumulator) {
case (TheHole): {
ThrowTypeError(MessageTemplate::kReduceNoInitial, kBuiltinNameReduce);
}
case (accumulator: JSAny): {
return accumulator;
}
}
}
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.reduce
transitioning javascript builtin
TypedArrayPrototypeReduce(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// arguments[0] = callback
// arguments[1] = initialValue.
try {
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise NotTypedArray;
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const initialValue = arguments.length >= 2 ? arguments[1] : TheHole;
return ReduceAllElements(uarray, callbackfn, initialValue);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameReduce);
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameReduce);
}
}
}

View File

@ -5,66 +5,65 @@
#include 'src/builtins/builtins-typed-array-gen.h'
namespace typed_array {
const kBuiltinNameReduceRight: constexpr string =
'%TypedArray%.prototype.reduceRight';
const kBuiltinNameReduceRight: constexpr string =
'%TypedArray%.prototype.reduceRight';
transitioning macro ReduceRightAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
initialValue: JSAny|TheHole): JSAny {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
let accumulator = initialValue;
for (let k: uintptr = length; k-- > 0;) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
typeswitch (accumulator) {
case (TheHole): {
accumulator = value;
}
case (accumulatorNotHole: JSAny): {
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
accumulator = Call(
context, callbackfn, Undefined, accumulatorNotHole, value,
Convert<Number>(k), witness.GetStable());
}
}
}
transitioning macro ReduceRightAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
initialValue: JSAny|TheHole): JSAny {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
let accumulator = initialValue;
for (let k: uintptr = length; k-- > 0;) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
typeswitch (accumulator) {
case (TheHole): {
ThrowTypeError(
MessageTemplate::kReduceNoInitial, kBuiltinNameReduceRight);
accumulator = value;
}
case (accumulator: JSAny): {
return accumulator;
case (accumulatorNotHole: JSAny): {
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
accumulator = Call(
context, callbackfn, Undefined, accumulatorNotHole, value,
Convert<Number>(k), witness.GetStable());
}
}
}
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.reduceright
transitioning javascript builtin
TypedArrayPrototypeReduceRight(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSAny {
// arguments[0] = callback
// arguments[1] = initialValue.
try {
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise NotTypedArray;
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const initialValue = arguments.length >= 2 ? arguments[1] : TheHole;
return ReduceRightAllElements(uarray, callbackfn, initialValue);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameReduceRight);
} label IsDetached deferred {
typeswitch (accumulator) {
case (TheHole): {
ThrowTypeError(
MessageTemplate::kDetachedOperation, kBuiltinNameReduceRight);
MessageTemplate::kReduceNoInitial, kBuiltinNameReduceRight);
}
case (accumulator: JSAny): {
return accumulator;
}
}
}
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.reduceright
transitioning javascript builtin
TypedArrayPrototypeReduceRight(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// arguments[0] = callback
// arguments[1] = initialValue.
try {
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise NotTypedArray;
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const initialValue = arguments.length >= 2 ? arguments[1] : TheHole;
return ReduceRightAllElements(uarray, callbackfn, initialValue);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameReduceRight);
} label IsDetached deferred {
ThrowTypeError(
MessageTemplate::kDetachedOperation, kBuiltinNameReduceRight);
}
}
}

View File

@ -5,307 +5,306 @@
#include 'src/builtins/builtins-typed-array-gen.h'
namespace typed_array {
const kBuiltinNameSet: constexpr string = '%TypedArray%.prototype.set';
const kBuiltinNameSet: constexpr string = '%TypedArray%.prototype.set';
extern runtime TypedArraySet(Context, JSTypedArray, Object, Number, Number):
void;
extern runtime TypedArraySet(
Context, JSTypedArray, Object, Number, Number): void;
extern macro
TypedArrayBuiltinsAssembler::CallCCopyFastNumberJSArrayElementsToTypedArray(
Context,
FastJSArray, // source
AttachedJSTypedArray, // dest
uintptr, // sourceLength
uintptr // destOffset
): void;
extern macro
TypedArrayBuiltinsAssembler::CallCCopyFastNumberJSArrayElementsToTypedArray(
Context,
FastJSArray, // source
AttachedJSTypedArray, // dest
uintptr, // sourceLength
uintptr // destOffset
): void;
extern macro
TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray(
AttachedJSTypedArray, // source
AttachedJSTypedArray, // dest
uintptr, // sourceLength
uintptr // destOffset
): void;
extern macro
TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray(
AttachedJSTypedArray, // source
AttachedJSTypedArray, // dest
uintptr, // sourceLength
uintptr // destOffset
): void;
// %TypedArray%.prototype.set ( overloaded [ , offset ] )
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-overloaded-offset
transitioning javascript builtin
TypedArrayPrototypeSet(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): JSAny {
// Steps 2-8 are the same for
// %TypedArray%.prototype.set ( array [ , offset ] ) and
// %TypedArray%.prototype.set ( typedArray [ , offset ] ) overloads.
// %TypedArray%.prototype.set ( overloaded [ , offset ] )
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-overloaded-offset
transitioning javascript builtin
TypedArrayPrototypeSet(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// Steps 2-8 are the same for
// %TypedArray%.prototype.set ( array [ , offset ] ) and
// %TypedArray%.prototype.set ( typedArray [ , offset ] ) overloads.
let target: JSTypedArray;
try {
// 2. Let target be the this value.
// 3. Perform ? RequireInternalSlot(target, [[TypedArrayName]]).
// 4. Assert: target has a [[ViewedArrayBuffer]] internal slot.
target = Cast<JSTypedArray>(receiver) otherwise NotTypedArray;
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameSet);
}
try {
// 5. Let targetOffset be ? ToInteger(offset).
// 6. If targetOffset < 0, throw a RangeError exception.
let targetOffsetOverflowed: bool = false;
let targetOffset: uintptr = 0;
if (arguments.length > 1) {
const offsetArg = arguments[1];
try {
targetOffset = ToUintPtr(offsetArg)
// On values less than zero throw RangeError immediately.
otherwise OffsetOutOfBounds,
// On UintPtr or SafeInteger range overflow throw RangeError after
// performing observable steps to follow the spec.
OffsetOverflow, OffsetOverflow;
} label OffsetOverflow {
targetOffsetOverflowed = true;
}
} else {
// If the offset argument is not provided then the targetOffset is 0.
}
// 7. Let targetBuffer be target.[[ViewedArrayBuffer]].
// 8. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError
// exception.
const utarget = typed_array::EnsureAttached(target) otherwise IsDetached;
const overloadedArg = arguments[0];
try {
// 1. Choose 22.2.3.23.2 or 22.2.3.23.1 depending on whether the
// overloadedArg has a [[TypedArrayName]] internal slot.
// If it does, the definition in 22.2.3.23.2 applies.
// If it does not, the definition in 22.2.3.23.1 applies.
const typedArray =
Cast<JSTypedArray>(overloadedArg) otherwise NotTypedArray;
// Step 9 is not observable, do it later.
// 10. Let srcBuffer be typedArray.[[ViewedArrayBuffer]].
// 11. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError
// exception.
const utypedArray =
typed_array::EnsureAttached(typedArray) otherwise IsDetached;
TypedArrayPrototypeSetTypedArray(
utarget, utypedArray, targetOffset, targetOffsetOverflowed)
otherwise OffsetOutOfBounds;
return Undefined;
} label NotTypedArray deferred {
TypedArrayPrototypeSetArray(
utarget, overloadedArg, targetOffset, targetOffsetOverflowed)
otherwise OffsetOutOfBounds, IsDetached;
return Undefined;
}
} label OffsetOutOfBounds deferred {
ThrowRangeError(MessageTemplate::kTypedArraySetOffsetOutOfBounds);
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSet);
}
let target: JSTypedArray;
try {
// 2. Let target be the this value.
// 3. Perform ? RequireInternalSlot(target, [[TypedArrayName]]).
// 4. Assert: target has a [[ViewedArrayBuffer]] internal slot.
target = Cast<JSTypedArray>(receiver) otherwise NotTypedArray;
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameSet);
}
// %TypedArray%.prototype.set ( array [ , offset ] )
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-array-offset
transitioning macro
TypedArrayPrototypeSetArray(implicit context: Context, receiver: JSAny)(
target: JSTypedArray, arrayArg: JSAny, targetOffset: uintptr,
targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds,
IfDetached {
// Steps 9-13 are not observable, do them later.
// TODO(v8:8906): This ported behaviour is an observable spec violation and
// the comment below seems to be outdated. Consider removing this code.
try {
const _arrayArgNum = Cast<Number>(arrayArg) otherwise NotNumber;
// For number as a first argument, throw TypeError instead of silently
// ignoring the call, so that users know they did something wrong.
// (Consistent with Firefox and Blink/WebKit)
ThrowTypeError(MessageTemplate::kInvalidArgument);
} label NotNumber {
// Proceed to step 14.
try {
// 5. Let targetOffset be ? ToInteger(offset).
// 6. If targetOffset < 0, throw a RangeError exception.
let targetOffsetOverflowed: bool = false;
let targetOffset: uintptr = 0;
if (arguments.length > 1) {
const offsetArg = arguments[1];
try {
targetOffset = ToUintPtr(offsetArg)
// On values less than zero throw RangeError immediately.
otherwise OffsetOutOfBounds,
// On UintPtr or SafeInteger range overflow throw RangeError after
// performing observable steps to follow the spec.
OffsetOverflow, OffsetOverflow;
} label OffsetOverflow {
targetOffsetOverflowed = true;
}
} else {
// If the offset argument is not provided then the targetOffset is 0.
}
// 14. Let src be ? ToObject(array).
const src: JSReceiver = ToObject_Inline(context, arrayArg);
// 15. Let srcLength be ? LengthOfArrayLike(src).
const srcLengthNum: Number = GetLengthProperty(src);
if (targetOffsetOverflowed) goto IfOffsetOutOfBounds;
// 9. Let targetLength be target.[[ArrayLength]].
const targetLength = target.length;
// 16. If srcLength + targetOffset > targetLength, throw a RangeError
// 7. Let targetBuffer be target.[[ViewedArrayBuffer]].
// 8. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError
// exception.
const srcLength = ChangeSafeIntegerNumberToUintPtr(srcLengthNum)
otherwise IfOffsetOutOfBounds;
CheckIntegerIndexAdditionOverflow(srcLength, targetOffset, targetLength)
otherwise IfOffsetOutOfBounds;
const utarget = typed_array::EnsureAttached(target) otherwise IsDetached;
const overloadedArg = arguments[0];
try {
// 1. Choose 22.2.3.23.2 or 22.2.3.23.1 depending on whether the
// overloadedArg has a [[TypedArrayName]] internal slot.
// If it does, the definition in 22.2.3.23.2 applies.
// If it does not, the definition in 22.2.3.23.1 applies.
const typedArray =
Cast<JSTypedArray>(overloadedArg) otherwise NotTypedArray;
// Step 9 is not observable, do it later.
// 10. Let srcBuffer be typedArray.[[ViewedArrayBuffer]].
// 11. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError
// exception.
const utypedArray =
typed_array::EnsureAttached(typedArray) otherwise IsDetached;
TypedArrayPrototypeSetTypedArray(
utarget, utypedArray, targetOffset, targetOffsetOverflowed)
otherwise OffsetOutOfBounds;
return Undefined;
} label NotTypedArray deferred {
TypedArrayPrototypeSetArray(
utarget, overloadedArg, targetOffset, targetOffsetOverflowed)
otherwise OffsetOutOfBounds, IsDetached;
return Undefined;
}
} label OffsetOutOfBounds deferred {
ThrowRangeError(MessageTemplate::kTypedArraySetOffsetOutOfBounds);
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSet);
}
}
// %TypedArray%.prototype.set ( array [ , offset ] )
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-array-offset
transitioning macro
TypedArrayPrototypeSetArray(implicit context: Context, receiver: JSAny)(
target: JSTypedArray, arrayArg: JSAny, targetOffset: uintptr,
targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds,
IfDetached {
// Steps 9-13 are not observable, do them later.
// TODO(v8:8906): This ported behaviour is an observable spec violation and
// the comment below seems to be outdated. Consider removing this code.
try {
const _arrayArgNum = Cast<Number>(arrayArg) otherwise NotNumber;
// For number as a first argument, throw TypeError instead of silently
// ignoring the call, so that users know they did something wrong.
// (Consistent with Firefox and Blink/WebKit)
ThrowTypeError(MessageTemplate::kInvalidArgument);
} label NotNumber {
// Proceed to step 14.
}
// 14. Let src be ? ToObject(array).
const src: JSReceiver = ToObject_Inline(context, arrayArg);
// 15. Let srcLength be ? LengthOfArrayLike(src).
const srcLengthNum: Number = GetLengthProperty(src);
if (targetOffsetOverflowed) goto IfOffsetOutOfBounds;
// 9. Let targetLength be target.[[ArrayLength]].
const targetLength = target.length;
// 16. If srcLength + targetOffset > targetLength, throw a RangeError
// exception.
const srcLength = ChangeSafeIntegerNumberToUintPtr(srcLengthNum)
otherwise IfOffsetOutOfBounds;
CheckIntegerIndexAdditionOverflow(srcLength, targetOffset, targetLength)
otherwise IfOffsetOutOfBounds;
// All the obvervable side effects are executed, so there's nothing else
// to do with the empty source array.
if (srcLength == 0) return;
// 10. Let targetName be the String value of target.[[TypedArrayName]].
// 11. Let targetElementSize be the Element Size value specified in
// Table 62 for targetName.
// 12. Let targetType be the Element Type value in Table 62 for
// targetName.
try {
// BigInt typed arrays are not handled by
// CopyFastNumberJSArrayElementsToTypedArray.
if (IsBigInt64ElementsKind(target.elements_kind)) goto IfSlow;
const fastSrc: FastJSArray = Cast<FastJSArray>(src) otherwise goto IfSlow;
const srcKind: ElementsKind = fastSrc.map.elements_kind;
// CopyFastNumberJSArrayElementsToTypedArray() can be used only with the
// following elements kinds:
// PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS,
// HOLEY_DOUBLE_ELEMENTS.
if (IsElementsKindInRange(
srcKind, ElementsKind::PACKED_SMI_ELEMENTS,
ElementsKind::HOLEY_SMI_ELEMENTS) ||
IsElementsKindInRange(
srcKind, ElementsKind::PACKED_DOUBLE_ELEMENTS,
ElementsKind::HOLEY_DOUBLE_ELEMENTS)) {
const utarget = typed_array::EnsureAttached(target) otherwise IfDetached;
CallCCopyFastNumberJSArrayElementsToTypedArray(
context, fastSrc, utarget, srcLength, targetOffset);
} else {
goto IfSlow;
}
} label IfSlow deferred {
TypedArraySet(
context, target, src, srcLengthNum, Convert<Number>(targetOffset));
}
}
// %TypedArray%.prototype.set ( typedArray [ , offset ] )
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-typedarray-offset
transitioning macro
TypedArrayPrototypeSetTypedArray(implicit context: Context, receiver: JSAny)(
target: AttachedJSTypedArray, typedArray: AttachedJSTypedArray,
targetOffset: uintptr,
targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds {
// Steps 12-20 are not observable, so we can handle offset overflow
// at step 21 here.
if (targetOffsetOverflowed) goto IfOffsetOutOfBounds;
// 9. Let targetLength be target.[[ArrayLength]].
const targetLength = target.length;
// 19. Let srcLength be typedArray.[[ArrayLength]].
const srcLength: uintptr = typedArray.length;
// Steps 12-20 are not observable, so we can do step 21 here.
// 21. If srcLength + targetOffset > targetLength, throw a RangeError
// exception.
CheckIntegerIndexAdditionOverflow(srcLength, targetOffset, targetLength)
otherwise IfOffsetOutOfBounds;
// 12. Let targetName be the String value of target.[[TypedArrayName]].
// 13. Let targetType be the Element Type value in Table 62 for
// targetName.
// 14. Let targetElementSize be the Element Size value specified in
// Table 62 for targetName.
const targetElementsInfo = GetTypedArrayElementsInfo(target);
// 16. Let srcName be the String value of typedArray.[[TypedArrayName]].
// 17. Let srcType be the Element Type value in Table 62 for srcName.
// 18. Let srcElementSize be the Element Size value specified in
// Table 62 for srcName.
const srcKind: ElementsKind = typedArray.elements_kind;
// const srcElementsInfo = GetTypedArrayElementsInfo(typedArray);
// We skip steps 23-25 because both memmove and
// CopyTypedArrayElementsToTypedArray() properly handle overlapping
// regions.
// 23. If both IsSharedArrayBuffer(srcBuffer) and
// IsSharedArrayBuffer(targetBuffer) are true, then
// 23a. If srcBuffer.[[ArrayBufferData]] and
// targetBuffer.[[ArrayBufferData]] are the same Shared Data Block
// values, let same be true; else let same be false.
// 24. Else, let same be SameValue(srcBuffer, targetBuffer).
// 25. If same is true, then
// a. Let srcByteLength be typedArray.[[ByteLength]].
// b. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset,
// srcByteLength, %ArrayBuffer%).
// c. NOTE: %ArrayBuffer% is used to clone srcBuffer because is it known
// to not have any observable side-effects.
// d. Let srcByteIndex be 0.
try {
// Use memmove if possible.
if (srcKind != targetElementsInfo.kind) {
// Uint8/Uint8Clamped elements could still be copied with memmove.
if (!IsUint8ElementsKind(srcKind) ||
!IsUint8ElementsKind(targetElementsInfo.kind)) {
goto IfSlow;
}
}
// All the obvervable side effects are executed, so there's nothing else
// to do with the empty source array.
if (srcLength == 0) return;
// 10. Let targetName be the String value of target.[[TypedArrayName]].
// 11. Let targetElementSize be the Element Size value specified in
// Table 62 for targetName.
// 12. Let targetType be the Element Type value in Table 62 for
// targetName.
// Source and destination typed arrays have same elements kinds (modulo
// Uint8-Uint8Clamped difference) so we can use targetElementsInfo for
// calculations.
const countBytes: uintptr =
targetElementsInfo.CalculateByteLength(srcLength)
otherwise unreachable;
const startOffset: uintptr =
targetElementsInfo.CalculateByteLength(targetOffset)
otherwise unreachable;
const dstPtr: RawPtr = target.data_ptr + Convert<intptr>(startOffset);
try {
// BigInt typed arrays are not handled by
// CopyFastNumberJSArrayElementsToTypedArray.
if (IsBigInt64ElementsKind(target.elements_kind)) goto IfSlow;
assert(countBytes <= target.byte_length - startOffset);
assert(countBytes <= typedArray.byte_length);
const fastSrc: FastJSArray = Cast<FastJSArray>(src) otherwise goto IfSlow;
const srcKind: ElementsKind = fastSrc.map.elements_kind;
// CopyFastNumberJSArrayElementsToTypedArray() can be used only with the
// following elements kinds:
// PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS,
// HOLEY_DOUBLE_ELEMENTS.
if (IsElementsKindInRange(
srcKind, ElementsKind::PACKED_SMI_ELEMENTS,
ElementsKind::HOLEY_SMI_ELEMENTS) ||
IsElementsKindInRange(
srcKind, ElementsKind::PACKED_DOUBLE_ELEMENTS,
ElementsKind::HOLEY_DOUBLE_ELEMENTS)) {
const utarget =
typed_array::EnsureAttached(target) otherwise IfDetached;
CallCCopyFastNumberJSArrayElementsToTypedArray(
context, fastSrc, utarget, srcLength, targetOffset);
} else {
goto IfSlow;
}
} label IfSlow deferred {
TypedArraySet(
context, target, src, srcLengthNum, Convert<Number>(targetOffset));
}
}
// %TypedArray%.prototype.set ( typedArray [ , offset ] )
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-typedarray-offset
transitioning macro
TypedArrayPrototypeSetTypedArray(implicit context: Context, receiver: JSAny)(
target: AttachedJSTypedArray, typedArray: AttachedJSTypedArray,
targetOffset: uintptr,
targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds {
// Steps 12-20 are not observable, so we can handle offset overflow
// at step 21 here.
if (targetOffsetOverflowed) goto IfOffsetOutOfBounds;
// 9. Let targetLength be target.[[ArrayLength]].
const targetLength = target.length;
// 19. Let srcLength be typedArray.[[ArrayLength]].
const srcLength: uintptr = typedArray.length;
// Steps 12-20 are not observable, so we can do step 21 here.
// 21. If srcLength + targetOffset > targetLength, throw a RangeError
// exception.
CheckIntegerIndexAdditionOverflow(srcLength, targetOffset, targetLength)
otherwise IfOffsetOutOfBounds;
// 12. Let targetName be the String value of target.[[TypedArrayName]].
// 13. Let targetType be the Element Type value in Table 62 for
// targetName.
// 14. Let targetElementSize be the Element Size value specified in
// Table 62 for targetName.
const targetElementsInfo = GetTypedArrayElementsInfo(target);
// 16. Let srcName be the String value of typedArray.[[TypedArrayName]].
// 17. Let srcType be the Element Type value in Table 62 for srcName.
// 18. Let srcElementSize be the Element Size value specified in
// Table 62 for srcName.
const srcKind: ElementsKind = typedArray.elements_kind;
// const srcElementsInfo = GetTypedArrayElementsInfo(typedArray);
// We skip steps 23-25 because both memmove and
// CopyTypedArrayElementsToTypedArray() properly handle overlapping
// regions.
// 23. If both IsSharedArrayBuffer(srcBuffer) and
// IsSharedArrayBuffer(targetBuffer) are true, then
// 23a. If srcBuffer.[[ArrayBufferData]] and
// targetBuffer.[[ArrayBufferData]] are the same Shared Data Block
// values, let same be true; else let same be false.
// 24. Else, let same be SameValue(srcBuffer, targetBuffer).
// 25. If same is true, then
// a. Let srcByteLength be typedArray.[[ByteLength]].
// b. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset,
// srcByteLength, %ArrayBuffer%).
// c. NOTE: %ArrayBuffer% is used to clone srcBuffer because is it known
// to not have any observable side-effects.
// d. Let srcByteIndex be 0.
try {
// Use memmove if possible.
if (srcKind != targetElementsInfo.kind) {
// Uint8/Uint8Clamped elements could still be copied with memmove.
if (!IsUint8ElementsKind(srcKind) ||
!IsUint8ElementsKind(targetElementsInfo.kind)) {
goto IfSlow;
}
// 29. If srcType is the same as targetType, then
// a. NOTE: If srcType and targetType are the same, the transfer must
// be performed in a manner that preserves the bit-level encoding of
// the source data.
// b. Repeat, while targetByteIndex < limit
// i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, Uint8,
// true, Unordered).
// ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, Uint8,
// value, true, Unordered).
// iii. Set srcByteIndex to srcByteIndex + 1.
// iv. Set targetByteIndex to targetByteIndex + 1.
CallCMemmove(dstPtr, typedArray.data_ptr, countBytes);
} label IfSlow deferred {
// 22. If target.[[ContentType]] is not equal to
// typedArray.[[ContentType]], throw a TypeError exception.
if (IsBigInt64ElementsKind(srcKind) !=
IsBigInt64ElementsKind(targetElementsInfo.kind))
deferred {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
}
// All the obvervable side effects are executed, so there's nothing else
// to do with the empty source array.
if (srcLength == 0) return;
// All the obvervable side effects are executed, so there's nothing else
// to do with the empty source array.
if (srcLength == 0) return;
// Source and destination typed arrays have same elements kinds (modulo
// Uint8-Uint8Clamped difference) so we can use targetElementsInfo for
// calculations.
const countBytes: uintptr =
targetElementsInfo.CalculateByteLength(srcLength)
otherwise unreachable;
const startOffset: uintptr =
targetElementsInfo.CalculateByteLength(targetOffset)
otherwise unreachable;
const dstPtr: RawPtr = target.data_ptr + Convert<intptr>(startOffset);
assert(countBytes <= target.byte_length - startOffset);
assert(countBytes <= typedArray.byte_length);
// 29. If srcType is the same as targetType, then
// a. NOTE: If srcType and targetType are the same, the transfer must
// be performed in a manner that preserves the bit-level encoding of
// the source data.
// b. Repeat, while targetByteIndex < limit
// i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, Uint8,
// true, Unordered).
// ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, Uint8,
// value, true, Unordered).
// iii. Set srcByteIndex to srcByteIndex + 1.
// iv. Set targetByteIndex to targetByteIndex + 1.
CallCMemmove(dstPtr, typedArray.data_ptr, countBytes);
} label IfSlow deferred {
// 22. If target.[[ContentType]] is not equal to
// typedArray.[[ContentType]], throw a TypeError exception.
if (IsBigInt64ElementsKind(srcKind) !=
IsBigInt64ElementsKind(targetElementsInfo.kind))
deferred {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
}
// All the obvervable side effects are executed, so there's nothing else
// to do with the empty source array.
if (srcLength == 0) return;
// 30. Else,
// a. Repeat, while targetByteIndex < limit
// i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex,
// srcType, true, Unordered).
// ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex,
// targetType, value, true, Unordered).
// iii. Set srcByteIndex to srcByteIndex + srcElementSize.
// iv. Set targetByteIndex to targetByteIndex + targetElementSize.
CallCCopyTypedArrayElementsToTypedArray(
typedArray, target, srcLength, targetOffset);
}
// 30. Else,
// a. Repeat, while targetByteIndex < limit
// i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex,
// srcType, true, Unordered).
// ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex,
// targetType, value, true, Unordered).
// iii. Set srcByteIndex to srcByteIndex + srcElementSize.
// iv. Set targetByteIndex to targetByteIndex + targetElementSize.
CallCCopyTypedArrayElementsToTypedArray(
typedArray, target, srcLength, targetOffset);
}
}
}

View File

@ -5,100 +5,99 @@
#include 'src/builtins/builtins-typed-array-gen.h'
namespace typed_array {
const kBuiltinNameSlice: constexpr string = '%TypedArray%.prototype.slice';
const kBuiltinNameSlice: constexpr string = '%TypedArray%.prototype.slice';
extern macro TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsSlice(
JSTypedArray, JSTypedArray, uintptr, uintptr): void;
extern macro TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsSlice(
JSTypedArray, JSTypedArray, uintptr, uintptr): void;
macro FastCopy(
src: typed_array::AttachedJSTypedArray, dest: JSTypedArray, k: uintptr,
count: uintptr) labels IfSlow {
if (IsForceSlowPath()) goto IfSlow;
macro FastCopy(
src: typed_array::AttachedJSTypedArray, dest: JSTypedArray, k: uintptr,
count: uintptr) labels IfSlow {
if (IsForceSlowPath()) goto IfSlow;
const srcKind: ElementsKind = src.elements_kind;
const destInfo = typed_array::GetTypedArrayElementsInfo(dest);
const srcKind: ElementsKind = src.elements_kind;
const destInfo = typed_array::GetTypedArrayElementsInfo(dest);
// dest could be a different type from src or share the same buffer
// with the src because of custom species constructor. If the types
// of src and result array are the same and they are not sharing the
// same buffer, use memmove.
if (srcKind != destInfo.kind) goto IfSlow;
if (dest.buffer == src.buffer) {
goto IfSlow;
}
const countBytes: uintptr = destInfo.CalculateByteLength(count)
otherwise unreachable;
const startOffset: uintptr = destInfo.CalculateByteLength(k)
otherwise unreachable;
const srcPtr: RawPtr = src.data_ptr + Convert<intptr>(startOffset);
assert(countBytes <= dest.byte_length);
assert(countBytes <= src.byte_length - startOffset);
typed_array::CallCMemmove(dest.data_ptr, srcPtr, countBytes);
// dest could be a different type from src or share the same buffer
// with the src because of custom species constructor. If the types
// of src and result array are the same and they are not sharing the
// same buffer, use memmove.
if (srcKind != destInfo.kind) goto IfSlow;
if (dest.buffer == src.buffer) {
goto IfSlow;
}
macro SlowCopy(implicit context: Context)(
src: JSTypedArray, dest: JSTypedArray, k: uintptr, final: uintptr) {
if (typed_array::IsBigInt64ElementsKind(src.elements_kind) !=
typed_array::IsBigInt64ElementsKind(dest.elements_kind))
deferred {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
}
const countBytes: uintptr = destInfo.CalculateByteLength(count)
otherwise unreachable;
const startOffset: uintptr = destInfo.CalculateByteLength(k)
otherwise unreachable;
const srcPtr: RawPtr = src.data_ptr + Convert<intptr>(startOffset);
CallCCopyTypedArrayElementsSlice(src, dest, k, final);
}
assert(countBytes <= dest.byte_length);
assert(countBytes <= src.byte_length - startOffset);
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.slice
transitioning javascript builtin TypedArrayPrototypeSlice(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSAny {
// arguments[0] = start
// arguments[1] = end
// 1. Let O be the this value.
// 2. Perform ? ValidateTypedArray(O).
const src: JSTypedArray =
ValidateTypedArray(context, receiver, kBuiltinNameSlice);
// 3. Let len be O.[[ArrayLength]].
const len: uintptr = src.length;
// 4. Let relativeStart be ? ToInteger(start).
// 5. If relativeStart < 0, let k be max((len + relativeStart), 0);
// else let k be min(relativeStart, len).
const start = arguments[0];
const k: uintptr =
start != Undefined ? ConvertToRelativeIndex(start, len) : 0;
// 6. If end is undefined, let relativeEnd be len;
// else let relativeEnd be ? ToInteger(end).
// 7. If relativeEnd < 0, let final be max((len + relativeEnd), 0);
// else let final be min(relativeEnd, len).
const end = arguments[1];
const final: uintptr =
end != Undefined ? ConvertToRelativeIndex(end, len) : len;
// 8. Let count be max(final - k, 0).
const count: uintptr = Unsigned(IntPtrMax(Signed(final - k), 0));
// 9. Let A be ? TypedArraySpeciesCreate(O, « count »).
const dest: JSTypedArray =
TypedArraySpeciesCreateByLength(kBuiltinNameSlice, src, count);
if (count > 0) {
try {
const srcAttached = typed_array::EnsureAttached(src)
otherwise IfDetached;
FastCopy(srcAttached, dest, k, count) otherwise IfSlow;
} label IfDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSlice);
} label IfSlow deferred {
SlowCopy(src, dest, k, final);
}
}
return dest;
}
typed_array::CallCMemmove(dest.data_ptr, srcPtr, countBytes);
}
macro SlowCopy(implicit context: Context)(
src: JSTypedArray, dest: JSTypedArray, k: uintptr, final: uintptr) {
if (typed_array::IsBigInt64ElementsKind(src.elements_kind) !=
typed_array::IsBigInt64ElementsKind(dest.elements_kind))
deferred {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
}
CallCCopyTypedArrayElementsSlice(src, dest, k, final);
}
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.slice
transitioning javascript builtin TypedArrayPrototypeSlice(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// arguments[0] = start
// arguments[1] = end
// 1. Let O be the this value.
// 2. Perform ? ValidateTypedArray(O).
const src: JSTypedArray =
ValidateTypedArray(context, receiver, kBuiltinNameSlice);
// 3. Let len be O.[[ArrayLength]].
const len: uintptr = src.length;
// 4. Let relativeStart be ? ToInteger(start).
// 5. If relativeStart < 0, let k be max((len + relativeStart), 0);
// else let k be min(relativeStart, len).
const start = arguments[0];
const k: uintptr =
start != Undefined ? ConvertToRelativeIndex(start, len) : 0;
// 6. If end is undefined, let relativeEnd be len;
// else let relativeEnd be ? ToInteger(end).
// 7. If relativeEnd < 0, let final be max((len + relativeEnd), 0);
// else let final be min(relativeEnd, len).
const end = arguments[1];
const final: uintptr =
end != Undefined ? ConvertToRelativeIndex(end, len) : len;
// 8. Let count be max(final - k, 0).
const count: uintptr = Unsigned(IntPtrMax(Signed(final - k), 0));
// 9. Let A be ? TypedArraySpeciesCreate(O, « count »).
const dest: JSTypedArray =
TypedArraySpeciesCreateByLength(kBuiltinNameSlice, src, count);
if (count > 0) {
try {
const srcAttached = typed_array::EnsureAttached(src)
otherwise IfDetached;
FastCopy(srcAttached, dest, k, count) otherwise IfSlow;
} label IfDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSlice);
} label IfSlow deferred {
SlowCopy(src, dest, k, final);
}
}
return dest;
}
}

View File

@ -5,49 +5,49 @@
#include 'src/builtins/builtins-typed-array-gen.h'
namespace typed_array {
const kBuiltinNameSome: constexpr string = '%TypedArray%.prototype.some';
const kBuiltinNameSome: constexpr string = '%TypedArray%.prototype.some';
transitioning macro SomeAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): Boolean {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const result = Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
if (ToBoolean(result)) {
return True;
}
transitioning macro SomeAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): Boolean {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const result = Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
if (ToBoolean(result)) {
return True;
}
return False;
}
return False;
}
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.some
transitioning javascript builtin
TypedArrayPrototypeSome(js-implicit context: NativeContext, receiver: JSAny)(
...arguments): JSAny {
// arguments[0] = callback
// arguments[1] = thisArg.
try {
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise NotTypedArray;
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.some
transitioning javascript builtin
TypedArrayPrototypeSome(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// arguments[0] = callback
// arguments[1] = thisArg.
try {
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise NotTypedArray;
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const thisArg = arguments[1];
return SomeAllElements(uarray, callbackfn, thisArg);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameSome);
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSome);
}
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const thisArg = arguments[1];
return SomeAllElements(uarray, callbackfn, thisArg);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameSome);
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSome);
}
}
}

View File

@ -5,141 +5,140 @@
#include 'src/builtins/builtins-typed-array-gen.h'
namespace typed_array {
const kBuiltinNameSort: constexpr string = '%TypedArray%.prototype.sort';
const kBuiltinNameSort: constexpr string = '%TypedArray%.prototype.sort';
extern runtime TypedArraySortFast(Context, JSAny): JSTypedArray;
extern runtime TypedArraySortFast(Context, JSAny): JSTypedArray;
transitioning macro CallCompare(
implicit context: Context, array: JSTypedArray,
comparefn: Callable)(a: JSAny, b: JSAny): Number {
// a. Let v be ? ToNumber(? Call(comparefn, undefined, x, y)).
const v: Number =
ToNumber_Inline(Call(context, comparefn, Undefined, a, b));
transitioning macro CallCompare(
implicit context: Context, array: JSTypedArray, comparefn: Callable)(
a: JSAny, b: JSAny): Number {
// a. Let v be ? ToNumber(? Call(comparefn, undefined, x, y)).
const v: Number = ToNumber_Inline(Call(context, comparefn, Undefined, a, b));
// b. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(array.buffer)) {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSort);
}
// c. If v is NaN, return +0.
if (NumberIsNaN(v)) return 0;
// d. return v.
return v;
// b. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(array.buffer)) {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSort);
}
// Merges two sorted runs [from, middle) and [middle, to)
// from "source" into "target".
transitioning macro
TypedArrayMerge(
implicit context: Context, array: JSTypedArray, comparefn: Callable)(
source: FixedArray, from: uintptr, middle: uintptr, to: uintptr,
target: FixedArray) {
let left: uintptr = from;
let right: uintptr = middle;
// c. If v is NaN, return +0.
if (NumberIsNaN(v)) return 0;
for (let targetIndex: uintptr = from; targetIndex < to; ++targetIndex) {
if (left < middle && right >= to) {
// If the left run has elements, but the right does not, we take
// from the left.
target.objects[targetIndex] = source.objects[left++];
} else if (left < middle) {
// If both have elements, we need to compare.
const leftElement = UnsafeCast<JSAny>(source.objects[left]);
const rightElement = UnsafeCast<JSAny>(source.objects[right]);
if (CallCompare(leftElement, rightElement) <= 0) {
target.objects[targetIndex] = leftElement;
left++;
} else {
target.objects[targetIndex] = rightElement;
right++;
}
// d. return v.
return v;
}
// Merges two sorted runs [from, middle) and [middle, to)
// from "source" into "target".
transitioning macro
TypedArrayMerge(
implicit context: Context, array: JSTypedArray, comparefn: Callable)(
source: FixedArray, from: uintptr, middle: uintptr, to: uintptr,
target: FixedArray) {
let left: uintptr = from;
let right: uintptr = middle;
for (let targetIndex: uintptr = from; targetIndex < to; ++targetIndex) {
if (left < middle && right >= to) {
// If the left run has elements, but the right does not, we take
// from the left.
target.objects[targetIndex] = source.objects[left++];
} else if (left < middle) {
// If both have elements, we need to compare.
const leftElement = UnsafeCast<JSAny>(source.objects[left]);
const rightElement = UnsafeCast<JSAny>(source.objects[right]);
if (CallCompare(leftElement, rightElement) <= 0) {
target.objects[targetIndex] = leftElement;
left++;
} else {
// No elements on the left, but the right does, so we take
// from the right.
assert(left == middle);
target.objects[targetIndex] = source.objects[right++];
target.objects[targetIndex] = rightElement;
right++;
}
} else {
// No elements on the left, but the right does, so we take
// from the right.
assert(left == middle);
target.objects[targetIndex] = source.objects[right++];
}
}
transitioning builtin
TypedArrayMergeSort(implicit context: Context)(
source: FixedArray, from: uintptr, to: uintptr, target: FixedArray,
array: JSTypedArray, comparefn: Callable): JSAny {
assert(to - from > 1);
const middle: uintptr = from + ((to - from) >>> 1);
// On the next recursion step source becomes target and vice versa.
// This saves the copy of the relevant range from the original
// array into a work array on each recursion step.
if (middle - from > 1) {
TypedArrayMergeSort(target, from, middle, source, array, comparefn);
}
if (to - middle > 1) {
TypedArrayMergeSort(target, middle, to, source, array, comparefn);
}
TypedArrayMerge(source, from, middle, to, target);
return Undefined;
}
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.sort
transitioning javascript builtin TypedArrayPrototypeSort(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSTypedArray {
// 1. If comparefn is not undefined and IsCallable(comparefn) is false,
// throw a TypeError exception.
const comparefnObj: JSAny = arguments[0];
if (comparefnObj != Undefined && !Is<Callable>(comparefnObj)) {
ThrowTypeError(MessageTemplate::kBadSortComparisonFunction, comparefnObj);
}
// 2. Let obj be the this value.
const obj: JSAny = receiver;
// 3. Let buffer be ? ValidateTypedArray(obj).
// ValidateTypedArray currently returns the array, not the ViewBuffer.
const array: JSTypedArray =
ValidateTypedArray(context, obj, kBuiltinNameSort);
// 4. Let len be obj.[[ArrayLength]].
const len: uintptr = array.length;
// Arrays of length 1 or less are considered sorted.
if (len < 2) return array;
// Default sorting is done in C++ using std::sort
if (comparefnObj == Undefined) {
return TypedArraySortFast(context, obj);
}
const comparefn: Callable =
Cast<Callable>(comparefnObj) otherwise unreachable;
const accessor: TypedArrayAccessor =
GetTypedArrayAccessor(array.elements_kind);
// Prepare the two work arrays. All numbers are converted to tagged
// objects first, and merge sorted between the two FixedArrays.
// The result is then written back into the JSTypedArray.
const work1: FixedArray = AllocateZeroedFixedArray(Convert<intptr>(len));
const work2: FixedArray = AllocateZeroedFixedArray(Convert<intptr>(len));
for (let i: uintptr = 0; i < len; ++i) {
const element: Numeric = accessor.LoadNumeric(array, i);
work1.objects[i] = element;
work2.objects[i] = element;
}
TypedArrayMergeSort(work2, 0, len, work1, array, comparefn);
// work1 contains the sorted numbers. Write them back.
for (let i: uintptr = 0; i < len; ++i) {
accessor.StoreNumeric(
context, array, i, UnsafeCast<Numeric>(work1.objects[i]));
}
return array;
}
}
transitioning builtin
TypedArrayMergeSort(implicit context: Context)(
source: FixedArray, from: uintptr, to: uintptr, target: FixedArray,
array: JSTypedArray, comparefn: Callable): JSAny {
assert(to - from > 1);
const middle: uintptr = from + ((to - from) >>> 1);
// On the next recursion step source becomes target and vice versa.
// This saves the copy of the relevant range from the original
// array into a work array on each recursion step.
if (middle - from > 1) {
TypedArrayMergeSort(target, from, middle, source, array, comparefn);
}
if (to - middle > 1) {
TypedArrayMergeSort(target, middle, to, source, array, comparefn);
}
TypedArrayMerge(source, from, middle, to, target);
return Undefined;
}
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.sort
transitioning javascript builtin TypedArrayPrototypeSort(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSTypedArray {
// 1. If comparefn is not undefined and IsCallable(comparefn) is false,
// throw a TypeError exception.
const comparefnObj: JSAny = arguments[0];
if (comparefnObj != Undefined && !Is<Callable>(comparefnObj)) {
ThrowTypeError(MessageTemplate::kBadSortComparisonFunction, comparefnObj);
}
// 2. Let obj be the this value.
const obj: JSAny = receiver;
// 3. Let buffer be ? ValidateTypedArray(obj).
// ValidateTypedArray currently returns the array, not the ViewBuffer.
const array: JSTypedArray =
ValidateTypedArray(context, obj, kBuiltinNameSort);
// 4. Let len be obj.[[ArrayLength]].
const len: uintptr = array.length;
// Arrays of length 1 or less are considered sorted.
if (len < 2) return array;
// Default sorting is done in C++ using std::sort
if (comparefnObj == Undefined) {
return TypedArraySortFast(context, obj);
}
const comparefn: Callable =
Cast<Callable>(comparefnObj) otherwise unreachable;
const accessor: TypedArrayAccessor =
GetTypedArrayAccessor(array.elements_kind);
// Prepare the two work arrays. All numbers are converted to tagged
// objects first, and merge sorted between the two FixedArrays.
// The result is then written back into the JSTypedArray.
const work1: FixedArray = AllocateZeroedFixedArray(Convert<intptr>(len));
const work2: FixedArray = AllocateZeroedFixedArray(Convert<intptr>(len));
for (let i: uintptr = 0; i < len; ++i) {
const element: Numeric = accessor.LoadNumeric(array, i);
work1.objects[i] = element;
work2.objects[i] = element;
}
TypedArrayMergeSort(work2, 0, len, work1, array, comparefn);
// work1 contains the sorted numbers. Write them back.
for (let i: uintptr = 0; i < len; ++i) {
accessor.StoreNumeric(
context, array, i, UnsafeCast<Numeric>(work1.objects[i]));
}
return array;
}
}

View File

@ -3,60 +3,60 @@
// found in the LICENSE file.
namespace typed_array {
// ES %TypedArray%.prototype.subarray
transitioning javascript builtin TypedArrayPrototypeSubArray(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSTypedArray {
const methodName: constexpr string = '%TypedArray%.prototype.subarray';
// ES %TypedArray%.prototype.subarray
transitioning javascript builtin TypedArrayPrototypeSubArray(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSTypedArray {
const methodName: constexpr string = '%TypedArray%.prototype.subarray';
// 1. Let O be the this value.
// 3. If O does not have a [[TypedArrayName]] internal slot, throw a
// TypeError exception.
const source = Cast<JSTypedArray>(receiver)
otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, methodName);
// 1. Let O be the this value.
// 3. If O does not have a [[TypedArrayName]] internal slot, throw a
// TypeError exception.
const source = Cast<JSTypedArray>(receiver)
otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, methodName);
// 5. Let buffer be O.[[ViewedArrayBuffer]].
const buffer = typed_array::GetBuffer(source);
// 5. Let buffer be O.[[ViewedArrayBuffer]].
const buffer = typed_array::GetBuffer(source);
// 6. Let srcLength be O.[[ArrayLength]].
const srcLength: uintptr = source.length;
// 6. Let srcLength be O.[[ArrayLength]].
const srcLength: uintptr = source.length;
// 7. Let relativeBegin be ? ToInteger(begin).
// 8. If relativeBegin < 0, let beginIndex be max((srcLength +
// relativeBegin), 0); else let beginIndex be min(relativeBegin,
// srcLength).
const arg0 = arguments[0];
const begin: uintptr =
arg0 != Undefined ? ConvertToRelativeIndex(arg0, srcLength) : 0;
// 7. Let relativeBegin be ? ToInteger(begin).
// 8. If relativeBegin < 0, let beginIndex be max((srcLength +
// relativeBegin), 0); else let beginIndex be min(relativeBegin,
// srcLength).
const arg0 = arguments[0];
const begin: uintptr =
arg0 != Undefined ? ConvertToRelativeIndex(arg0, srcLength) : 0;
// 9. If end is undefined, let relativeEnd be srcLength;
// else, let relativeEnd be ? ToInteger(end).
// 10. If relativeEnd < 0, let endIndex be max((srcLength + relativeEnd),
// 0); else let endIndex be min(relativeEnd, srcLength).
const arg1 = arguments[1];
const end: uintptr =
arg1 != Undefined ? ConvertToRelativeIndex(arg1, srcLength) : srcLength;
// 9. If end is undefined, let relativeEnd be srcLength;
// else, let relativeEnd be ? ToInteger(end).
// 10. If relativeEnd < 0, let endIndex be max((srcLength + relativeEnd),
// 0); else let endIndex be min(relativeEnd, srcLength).
const arg1 = arguments[1];
const end: uintptr =
arg1 != Undefined ? ConvertToRelativeIndex(arg1, srcLength) : srcLength;
// 11. Let newLength be max(endIndex - beginIndex, 0).
const newLength: uintptr = Unsigned(IntPtrMax(Signed(end - begin), 0));
// 11. Let newLength be max(endIndex - beginIndex, 0).
const newLength: uintptr = Unsigned(IntPtrMax(Signed(end - begin), 0));
// 12. Let constructorName be the String value of O.[[TypedArrayName]].
// 13. Let elementSize be the Number value of the Element Size value
// specified in Table 52 for constructorName.
const elementsInfo = typed_array::GetTypedArrayElementsInfo(source);
// 12. Let constructorName be the String value of O.[[TypedArrayName]].
// 13. Let elementSize be the Number value of the Element Size value
// specified in Table 52 for constructorName.
const elementsInfo = typed_array::GetTypedArrayElementsInfo(source);
// 14. Let srcByteOffset be O.[[ByteOffset]].
const srcByteOffset: uintptr = source.byte_offset;
// 14. Let srcByteOffset be O.[[ByteOffset]].
const srcByteOffset: uintptr = source.byte_offset;
// 15. Let beginByteOffset be srcByteOffset + beginIndex × elementSize.
const beginByteOffset =
srcByteOffset + elementsInfo.CalculateByteLength(begin)
otherwise ThrowRangeError(MessageTemplate::kInvalidArrayBufferLength);
// 15. Let beginByteOffset be srcByteOffset + beginIndex × elementSize.
const beginByteOffset =
srcByteOffset + elementsInfo.CalculateByteLength(begin)
otherwise ThrowRangeError(MessageTemplate::kInvalidArrayBufferLength);
// 16. Let argumentsList be « buffer, beginByteOffset, newLength ».
// 17. Return ? TypedArraySpeciesCreate(O, argumentsList).
return TypedArraySpeciesCreateByBuffer(
methodName, source, buffer, beginByteOffset, newLength);
}
// 16. Let argumentsList be « buffer, beginByteOffset, newLength ».
// 17. Return ? TypedArraySpeciesCreate(O, argumentsList).
return TypedArraySpeciesCreateByBuffer(
methodName, source, buffer, beginByteOffset, newLength);
}
}

View File

@ -5,269 +5,267 @@
#include 'src/builtins/builtins-typed-array-gen.h'
namespace typed_array {
// Naming convention from elements.cc. We have a similar intent but implement
// fastpaths using generics instead of using a class hierarchy for elements
// kinds specific implementations.
type Uint8Elements extends ElementsKind;
type Int8Elements extends ElementsKind;
type Uint16Elements extends ElementsKind;
type Int16Elements extends ElementsKind;
type Uint32Elements extends ElementsKind;
type Int32Elements extends ElementsKind;
type Float32Elements extends ElementsKind;
type Float64Elements extends ElementsKind;
type Uint8ClampedElements extends ElementsKind;
type BigUint64Elements extends ElementsKind;
type BigInt64Elements extends ElementsKind;
// Naming convention from elements.cc. We have a similar intent but implement
// fastpaths using generics instead of using a class hierarchy for elements
// kinds specific implementations.
type Uint8Elements extends ElementsKind;
type Int8Elements extends ElementsKind;
type Uint16Elements extends ElementsKind;
type Int16Elements extends ElementsKind;
type Uint32Elements extends ElementsKind;
type Int32Elements extends ElementsKind;
type Float32Elements extends ElementsKind;
type Float64Elements extends ElementsKind;
type Uint8ClampedElements extends ElementsKind;
type BigUint64Elements extends ElementsKind;
type BigInt64Elements extends ElementsKind;
@export
struct TypedArrayElementsInfo {
// Calculates the number of bytes required for specified number of elements.
macro CalculateByteLength(length: uintptr): uintptr labels IfInvalid {
if (length > kTypedArrayMaxLength) goto IfInvalid;
const maxArrayLength = kArrayBufferMaxByteLength >>> this.sizeLog2;
if (length > maxArrayLength) goto IfInvalid;
const byteLength = length << this.sizeLog2;
return byteLength;
}
// Calculates the maximum number of elements supported by a specified number
// of bytes.
macro CalculateLength(byteLength: uintptr): uintptr labels IfInvalid {
const length = byteLength >>> this.sizeLog2;
if (length > kTypedArrayMaxLength) goto IfInvalid;
return length;
}
// Determines if `bytes` (byte offset or length) cannot be evenly divided by
// element size.
macro IsUnaligned(bytes: uintptr): bool {
// Exploits the fact the element size is a power of 2. Determining whether
// there is remainder (not aligned) can be achieved efficiently with bit
// masking. Shift is safe as sizeLog2 can be 3 at most (see
// ElementsKindToShiftSize).
return (bytes & ((1 << this.sizeLog2) - 1)) != 0;
}
sizeLog2: uintptr;
kind: ElementsKind;
}
extern runtime TypedArrayCopyElements(Context, JSTypedArray, Object, Number):
void;
extern macro TypedArrayBuiltinsAssembler::ValidateTypedArray(
Context, JSAny, constexpr string): JSTypedArray;
extern macro TypedArrayBuiltinsAssembler::CallCMemcpy(
RawPtr, RawPtr, uintptr): void;
extern macro TypedArrayBuiltinsAssembler::CallCMemmove(
RawPtr, RawPtr, uintptr): void;
extern macro TypedArrayBuiltinsAssembler::CallCMemset(
RawPtr, intptr, uintptr): void;
extern macro TypedArrayBuiltinsAssembler::GetBuffer(
implicit context: Context)(JSTypedArray): JSArrayBuffer;
extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(
JSTypedArray): TypedArrayElementsInfo;
extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(Map):
TypedArrayElementsInfo;
extern macro TypedArrayBuiltinsAssembler::IsUint8ElementsKind(ElementsKind):
bool;
extern macro TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind(
ElementsKind): bool;
extern macro LoadFixedTypedArrayElementAsTagged(
RawPtr, uintptr, constexpr ElementsKind): Numeric;
extern macro TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromNumeric(
Context, JSTypedArray, uintptr, Numeric, constexpr ElementsKind);
extern macro TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromTagged(
Context, JSTypedArray, uintptr, JSAny,
constexpr ElementsKind) labels IfDetached;
type LoadNumericFn = builtin(JSTypedArray, uintptr) => Numeric;
type StoreNumericFn = builtin(Context, JSTypedArray, uintptr, Numeric) => Smi;
type StoreJSAnyFn = builtin(Context, JSTypedArray, uintptr, JSAny) => Smi;
// The result codes returned by StoreNumericFn and StoreJSAnyFn builtins.
const kStoreSucceded: Smi = 0;
const kStoreFailureArrayDetached: Smi = 1;
struct TypedArrayAccessor {
macro LoadNumeric(array: JSTypedArray, index: uintptr): Numeric {
const loadfn: LoadNumericFn = this.loadNumericFn;
return loadfn(array, index);
}
macro StoreNumeric(
context: Context, array: JSTypedArray, index: uintptr, value: Numeric) {
const storefn: StoreNumericFn = this.storeNumericFn;
const result = storefn(context, array, index, value);
assert(result == kStoreSucceded);
}
macro StoreJSAny(
context: Context, array: JSTypedArray, index: uintptr, value: JSAny)
labels IfDetached {
const storefn: StoreJSAnyFn = this.storeJSAnyFn;
const result = storefn(context, array, index, value);
if (result == kStoreFailureArrayDetached) {
goto IfDetached;
}
assert(result == kStoreSucceded);
}
loadNumericFn: LoadNumericFn;
storeNumericFn: StoreNumericFn;
storeJSAnyFn: StoreJSAnyFn;
@export
struct TypedArrayElementsInfo {
// Calculates the number of bytes required for specified number of elements.
macro CalculateByteLength(length: uintptr): uintptr labels IfInvalid {
if (length > kTypedArrayMaxLength) goto IfInvalid;
const maxArrayLength = kArrayBufferMaxByteLength >>> this.sizeLog2;
if (length > maxArrayLength) goto IfInvalid;
const byteLength = length << this.sizeLog2;
return byteLength;
}
macro GetTypedArrayAccessor<T : type extends ElementsKind>():
TypedArrayAccessor {
const loadNumericFn = LoadTypedElement<T>;
const storeNumericFn = StoreTypedElementNumeric<T>;
const storeJSAnyFn = StoreTypedElementJSAny<T>;
return TypedArrayAccessor{loadNumericFn, storeNumericFn, storeJSAnyFn};
// Calculates the maximum number of elements supported by a specified number
// of bytes.
macro CalculateLength(byteLength: uintptr): uintptr labels IfInvalid {
const length = byteLength >>> this.sizeLog2;
if (length > kTypedArrayMaxLength) goto IfInvalid;
return length;
}
macro GetTypedArrayAccessor(elementsKind: ElementsKind): TypedArrayAccessor {
if (IsElementsKindGreaterThan(
elementsKind, ElementsKind::UINT32_ELEMENTS)) {
if (elementsKind == ElementsKind::INT32_ELEMENTS) {
return GetTypedArrayAccessor<Int32Elements>();
} else if (elementsKind == ElementsKind::FLOAT32_ELEMENTS) {
return GetTypedArrayAccessor<Float32Elements>();
} else if (elementsKind == ElementsKind::FLOAT64_ELEMENTS) {
return GetTypedArrayAccessor<Float64Elements>();
} else if (elementsKind == ElementsKind::UINT8_CLAMPED_ELEMENTS) {
return GetTypedArrayAccessor<Uint8ClampedElements>();
} else if (elementsKind == ElementsKind::BIGUINT64_ELEMENTS) {
return GetTypedArrayAccessor<BigUint64Elements>();
} else if (elementsKind == ElementsKind::BIGINT64_ELEMENTS) {
return GetTypedArrayAccessor<BigInt64Elements>();
}
} else {
if (elementsKind == ElementsKind::UINT8_ELEMENTS) {
return GetTypedArrayAccessor<Uint8Elements>();
} else if (elementsKind == ElementsKind::INT8_ELEMENTS) {
return GetTypedArrayAccessor<Int8Elements>();
} else if (elementsKind == ElementsKind::UINT16_ELEMENTS) {
return GetTypedArrayAccessor<Uint16Elements>();
} else if (elementsKind == ElementsKind::INT16_ELEMENTS) {
return GetTypedArrayAccessor<Int16Elements>();
} else if (elementsKind == ElementsKind::UINT32_ELEMENTS) {
return GetTypedArrayAccessor<Uint32Elements>();
}
}
unreachable;
// Determines if `bytes` (byte offset or length) cannot be evenly divided by
// element size.
macro IsUnaligned(bytes: uintptr): bool {
// Exploits the fact the element size is a power of 2. Determining whether
// there is remainder (not aligned) can be achieved efficiently with bit
// masking. Shift is safe as sizeLog2 can be 3 at most (see
// ElementsKindToShiftSize).
return (bytes & ((1 << this.sizeLog2) - 1)) != 0;
}
extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOnHeapDataPtr(
JSTypedArray, ByteArray, uintptr): void;
extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOffHeapDataPtr(
JSTypedArray, RawPtr, uintptr): void;
// AttachedJSTypedArray guards that the array's buffer is not detached.
transient type AttachedJSTypedArray extends JSTypedArray;
macro EnsureAttached(array: JSTypedArray): AttachedJSTypedArray
labels Detached {
if (IsDetachedBuffer(array.buffer)) goto Detached;
return %RawDownCast<AttachedJSTypedArray>(array);
}
struct AttachedJSTypedArrayWitness {
macro Get(): AttachedJSTypedArray {
return this.unstable;
}
macro GetStable(): JSTypedArray {
return this.stable;
}
macro Recheck() labels Detached {
if (IsDetachedBuffer(this.stable.buffer)) goto Detached;
this.unstable = %RawDownCast<AttachedJSTypedArray>(this.stable);
}
macro Load(implicit context: Context)(k: uintptr): JSAny {
const lf: LoadNumericFn = this.loadfn;
return lf(this.unstable, k);
}
stable: JSTypedArray;
unstable: AttachedJSTypedArray;
loadfn: LoadNumericFn;
}
macro NewAttachedJSTypedArrayWitness(array: AttachedJSTypedArray):
AttachedJSTypedArrayWitness {
const kind = array.elements_kind;
const accessor: TypedArrayAccessor = GetTypedArrayAccessor(kind);
return AttachedJSTypedArrayWitness{
stable: array,
unstable: array,
loadfn: accessor.loadNumericFn
};
}
macro KindForArrayType<T : type extends ElementsKind>():
constexpr ElementsKind;
KindForArrayType<Uint8Elements>(): constexpr ElementsKind {
return ElementsKind::UINT8_ELEMENTS;
}
KindForArrayType<Int8Elements>(): constexpr ElementsKind {
return ElementsKind::INT8_ELEMENTS;
}
KindForArrayType<Uint16Elements>(): constexpr ElementsKind {
return ElementsKind::UINT16_ELEMENTS;
}
KindForArrayType<Int16Elements>(): constexpr ElementsKind {
return ElementsKind::INT16_ELEMENTS;
}
KindForArrayType<Uint32Elements>(): constexpr ElementsKind {
return ElementsKind::UINT32_ELEMENTS;
}
KindForArrayType<Int32Elements>(): constexpr ElementsKind {
return ElementsKind::INT32_ELEMENTS;
}
KindForArrayType<Float32Elements>(): constexpr ElementsKind {
return ElementsKind::FLOAT32_ELEMENTS;
}
KindForArrayType<Float64Elements>(): constexpr ElementsKind {
return ElementsKind::FLOAT64_ELEMENTS;
}
KindForArrayType<Uint8ClampedElements>(): constexpr ElementsKind {
return ElementsKind::UINT8_CLAMPED_ELEMENTS;
}
KindForArrayType<BigUint64Elements>(): constexpr ElementsKind {
return ElementsKind::BIGUINT64_ELEMENTS;
}
KindForArrayType<BigInt64Elements>(): constexpr ElementsKind {
return ElementsKind::BIGINT64_ELEMENTS;
}
builtin LoadTypedElement<T : type extends ElementsKind>(
array: JSTypedArray, index: uintptr): Numeric {
return LoadFixedTypedArrayElementAsTagged(
array.data_ptr, index, KindForArrayType<T>());
}
builtin StoreTypedElementNumeric<T : type extends ElementsKind>(
context: Context, typedArray: JSTypedArray, index: uintptr,
value: Numeric): Smi {
StoreJSTypedArrayElementFromNumeric(
context, typedArray, index, value, KindForArrayType<T>());
return kStoreSucceded;
}
// Returns True on sucess or False if the typedArrays was detached.
builtin StoreTypedElementJSAny<T : type extends ElementsKind>(
context: Context, typedArray: JSTypedArray, index: uintptr,
value: JSAny): Smi {
try {
StoreJSTypedArrayElementFromTagged(
context, typedArray, index, value, KindForArrayType<T>())
otherwise IfDetached;
} label IfDetached {
return kStoreFailureArrayDetached;
}
return kStoreSucceded;
}
sizeLog2: uintptr;
kind: ElementsKind;
}
extern runtime TypedArrayCopyElements(
Context, JSTypedArray, Object, Number): void;
extern macro TypedArrayBuiltinsAssembler::ValidateTypedArray(
Context, JSAny, constexpr string): JSTypedArray;
extern macro TypedArrayBuiltinsAssembler::CallCMemcpy(
RawPtr, RawPtr, uintptr): void;
extern macro TypedArrayBuiltinsAssembler::CallCMemmove(
RawPtr, RawPtr, uintptr): void;
extern macro TypedArrayBuiltinsAssembler::CallCMemset(
RawPtr, intptr, uintptr): void;
extern macro TypedArrayBuiltinsAssembler::GetBuffer(implicit context: Context)(
JSTypedArray): JSArrayBuffer;
extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(
JSTypedArray): TypedArrayElementsInfo;
extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(Map):
TypedArrayElementsInfo;
extern macro TypedArrayBuiltinsAssembler::IsUint8ElementsKind(ElementsKind):
bool;
extern macro TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind(ElementsKind):
bool;
extern macro LoadFixedTypedArrayElementAsTagged(
RawPtr, uintptr, constexpr ElementsKind): Numeric;
extern macro TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromNumeric(
Context, JSTypedArray, uintptr, Numeric, constexpr ElementsKind);
extern macro TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromTagged(
Context, JSTypedArray, uintptr, JSAny, constexpr ElementsKind)
labels IfDetached;
type LoadNumericFn = builtin(JSTypedArray, uintptr) => Numeric;
type StoreNumericFn = builtin(Context, JSTypedArray, uintptr, Numeric) => Smi;
type StoreJSAnyFn = builtin(Context, JSTypedArray, uintptr, JSAny) => Smi;
// The result codes returned by StoreNumericFn and StoreJSAnyFn builtins.
const kStoreSucceded: Smi = 0;
const kStoreFailureArrayDetached: Smi = 1;
struct TypedArrayAccessor {
macro LoadNumeric(array: JSTypedArray, index: uintptr): Numeric {
const loadfn: LoadNumericFn = this.loadNumericFn;
return loadfn(array, index);
}
macro StoreNumeric(
context: Context, array: JSTypedArray, index: uintptr, value: Numeric) {
const storefn: StoreNumericFn = this.storeNumericFn;
const result = storefn(context, array, index, value);
assert(result == kStoreSucceded);
}
macro StoreJSAny(
context: Context, array: JSTypedArray, index: uintptr, value: JSAny)
labels IfDetached {
const storefn: StoreJSAnyFn = this.storeJSAnyFn;
const result = storefn(context, array, index, value);
if (result == kStoreFailureArrayDetached) {
goto IfDetached;
}
assert(result == kStoreSucceded);
}
loadNumericFn: LoadNumericFn;
storeNumericFn: StoreNumericFn;
storeJSAnyFn: StoreJSAnyFn;
}
macro GetTypedArrayAccessor<T : type extends ElementsKind>():
TypedArrayAccessor {
const loadNumericFn = LoadTypedElement<T>;
const storeNumericFn = StoreTypedElementNumeric<T>;
const storeJSAnyFn = StoreTypedElementJSAny<T>;
return TypedArrayAccessor{loadNumericFn, storeNumericFn, storeJSAnyFn};
}
macro GetTypedArrayAccessor(elementsKind: ElementsKind): TypedArrayAccessor {
if (IsElementsKindGreaterThan(elementsKind, ElementsKind::UINT32_ELEMENTS)) {
if (elementsKind == ElementsKind::INT32_ELEMENTS) {
return GetTypedArrayAccessor<Int32Elements>();
} else if (elementsKind == ElementsKind::FLOAT32_ELEMENTS) {
return GetTypedArrayAccessor<Float32Elements>();
} else if (elementsKind == ElementsKind::FLOAT64_ELEMENTS) {
return GetTypedArrayAccessor<Float64Elements>();
} else if (elementsKind == ElementsKind::UINT8_CLAMPED_ELEMENTS) {
return GetTypedArrayAccessor<Uint8ClampedElements>();
} else if (elementsKind == ElementsKind::BIGUINT64_ELEMENTS) {
return GetTypedArrayAccessor<BigUint64Elements>();
} else if (elementsKind == ElementsKind::BIGINT64_ELEMENTS) {
return GetTypedArrayAccessor<BigInt64Elements>();
}
} else {
if (elementsKind == ElementsKind::UINT8_ELEMENTS) {
return GetTypedArrayAccessor<Uint8Elements>();
} else if (elementsKind == ElementsKind::INT8_ELEMENTS) {
return GetTypedArrayAccessor<Int8Elements>();
} else if (elementsKind == ElementsKind::UINT16_ELEMENTS) {
return GetTypedArrayAccessor<Uint16Elements>();
} else if (elementsKind == ElementsKind::INT16_ELEMENTS) {
return GetTypedArrayAccessor<Int16Elements>();
} else if (elementsKind == ElementsKind::UINT32_ELEMENTS) {
return GetTypedArrayAccessor<Uint32Elements>();
}
}
unreachable;
}
extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOnHeapDataPtr(
JSTypedArray, ByteArray, uintptr): void;
extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOffHeapDataPtr(
JSTypedArray, RawPtr, uintptr): void;
// AttachedJSTypedArray guards that the array's buffer is not detached.
transient type AttachedJSTypedArray extends JSTypedArray;
macro EnsureAttached(array: JSTypedArray): AttachedJSTypedArray
labels Detached {
if (IsDetachedBuffer(array.buffer)) goto Detached;
return %RawDownCast<AttachedJSTypedArray>(array);
}
struct AttachedJSTypedArrayWitness {
macro Get(): AttachedJSTypedArray {
return this.unstable;
}
macro GetStable(): JSTypedArray {
return this.stable;
}
macro Recheck() labels Detached {
if (IsDetachedBuffer(this.stable.buffer)) goto Detached;
this.unstable = %RawDownCast<AttachedJSTypedArray>(this.stable);
}
macro Load(implicit context: Context)(k: uintptr): JSAny {
const lf: LoadNumericFn = this.loadfn;
return lf(this.unstable, k);
}
stable: JSTypedArray;
unstable: AttachedJSTypedArray;
loadfn: LoadNumericFn;
}
macro NewAttachedJSTypedArrayWitness(array: AttachedJSTypedArray):
AttachedJSTypedArrayWitness {
const kind = array.elements_kind;
const accessor: TypedArrayAccessor = GetTypedArrayAccessor(kind);
return AttachedJSTypedArrayWitness{
stable: array,
unstable: array,
loadfn: accessor.loadNumericFn
};
}
macro KindForArrayType<T : type extends ElementsKind>(): constexpr ElementsKind;
KindForArrayType<Uint8Elements>(): constexpr ElementsKind {
return ElementsKind::UINT8_ELEMENTS;
}
KindForArrayType<Int8Elements>(): constexpr ElementsKind {
return ElementsKind::INT8_ELEMENTS;
}
KindForArrayType<Uint16Elements>(): constexpr ElementsKind {
return ElementsKind::UINT16_ELEMENTS;
}
KindForArrayType<Int16Elements>(): constexpr ElementsKind {
return ElementsKind::INT16_ELEMENTS;
}
KindForArrayType<Uint32Elements>(): constexpr ElementsKind {
return ElementsKind::UINT32_ELEMENTS;
}
KindForArrayType<Int32Elements>(): constexpr ElementsKind {
return ElementsKind::INT32_ELEMENTS;
}
KindForArrayType<Float32Elements>(): constexpr ElementsKind {
return ElementsKind::FLOAT32_ELEMENTS;
}
KindForArrayType<Float64Elements>(): constexpr ElementsKind {
return ElementsKind::FLOAT64_ELEMENTS;
}
KindForArrayType<Uint8ClampedElements>(): constexpr ElementsKind {
return ElementsKind::UINT8_CLAMPED_ELEMENTS;
}
KindForArrayType<BigUint64Elements>(): constexpr ElementsKind {
return ElementsKind::BIGUINT64_ELEMENTS;
}
KindForArrayType<BigInt64Elements>(): constexpr ElementsKind {
return ElementsKind::BIGINT64_ELEMENTS;
}
builtin LoadTypedElement<T : type extends ElementsKind>(
array: JSTypedArray, index: uintptr): Numeric {
return LoadFixedTypedArrayElementAsTagged(
array.data_ptr, index, KindForArrayType<T>());
}
builtin StoreTypedElementNumeric<T : type extends ElementsKind>(
context: Context, typedArray: JSTypedArray, index: uintptr,
value: Numeric): Smi {
StoreJSTypedArrayElementFromNumeric(
context, typedArray, index, value, KindForArrayType<T>());
return kStoreSucceded;
}
// Returns True on sucess or False if the typedArrays was detached.
builtin StoreTypedElementJSAny<T : type extends ElementsKind>(
context: Context, typedArray: JSTypedArray, index: uintptr,
value: JSAny): Smi {
try {
StoreJSTypedArrayElementFromTagged(
context, typedArray, index, value, KindForArrayType<T>())
otherwise IfDetached;
} label IfDetached {
return kStoreFailureArrayDetached;
}
return kStoreSucceded;
}
}

Some files were not shown because too many files have changed in this diff Show More