OLD | NEW |
1 // Copyright (c) 2017 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef NativeValueTraitsImpl_h | 5 #ifndef NativeValueTraitsImpl_h |
6 #define NativeValueTraitsImpl_h | 6 #define NativeValueTraitsImpl_h |
7 | 7 |
8 #include "bindings/core/v8/IDLTypes.h" | 8 #include "bindings/core/v8/IDLTypes.h" |
9 #include "bindings/core/v8/NativeValueTraits.h" | 9 #include "bindings/core/v8/NativeValueTraits.h" |
10 #include "bindings/core/v8/V8Binding.h" | 10 #include "bindings/core/v8/V8Binding.h" |
(...skipping 287 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
298 } | 298 } |
299 | 299 |
300 static inline ImplType nativeValue(v8::Isolate* isolate, | 300 static inline ImplType nativeValue(v8::Isolate* isolate, |
301 v8::Local<v8::Value> value, | 301 v8::Local<v8::Value> value, |
302 ExceptionState& exceptionState, | 302 ExceptionState& exceptionState, |
303 int index) { | 303 int index) { |
304 return toImplArray<ImplType, T>(value, index, isolate, exceptionState); | 304 return toImplArray<ImplType, T>(value, index, isolate, exceptionState); |
305 } | 305 } |
306 }; | 306 }; |
307 | 307 |
| 308 // Records |
| 309 template <typename K, typename V> |
| 310 struct NativeValueTraits<IDLRecord<K, V>> |
| 311 : public NativeValueTraitsBase<IDLRecord<K, V>> { |
| 312 // Nondependent types need to be explicitly qualified to be accessible. |
| 313 using typename NativeValueTraitsBase<IDLRecord<K, V>>::ImplType; |
| 314 |
| 315 // Converts a JavaScript value |O| to an IDL record<K, V> value. In C++, a |
| 316 // record is represented as a Vector<std::pair<k, v>> (or a HeapVector if |
| 317 // necessary). See https://heycam.github.io/webidl/#es-record. |
| 318 static ImplType nativeValue(v8::Isolate* isolate, |
| 319 v8::Local<v8::Value> v8Value, |
| 320 ExceptionState& exceptionState) { |
| 321 v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| 322 |
| 323 // "1. If Type(O) is not Object, throw a TypeError." |
| 324 if (!v8Value->IsObject()) { |
| 325 exceptionState.throwTypeError( |
| 326 "Only objects can be converted to record<K,V> types"); |
| 327 return ImplType(); |
| 328 } |
| 329 v8::Local<v8::Object> v8Object = v8::Local<v8::Object>::Cast(v8Value); |
| 330 v8::TryCatch block(isolate); |
| 331 |
| 332 // "3. Let keys be ? O.[[OwnPropertyKeys]]()." |
| 333 v8::Local<v8::Array> keys; |
| 334 // While we could pass v8::ONLY_ENUMERABLE below, doing so breaks |
| 335 // web-platform-tests' headers-record.html and deviates from the spec |
| 336 // algorithm. |
| 337 // Symbols are being skipped due to |
| 338 // https://github.com/heycam/webidl/issues/294. |
| 339 if (!v8Object |
| 340 ->GetOwnPropertyNames(context, |
| 341 static_cast<v8::PropertyFilter>( |
| 342 v8::PropertyFilter::ALL_PROPERTIES | |
| 343 v8::PropertyFilter::SKIP_SYMBOLS)) |
| 344 .ToLocal(&keys)) { |
| 345 exceptionState.rethrowV8Exception(block.Exception()); |
| 346 return ImplType(); |
| 347 } |
| 348 if (keys->Length() > ImplType::maxCapacity()) { |
| 349 exceptionState.throwRangeError("Array length exceeds supported limit."); |
| 350 return ImplType(); |
| 351 } |
| 352 |
| 353 // "2. Let result be a new empty instance of record<K, V>." |
| 354 ImplType result; |
| 355 result.reserveInitialCapacity(keys->Length()); |
| 356 |
| 357 // The conversion algorithm needs a data structure with fast insertion at |
| 358 // the end while at the same time requiring fast checks for previous insert |
| 359 // of a given key. |seenKeys| is a key/position in |result| map that aids in |
| 360 // the latter part. |
| 361 HashMap<String, size_t> seenKeys; |
| 362 |
| 363 for (uint32_t i = 0; i < keys->Length(); ++i) { |
| 364 // "4. Repeat, for each element key of keys in List order:" |
| 365 v8::Local<v8::Value> key; |
| 366 if (!keys->Get(context, i).ToLocal(&key)) { |
| 367 exceptionState.rethrowV8Exception(block.Exception()); |
| 368 return ImplType(); |
| 369 } |
| 370 |
| 371 // "4.1. Let desc be ? O.[[GetOwnProperty]](key)." |
| 372 v8::Local<v8::Value> desc; |
| 373 if (!v8Object |
| 374 ->GetOwnPropertyDescriptor( |
| 375 context, key->ToString(context).ToLocalChecked()) |
| 376 .ToLocal(&desc)) { |
| 377 exceptionState.rethrowV8Exception(block.Exception()); |
| 378 return ImplType(); |
| 379 } |
| 380 |
| 381 // "4.2. If desc is not undefined and desc.[[Enumerable]] is true:" |
| 382 // We can call ToLocalChecked() and ToChecked() here because |
| 383 // GetOwnPropertyDescriptor is responsible for catching any exceptions |
| 384 // and failures, and if we got to this point of the code we have a proper |
| 385 // object that was not created by a user. |
| 386 DCHECK(desc->IsObject()); |
| 387 v8::Local<v8::Value> enumerable = |
| 388 v8::Local<v8::Object>::Cast(desc) |
| 389 ->Get(context, v8String(isolate, "enumerable")) |
| 390 .ToLocalChecked(); |
| 391 if (!enumerable->BooleanValue(context).ToChecked()) |
| 392 continue; |
| 393 |
| 394 // "4.2.1. Let typedKey be key converted to an IDL value of type K." |
| 395 String typedKey = |
| 396 NativeValueTraits<K>::nativeValue(isolate, key, exceptionState); |
| 397 if (exceptionState.hadException()) |
| 398 return ImplType(); |
| 399 |
| 400 // "4.2.2. Let value be ? Get(O, key)." |
| 401 v8::Local<v8::Value> value; |
| 402 if (!v8Object->Get(context, key).ToLocal(&value)) { |
| 403 exceptionState.rethrowV8Exception(block.Exception()); |
| 404 return ImplType(); |
| 405 } |
| 406 |
| 407 // "4.2.3. Let typedValue be value converted to an IDL value of type V." |
| 408 typename ImplType::ValueType::second_type typedValue = |
| 409 NativeValueTraits<V>::nativeValue(isolate, value, exceptionState); |
| 410 if (exceptionState.hadException()) |
| 411 return ImplType(); |
| 412 |
| 413 if (seenKeys.contains(typedKey)) { |
| 414 // "4.2.4. If typedKey is already a key in result, set its value to |
| 415 // typedValue. |
| 416 // Note: This can happen when O is a proxy object." |
| 417 const size_t pos = seenKeys.at(typedKey); |
| 418 result[pos] = std::make_pair(typedKey, typedValue); |
| 419 } else { |
| 420 // "4.2.5. Otherwise, append to result a mapping (typedKey, |
| 421 // typedValue)." |
| 422 // Note we can take this shortcut because we are always appending. |
| 423 const size_t pos = result.size(); |
| 424 seenKeys.set(typedKey, pos); |
| 425 result.uncheckedAppend(std::make_pair(typedKey, typedValue)); |
| 426 } |
| 427 } |
| 428 // "5. Return result." |
| 429 return result; |
| 430 } |
| 431 }; |
| 432 |
308 } // namespace blink | 433 } // namespace blink |
309 | 434 |
310 #endif // NativeValueTraitsImpl_h | 435 #endif // NativeValueTraitsImpl_h |
OLD | NEW |