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