arguments.tq 13.2 KB
Newer Older
Tobias Tebbi's avatar
Tobias Tebbi committed
1 2 3 4
// 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.

5
extern class JSArgumentsObject extends JSObject {}
Tobias Tebbi's avatar
Tobias Tebbi committed
6

7 8 9 10 11 12 13
type JSArgumentsObjectWithLength =
    JSSloppyArgumentsObject|JSStrictArgumentsObject;

@export
macro IsJSArgumentsObjectWithLength(implicit context: Context)(o: Object):
    bool {
  return Is<JSArgumentsObjectWithLength>(o);
Tobias Tebbi's avatar
Tobias Tebbi committed
14 15 16
}

// Just a starting shape for JSObject; properties can move after initialization.
17 18
extern shape JSSloppyArgumentsObject extends JSArgumentsObject {
  length: JSAny;
Tobias Tebbi's avatar
Tobias Tebbi committed
19 20 21 22
  callee: JSAny;
}

// Just a starting shape for JSObject; properties can move after initialization.
23 24
extern shape JSStrictArgumentsObject extends JSArgumentsObject {
  length: JSAny;
Tobias Tebbi's avatar
Tobias Tebbi committed
25 26
}

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
// 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
51
// entry has been deleted from the arguments object, and value is looked up in
52 53
// the unmapped arguments array, as described above. Otherwise, t is a Smi
// index into the context array specified at elements.context, and the return
54
// value is elements.context[t].
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
//
// 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;
80
  arguments: FixedArray|NumberDictionary;
81
  @cppRelaxedLoad mapped_entries[length]: Smi|TheHole;
82 83 84 85 86 87 88 89
}

macro NewSloppyArgumentsElements<Iterator: type>(
    length: Smi, context: Context, arguments: FixedArray,
    it: Iterator): SloppyArgumentsElements {
  return new
  SloppyArgumentsElements{length, context, arguments, mapped_entries: ...it};
}
Tobias Tebbi's avatar
Tobias Tebbi committed
90

91
@generatePrint
Tobias Tebbi's avatar
Tobias Tebbi committed
92 93 94
extern class AliasedArgumentsEntry extends Struct {
  aliased_context_slot: Smi;
}
95 96 97 98

// TODO(danno): This should be a namespace {} once supported
namespace arguments {

99 100 101 102 103 104 105 106 107 108
macro NewJSStrictArgumentsObject(implicit context: Context)(
    elements: FixedArray): JSStrictArgumentsObject {
  const map = GetStrictArgumentsMap();
  return new JSStrictArgumentsObject{
    map,
    properties_or_hash: kEmptyFixedArray,
    elements,
    length: elements.length
  };
}
109

110
macro NewJSSloppyArgumentsObject(implicit context: Context)(
111
    elements: FixedArrayBase, callee: JSFunction): JSSloppyArgumentsObject {
112 113 114 115 116 117 118 119 120
  const map = GetSloppyArgumentsMap();
  return new JSSloppyArgumentsObject{
    map,
    properties_or_hash: kEmptyFixedArray,
    elements,
    length: elements.length,
    callee
  };
}
121

122
macro NewJSFastAliasedArgumentsObject(implicit context: Context)(
123
    elements: FixedArrayBase, length: Smi,
124 125 126 127 128 129 130 131 132 133 134
    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
  };
}
135

136
struct ParameterMapIterator {
137 138 139 140
  macro Next(): Smi labels NoMore {
    if (this.currentIndex == this.endInterationIndex) goto NoMore;
    this.currentIndex--;
    return Convert<Smi>(this.currentIndex);
141
  }
142 143 144
  currentIndex: intptr;
  const endInterationIndex: intptr;
}
145

146
macro NewParameterMapIterator(
147
    context: Context, formalParameterCount: intptr,
148
    mappedCount: intptr): ParameterMapIterator {
149
  const flags = context.GetScopeInfo().flags;
150
  let contextHeaderSize: intptr = ContextSlot::MIN_CONTEXT_SLOTS;
151 152 153 154 155 156 157 158 159 160 161 162 163
  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,
164
    endInterationIndex: firstContextIndex
165 166
  };
}
167

168 169 170 171 172
struct ParameterValueIterator {
  macro Next(): Object labels NoMore() {
    if (this.mapped_count != 0) {
      this.mapped_count--;
      return TheHole;
173
    }
174 175
    if (this.current == this.arguments.length) goto NoMore;
    return this.arguments[this.current++];
176
  }
177 178 179 180
  mapped_count: intptr;
  const arguments: Arguments;
  current: intptr;
}
181

182 183 184 185 186 187 188 189
macro NewParameterValueIterator(
    mappedCount: intptr, arguments: Arguments): ParameterValueIterator {
  return ParameterValueIterator{
    mapped_count: mappedCount,
    arguments,
    current: mappedCount
  };
}
190

191 192 193 194 195 196 197 198
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);
}
199

200 201 202 203 204 205 206 207 208 209 210
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);
}

211 212 213 214 215
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();
216 217
  const elements =
      NewRestArgumentsElements(info.frame, formalParameterCount, argumentCount);
218 219
  return NewJSArray(map, elements);
}
220

221 222 223 224 225 226 227
macro NewStrictArgumentsElements(
    frame: FrameWithArguments, argumentCount: intptr): FixedArray {
  const arguments = GetFrameArguments(frame, argumentCount);
  const it = ArgumentsIterator{arguments, current: 0};
  return NewFixedArray(argumentCount, it);
}

228 229 230
macro NewStrictArguments(implicit context: Context)(
    info: FrameWithArgumentsInfo): JSStrictArgumentsObject {
  const argumentCount = Convert<intptr>(info.argument_count);
231
  const elements = NewStrictArgumentsElements(info.frame, argumentCount);
232 233 234
  return NewJSStrictArgumentsObject(elements);
}

235 236 237 238 239 240 241 242 243 244 245 246 247
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);
}

248 249 250 251
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);
252 253
  const parameterValues = arguments::NewSloppyArgumentsElements(
      info.frame, formalParameterCount, argumentCount);
254
  if (formalParameterCount == 0) {
255
    return NewJSSloppyArgumentsObject(parameterValues, callee);
256
  }
257
  const mappedCount = IntPtrMin(formalParameterCount, argumentCount);
258 259 260 261 262
  let paramIter =
      NewParameterMapIterator(context, formalParameterCount, mappedCount);
  const elementsLength = Convert<Smi>(mappedCount);
  const elements = NewSloppyArgumentsElements(
      elementsLength, context, parameterValues, paramIter);
263 264 265
  const length = Convert<Smi>(argumentCount);
  return NewJSFastAliasedArgumentsObject(elements, length, callee);
}
266 267

}  // namespace arguments
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294

@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);
}
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315

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));
}
316

317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
builtin FastNewSloppyArguments(implicit context: Context)(f: JSFunction):
    JSSloppyArgumentsObject {
  return EmitFastNewSloppyArguments(f);
}

builtin FastNewStrictArguments(implicit context: Context)(f: JSFunction):
    JSStrictArgumentsObject {
  return EmitFastNewStrictArguments(f);
}

builtin FastNewRestArguments(implicit context: Context)(f: JSFunction):
    JSArray {
  return EmitFastNewRestArguments(f);
}

332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
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;
}