blob: 2fc9dfc53a9448852359d873aaee7e135ff3440b [file] [log] [blame]
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@generateCppClass
extern class JSArgumentsObject extends JSObject {
}
type JSArgumentsObjectWithLength =
JSSloppyArgumentsObject|JSStrictArgumentsObject;
@export
macro IsJSArgumentsObjectWithLength(implicit context: Context)(o: Object):
bool {
return Is<JSArgumentsObjectWithLength>(o);
}
// Just a starting shape for JSObject; properties can move after initialization.
extern shape JSSloppyArgumentsObject extends JSArgumentsObject {
length: JSAny;
callee: JSAny;
}
// Just a starting shape for JSObject; properties can move after initialization.
extern shape JSStrictArgumentsObject extends JSArgumentsObject {
length: JSAny;
}
// Helper class to access FAST_ and SLOW_SLOPPY_ARGUMENTS_ELEMENTS, dividing
// arguments into two types for a given SloppyArgumentsElements object:
// mapped and unmapped.
//
// For clarity SloppyArgumentsElements fields are qualified with "elements."
// below.
//
// Mapped arguments are actual arguments. Unmapped arguments are values added
// to the arguments object after it was created for the call. Mapped arguments
// are stored in the context at indexes given by elements.mapped_entries[key].
// Unmapped arguments are stored as regular indexed properties in the arguments
// array which can be accessed from elements.arguments.
//
// elements.length is min(number_of_actual_arguments,
// number_of_formal_arguments) for a concrete call to a function.
//
// Once a SloppyArgumentsElements is generated, lookup of an argument with index
// |key| in |elements| works as follows:
//
// If key >= elements.length then attempt to look in the unmapped arguments
// array and return the value at key, missing to the runtime if the unmapped
// arguments array is not a fixed array or if key >= elements.arguments.length.
//
// Otherwise, t = elements.mapped_entries[key]. If t is the hole, then the
// entry has been deleted fron the arguments object, and value is looked up in
// the unmapped arguments array, as described above. Otherwise, t is a Smi
// index into the context array specified at elements.context, and the return
// value is elements.context[t].
//
// A graphic representation of a SloppyArgumentsElements object and a
// corresponding unmapped arguments FixedArray:
//
// SloppyArgumentsElements
// +---+-----------------------+
// | Context context |
// +---------------------------+
// | FixedArray arguments +----+ HOLEY_ELEMENTS
// +---------------------------+ v-----+-----------+
// | 0 | Object mapped_entries | | 0 | the_hole |
// |...| ... | | ... | ... |
// |n-1| Object mapped_entries | | n-1 | the_hole |
// +---------------------------+ | n | element_1 |
// | ... | ... |
// |n+m-1| element_m |
// +-----------------+
//
// The elements.arguments backing store kind depends on the ElementsKind of
// the outer JSArgumentsObject:
// - FAST_SLOPPY_ARGUMENTS_ELEMENTS: HOLEY_ELEMENTS
// - SLOW_SLOPPY_ARGUMENTS_ELEMENTS: DICTIONARY_ELEMENTS
@export
class SloppyArgumentsElements extends FixedArrayBase {
context: Context;
arguments: FixedArray|NumberDictionary;
mapped_entries[length]: Smi|TheHole;
}
macro NewSloppyArgumentsElements<Iterator: type>(
length: Smi, context: Context, arguments: FixedArray,
it: Iterator): SloppyArgumentsElements {
return new
SloppyArgumentsElements{length, context, arguments, mapped_entries: ...it};
}
@generateCppClass
@generatePrint
extern class AliasedArgumentsEntry extends Struct {
aliased_context_slot: Smi;
}
// TODO(danno): This should be a namespace {} once supported
namespace arguments {
macro NewJSStrictArgumentsObject(implicit context: Context)(
elements: FixedArray): JSStrictArgumentsObject {
const map = GetStrictArgumentsMap();
return new JSStrictArgumentsObject{
map,
properties_or_hash: kEmptyFixedArray,
elements,
length: elements.length
};
}
macro NewJSSloppyArgumentsObject(implicit context: Context)(
elements: FixedArrayBase, callee: JSFunction): JSSloppyArgumentsObject {
const map = GetSloppyArgumentsMap();
return new JSSloppyArgumentsObject{
map,
properties_or_hash: kEmptyFixedArray,
elements,
length: elements.length,
callee
};
}
macro NewJSFastAliasedArgumentsObject(implicit context: Context)(
elements: FixedArrayBase, length: Smi,
callee: JSFunction): JSSloppyArgumentsObject {
// TODO(danno): FastAliasedArguments should really be a type for itself
const map = GetFastAliasedArgumentsMap();
return new JSSloppyArgumentsObject{
map,
properties_or_hash: kEmptyFixedArray,
elements,
length,
callee
};
}
struct ParameterMapIterator {
macro Next(): Smi labels NoMore {
if (this.currentIndex == this.endInterationIndex) goto NoMore;
this.currentIndex--;
return Convert<Smi>(this.currentIndex);
}
currentIndex: intptr;
const endInterationIndex: intptr;
}
macro NewParameterMapIterator(
context: Context, formalParameterCount: intptr,
mappedCount: intptr): ParameterMapIterator {
const flags = context.GetScopeInfo().flags;
let contextHeaderSize: intptr = ContextSlot::MIN_CONTEXT_SLOTS;
if (flags.has_context_extension_slot) ++contextHeaderSize;
// Copy the parameter slots and the holes in the arguments.
// We need to fill in mapped_count slots. They index the context,
// where parameters are stored in reverse order, at
// context_header_size .. context_header_size+argument_count-1
// The mapped parameter thus need to get indices
// context_header_size+parameter_count-1 ..
// context_header_size+argument_count-mapped_count
// We loop from right to left.
const afterLastContextIndex = contextHeaderSize + formalParameterCount;
const firstContextIndex = afterLastContextIndex - mappedCount;
return ParameterMapIterator{
currentIndex: afterLastContextIndex,
endInterationIndex: firstContextIndex
};
}
struct ParameterValueIterator {
macro Next(): Object labels NoMore() {
if (this.mapped_count != 0) {
this.mapped_count--;
return TheHole;
}
if (this.current == this.arguments.length) goto NoMore;
return this.arguments[this.current++];
}
mapped_count: intptr;
const arguments: Arguments;
current: intptr;
}
macro NewParameterValueIterator(
mappedCount: intptr, arguments: Arguments): ParameterValueIterator {
return ParameterValueIterator{
mapped_count: mappedCount,
arguments,
current: mappedCount
};
}
macro NewAllArguments(implicit context: Context)(
frame: FrameWithArguments, argumentCount: intptr): JSArray {
const map = GetFastPackedElementsJSArrayMap();
const arguments = GetFrameArguments(frame, argumentCount);
const it = ArgumentsIterator{arguments, current: 0};
const elements = NewFixedArray(argumentCount, it);
return NewJSArray(map, elements);
}
macro NewRestArgumentsElements(
frame: FrameWithArguments, formalParameterCount: intptr,
argumentCount: intptr): FixedArray {
const length = (formalParameterCount >= argumentCount) ?
0 :
argumentCount - formalParameterCount;
const arguments = GetFrameArguments(frame, argumentCount);
const it = ArgumentsIterator{arguments, current: formalParameterCount};
return NewFixedArray(length, it);
}
macro NewRestArguments(implicit context: Context)(info: FrameWithArgumentsInfo):
JSArray {
const argumentCount = Convert<intptr>(info.argument_count);
const formalParameterCount = Convert<intptr>(info.formal_parameter_count);
const map = GetFastPackedElementsJSArrayMap();
const elements =
NewRestArgumentsElements(info.frame, formalParameterCount, argumentCount);
return NewJSArray(map, elements);
}
macro NewStrictArgumentsElements(
frame: FrameWithArguments, argumentCount: intptr): FixedArray {
const arguments = GetFrameArguments(frame, argumentCount);
const it = ArgumentsIterator{arguments, current: 0};
return NewFixedArray(argumentCount, it);
}
macro NewStrictArguments(implicit context: Context)(
info: FrameWithArgumentsInfo): JSStrictArgumentsObject {
const argumentCount = Convert<intptr>(info.argument_count);
const elements = NewStrictArgumentsElements(info.frame, argumentCount);
return NewJSStrictArgumentsObject(elements);
}
macro NewSloppyArgumentsElements(
frame: FrameWithArguments, formalParameterCount: intptr,
argumentCount: intptr): FixedArray {
const arguments = GetFrameArguments(frame, argumentCount);
if (formalParameterCount == 0) {
const it = ArgumentsIterator{arguments, current: 0};
return NewFixedArray(argumentCount, it);
}
const mappedCount = IntPtrMin(formalParameterCount, argumentCount);
const it = NewParameterValueIterator(mappedCount, arguments);
return NewFixedArray(argumentCount, it);
}
macro NewSloppyArguments(implicit context: Context)(
info: FrameWithArgumentsInfo, callee: JSFunction): JSSloppyArgumentsObject {
const argumentCount = Convert<intptr>(info.argument_count);
const formalParameterCount = Convert<intptr>(info.formal_parameter_count);
const parameterValues = arguments::NewSloppyArgumentsElements(
info.frame, formalParameterCount, argumentCount);
if (formalParameterCount == 0) {
return NewJSSloppyArgumentsObject(parameterValues, callee);
}
const mappedCount = IntPtrMin(formalParameterCount, argumentCount);
let paramIter =
NewParameterMapIterator(context, formalParameterCount, mappedCount);
const elementsLength = Convert<Smi>(mappedCount);
const elements = NewSloppyArgumentsElements(
elementsLength, context, parameterValues, paramIter);
const length = Convert<Smi>(argumentCount);
return NewJSFastAliasedArgumentsObject(elements, length, callee);
}
} // namespace arguments
@export
macro EmitFastNewAllArguments(implicit context: Context)(
frame: FrameWithArguments, argc: intptr): JSArray {
return arguments::NewAllArguments(frame, argc);
}
@export
macro EmitFastNewRestArguments(implicit context: Context)(_f: JSFunction):
JSArray {
const info = GetFrameWithArgumentsInfo();
return arguments::NewRestArguments(info);
}
@export
macro EmitFastNewStrictArguments(implicit context: Context)(_f: JSFunction):
JSStrictArgumentsObject {
const info = GetFrameWithArgumentsInfo();
return arguments::NewStrictArguments(info);
}
@export
macro EmitFastNewSloppyArguments(implicit context: Context)(f: JSFunction):
JSSloppyArgumentsObject {
const info = GetFrameWithArgumentsInfo();
return arguments::NewSloppyArguments(info, f);
}
builtin NewSloppyArgumentsElements(
frame: FrameWithArguments, formalParameterCount: intptr,
argumentCount: Smi): FixedArray {
return arguments::NewSloppyArgumentsElements(
frame, formalParameterCount, Convert<intptr>(argumentCount));
}
builtin NewStrictArgumentsElements(
frame: FrameWithArguments, _formalParameterCount: intptr,
argumentCount: Smi): FixedArray {
return arguments::NewStrictArgumentsElements(
frame, Convert<intptr>(argumentCount));
}
builtin NewRestArgumentsElements(
frame: FrameWithArguments, formalParameterCount: intptr,
argumentCount: Smi): FixedArray {
return arguments::NewRestArgumentsElements(
frame, formalParameterCount, Convert<intptr>(argumentCount));
}
macro
AccessSloppyArgumentsCommon(
receiver: JSObject, keyObject: Object): &Object labels Bailout {
const key = Cast<Smi>(keyObject) otherwise Bailout;
const elements =
Cast<SloppyArgumentsElements>(receiver.elements) otherwise Bailout;
try {
if (OutOfBounds(key, elements.length)) goto Unmapped;
const mappedIndex = elements.mapped_entries[key];
typeswitch (mappedIndex) {
case (contextIndex: Smi): {
return &(elements.context.elements[contextIndex]);
}
case (TheHole): {
goto Unmapped;
}
}
} label Unmapped {
typeswitch (elements.arguments) {
case (NumberDictionary): {
goto Bailout;
}
case (arguments: FixedArray): {
if (OutOfBounds(key, arguments.length)) goto Bailout;
if (arguments.objects[key] == TheHole) goto Bailout;
return &(arguments.objects[key]);
}
}
}
}
@export
macro SloppyArgumentsLoad(receiver: JSObject, keyObject: Object):
JSAny labels Bailout {
return UnsafeCast<JSAny>(
*AccessSloppyArgumentsCommon(receiver, keyObject) otherwise Bailout);
}
@export
macro SloppyArgumentsHas(receiver: JSObject, keyObject: Object):
JSAny labels Bailout {
AccessSloppyArgumentsCommon(receiver, keyObject) otherwise Bailout;
return True;
}
@export
macro SloppyArgumentsStore(receiver: JSObject, keyObject: Object, value: JSAny):
JSAny labels Bailout {
let destination =
AccessSloppyArgumentsCommon(receiver, keyObject) otherwise Bailout;
*destination = value;
return value;
}