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