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