[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:
parent
8b2a322110
commit
45557b1f89
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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)(
|
||||
|
@ -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
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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).
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
@ -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{});
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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';
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 '(?:)';
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user