• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2019 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5extern class JSArgumentsObject extends JSObject {}
6
7type JSArgumentsObjectWithLength =
8    JSSloppyArgumentsObject|JSStrictArgumentsObject;
9
10@export
11macro IsJSArgumentsObjectWithLength(implicit context: Context)(o: Object):
12    bool {
13  return Is<JSArgumentsObjectWithLength>(o);
14}
15
16// Just a starting shape for JSObject; properties can move after initialization.
17extern shape JSSloppyArgumentsObject extends JSArgumentsObject {
18  length: JSAny;
19  callee: JSAny;
20}
21
22// Just a starting shape for JSObject; properties can move after initialization.
23extern shape JSStrictArgumentsObject extends JSArgumentsObject {
24  length: JSAny;
25}
26
27// Helper class to access FAST_ and SLOW_SLOPPY_ARGUMENTS_ELEMENTS, dividing
28// arguments into two types for a given SloppyArgumentsElements object:
29// mapped and unmapped.
30//
31// For clarity SloppyArgumentsElements fields are qualified with "elements."
32// below.
33//
34// Mapped arguments are actual arguments. Unmapped arguments are values added
35// to the arguments object after it was created for the call. Mapped arguments
36// are stored in the context at indexes given by elements.mapped_entries[key].
37// Unmapped arguments are stored as regular indexed properties in the arguments
38// array which can be accessed from elements.arguments.
39//
40// elements.length is min(number_of_actual_arguments,
41// number_of_formal_arguments) for a concrete call to a function.
42//
43// Once a SloppyArgumentsElements is generated, lookup of an argument with index
44// |key| in |elements| works as follows:
45//
46// If key >= elements.length then attempt to look in the unmapped arguments
47// array and return the value at key, missing to the runtime if the unmapped
48// arguments array is not a fixed array or if key >= elements.arguments.length.
49//
50// Otherwise, t = elements.mapped_entries[key]. If t is the hole, then the
51// entry has been deleted from the arguments object, and value is looked up in
52// the unmapped arguments array, as described above. Otherwise, t is a Smi
53// index into the context array specified at elements.context, and the return
54// value is elements.context[t].
55//
56// A graphic representation of a SloppyArgumentsElements object and a
57// corresponding unmapped arguments FixedArray:
58//
59// SloppyArgumentsElements
60// +---+-----------------------+
61// | Context context           |
62// +---------------------------+
63// | FixedArray arguments      +----+ HOLEY_ELEMENTS
64// +---------------------------+    v-----+-----------+
65// | 0 | Object mapped_entries |    |  0  | the_hole  |
66// |...| ...                   |    | ... | ...       |
67// |n-1| Object mapped_entries |    | n-1 | the_hole  |
68// +---------------------------+    |  n  | element_1 |
69//                                  | ... | ...       |
70//                                  |n+m-1| element_m |
71//                                  +-----------------+
72//
73// The elements.arguments backing store kind depends on the ElementsKind of
74// the outer JSArgumentsObject:
75// - FAST_SLOPPY_ARGUMENTS_ELEMENTS: HOLEY_ELEMENTS
76// - SLOW_SLOPPY_ARGUMENTS_ELEMENTS: DICTIONARY_ELEMENTS
77@export
78class SloppyArgumentsElements extends FixedArrayBase {
79  context: Context;
80  arguments: FixedArray|NumberDictionary;
81  @cppRelaxedLoad mapped_entries[length]: Smi|TheHole;
82}
83
84macro NewSloppyArgumentsElements<Iterator: type>(
85    length: Smi, context: Context, arguments: FixedArray,
86    it: Iterator): SloppyArgumentsElements {
87  return new
88  SloppyArgumentsElements{length, context, arguments, mapped_entries: ...it};
89}
90
91extern class AliasedArgumentsEntry extends Struct { aliased_context_slot: Smi; }
92
93// TODO(danno): This should be a namespace {} once supported
94namespace arguments {
95
96macro NewJSStrictArgumentsObject(implicit context: Context)(
97    elements: FixedArray): JSStrictArgumentsObject {
98  const map = GetStrictArgumentsMap();
99  return new JSStrictArgumentsObject{
100    map,
101    properties_or_hash: kEmptyFixedArray,
102    elements,
103    length: elements.length
104  };
105}
106
107macro NewJSSloppyArgumentsObject(implicit context: Context)(
108    elements: FixedArrayBase, callee: JSFunction): JSSloppyArgumentsObject {
109  const map = GetSloppyArgumentsMap();
110  return new JSSloppyArgumentsObject{
111    map,
112    properties_or_hash: kEmptyFixedArray,
113    elements,
114    length: elements.length,
115    callee
116  };
117}
118
119macro NewJSFastAliasedArgumentsObject(implicit context: Context)(
120    elements: FixedArrayBase, length: Smi,
121    callee: JSFunction): JSSloppyArgumentsObject {
122  // TODO(danno): FastAliasedArguments should really be a type for itself
123  const map = GetFastAliasedArgumentsMap();
124  return new JSSloppyArgumentsObject{
125    map,
126    properties_or_hash: kEmptyFixedArray,
127    elements,
128    length,
129    callee
130  };
131}
132
133struct ParameterMapIterator {
134  macro Next(): Smi labels NoMore {
135    if (this.currentIndex == this.endInterationIndex) goto NoMore;
136    this.currentIndex--;
137    return Convert<Smi>(this.currentIndex);
138  }
139  currentIndex: intptr;
140  const endInterationIndex: intptr;
141}
142
143macro NewParameterMapIterator(
144    context: Context, formalParameterCount: intptr,
145    mappedCount: intptr): ParameterMapIterator {
146  const flags = context.GetScopeInfo().flags;
147  let contextHeaderSize: intptr = ContextSlot::MIN_CONTEXT_SLOTS;
148  if (flags.has_context_extension_slot) ++contextHeaderSize;
149  if (flags.receiver_variable ==
150      FromConstexpr<VariableAllocationInfo>(VariableAllocationInfo::CONTEXT)) {
151    ++contextHeaderSize;
152  }
153  // Copy the parameter slots and the holes in the arguments.
154  // We need to fill in mapped_count slots. They index the context,
155  // where parameters are stored in reverse order, at
156  //   context_header_size .. context_header_size+argument_count-1
157  // The mapped parameter thus need to get indices
158  //   context_header_size+parameter_count-1 ..
159  //       context_header_size+argument_count-mapped_count
160  // We loop from right to left.
161  const afterLastContextIndex = contextHeaderSize + formalParameterCount;
162  const firstContextIndex = afterLastContextIndex - mappedCount;
163  return ParameterMapIterator{
164    currentIndex: afterLastContextIndex,
165    endInterationIndex: firstContextIndex
166  };
167}
168
169struct ParameterValueIterator {
170  macro Next(): Object labels NoMore() {
171    if (this.mapped_count != 0) {
172      this.mapped_count--;
173      return TheHole;
174    }
175    if (this.current == this.arguments.length) goto NoMore;
176    return this.arguments[this.current++];
177  }
178  mapped_count: intptr;
179  const arguments: Arguments;
180  current: intptr;
181}
182
183macro NewParameterValueIterator(
184    mappedCount: intptr, arguments: Arguments): ParameterValueIterator {
185  return ParameterValueIterator{
186    mapped_count: mappedCount,
187    arguments,
188    current: mappedCount
189  };
190}
191
192macro NewAllArguments(implicit context: Context)(
193    frame: FrameWithArguments, argumentCount: intptr): JSArray {
194  const map = GetFastPackedElementsJSArrayMap();
195  const arguments = GetFrameArguments(frame, argumentCount);
196  const it = ArgumentsIterator{arguments, current: 0};
197  const elements = NewFixedArray(argumentCount, it);
198  return NewJSArray(map, elements);
199}
200
201macro NewRestArgumentsElements(
202    frame: FrameWithArguments, formalParameterCount: intptr,
203    argumentCount: intptr): FixedArray {
204  const length = (formalParameterCount >= argumentCount) ?
205      0 :
206      argumentCount - formalParameterCount;
207  const arguments = GetFrameArguments(frame, argumentCount);
208  const it = ArgumentsIterator{arguments, current: formalParameterCount};
209  return NewFixedArray(length, it);
210}
211
212macro NewRestArguments(implicit context: Context)(info: FrameWithArgumentsInfo):
213    JSArray {
214  const argumentCount = Convert<intptr>(info.argument_count);
215  const formalParameterCount = Convert<intptr>(info.formal_parameter_count);
216  const map = GetFastPackedElementsJSArrayMap();
217  const elements =
218      NewRestArgumentsElements(info.frame, formalParameterCount, argumentCount);
219  return NewJSArray(map, elements);
220}
221
222macro NewStrictArgumentsElements(
223    frame: FrameWithArguments, argumentCount: intptr): FixedArray {
224  const arguments = GetFrameArguments(frame, argumentCount);
225  const it = ArgumentsIterator{arguments, current: 0};
226  return NewFixedArray(argumentCount, it);
227}
228
229macro NewStrictArguments(implicit context: Context)(
230    info: FrameWithArgumentsInfo): JSStrictArgumentsObject {
231  const argumentCount = Convert<intptr>(info.argument_count);
232  const elements = NewStrictArgumentsElements(info.frame, argumentCount);
233  return NewJSStrictArgumentsObject(elements);
234}
235
236macro NewSloppyArgumentsElements(
237    frame: FrameWithArguments, formalParameterCount: intptr,
238    argumentCount: intptr): FixedArray {
239  const arguments = GetFrameArguments(frame, argumentCount);
240  if (formalParameterCount == 0) {
241    const it = ArgumentsIterator{arguments, current: 0};
242    return NewFixedArray(argumentCount, it);
243  }
244  const mappedCount = IntPtrMin(formalParameterCount, argumentCount);
245  const it = NewParameterValueIterator(mappedCount, arguments);
246  return NewFixedArray(argumentCount, it);
247}
248
249macro NewSloppyArguments(implicit context: Context)(
250    info: FrameWithArgumentsInfo, callee: JSFunction): JSSloppyArgumentsObject {
251  const argumentCount = Convert<intptr>(info.argument_count);
252  const formalParameterCount = Convert<intptr>(info.formal_parameter_count);
253  const parameterValues = arguments::NewSloppyArgumentsElements(
254      info.frame, formalParameterCount, argumentCount);
255  if (formalParameterCount == 0) {
256    return NewJSSloppyArgumentsObject(parameterValues, callee);
257  }
258  const mappedCount = IntPtrMin(formalParameterCount, argumentCount);
259  let paramIter =
260      NewParameterMapIterator(context, formalParameterCount, mappedCount);
261  const elementsLength = Convert<Smi>(mappedCount);
262  const elements = NewSloppyArgumentsElements(
263      elementsLength, context, parameterValues, paramIter);
264  const length = Convert<Smi>(argumentCount);
265  return NewJSFastAliasedArgumentsObject(elements, length, callee);
266}
267
268}  // namespace arguments
269
270@export
271macro EmitFastNewAllArguments(implicit context: Context)(
272    frame: FrameWithArguments, argc: intptr): JSArray {
273  return arguments::NewAllArguments(frame, argc);
274}
275
276@export
277macro EmitFastNewRestArguments(implicit context: Context)(_f: JSFunction):
278    JSArray {
279  const info = GetFrameWithArgumentsInfo();
280  return arguments::NewRestArguments(info);
281}
282
283@export
284macro EmitFastNewStrictArguments(implicit context: Context)(_f: JSFunction):
285    JSStrictArgumentsObject {
286  const info = GetFrameWithArgumentsInfo();
287  return arguments::NewStrictArguments(info);
288}
289
290@export
291macro EmitFastNewSloppyArguments(implicit context: Context)(f: JSFunction):
292    JSSloppyArgumentsObject {
293  const info = GetFrameWithArgumentsInfo();
294  return arguments::NewSloppyArguments(info, f);
295}
296
297builtin NewSloppyArgumentsElements(
298    frame: FrameWithArguments, formalParameterCount: intptr,
299    argumentCount: Smi): FixedArray {
300  return arguments::NewSloppyArgumentsElements(
301      frame, formalParameterCount, Convert<intptr>(argumentCount));
302}
303
304builtin NewStrictArgumentsElements(
305    frame: FrameWithArguments, _formalParameterCount: intptr,
306    argumentCount: Smi): FixedArray {
307  return arguments::NewStrictArgumentsElements(
308      frame, Convert<intptr>(argumentCount));
309}
310
311builtin NewRestArgumentsElements(
312    frame: FrameWithArguments, formalParameterCount: intptr,
313    argumentCount: Smi): FixedArray {
314  return arguments::NewRestArgumentsElements(
315      frame, formalParameterCount, Convert<intptr>(argumentCount));
316}
317
318builtin FastNewSloppyArguments(implicit context: Context)(f: JSFunction):
319    JSSloppyArgumentsObject {
320  return EmitFastNewSloppyArguments(f);
321}
322
323builtin FastNewStrictArguments(implicit context: Context)(f: JSFunction):
324    JSStrictArgumentsObject {
325  return EmitFastNewStrictArguments(f);
326}
327
328builtin FastNewRestArguments(implicit context: Context)(f: JSFunction):
329    JSArray {
330  return EmitFastNewRestArguments(f);
331}
332
333macro
334AccessSloppyArgumentsCommon(
335    receiver: JSObject, keyObject: Object): &Object labels Bailout {
336  const key = Cast<Smi>(keyObject) otherwise Bailout;
337  const elements =
338      Cast<SloppyArgumentsElements>(receiver.elements) otherwise Bailout;
339
340  try {
341    if (OutOfBounds(key, elements.length)) goto Unmapped;
342    const mappedIndex = elements.mapped_entries[key];
343    typeswitch (mappedIndex) {
344      case (contextIndex: Smi): {
345        return &(elements.context.elements[contextIndex]);
346      }
347      case (TheHole): {
348        goto Unmapped;
349      }
350    }
351  } label Unmapped {
352    typeswitch (elements.arguments) {
353      case (NumberDictionary): {
354        goto Bailout;
355      }
356      case (arguments: FixedArray): {
357        if (OutOfBounds(key, arguments.length)) goto Bailout;
358        if (arguments.objects[key] == TheHole) goto Bailout;
359        return &(arguments.objects[key]);
360      }
361    }
362  }
363}
364
365@export
366macro SloppyArgumentsLoad(receiver: JSObject, keyObject: Object):
367    JSAny labels Bailout {
368  return UnsafeCast<JSAny>(
369      *AccessSloppyArgumentsCommon(receiver, keyObject) otherwise Bailout);
370}
371
372@export
373macro SloppyArgumentsHas(receiver: JSObject, keyObject: Object):
374    JSAny labels Bailout {
375  AccessSloppyArgumentsCommon(receiver, keyObject) otherwise Bailout;
376  return True;
377}
378
379@export
380macro SloppyArgumentsStore(receiver: JSObject, keyObject: Object, value: JSAny):
381    JSAny labels Bailout {
382  let destination =
383      AccessSloppyArgumentsCommon(receiver, keyObject) otherwise Bailout;
384  *destination = value;
385  return value;
386}
387