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 inline 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 if (!isPropertyEnumerable(isolate, desc, exceptionState)) { | |
383 if (exceptionState.hadException()) | |
384 return ImplType(); | |
385 continue; | |
386 } | |
387 | |
388 // "4.2.1. Let typedKey be key converted to an IDL value of type K." | |
389 String typedKey = | |
390 NativeValueTraits<K>::nativeValue(isolate, key, exceptionState); | |
391 if (exceptionState.hadException()) | |
392 return ImplType(); | |
393 | |
394 // "4.2.2. Let value be ? Get(O, key)." | |
395 v8::Local<v8::Value> value; | |
396 if (!v8Object->Get(context, key).ToLocal(&value)) { | |
397 exceptionState.rethrowV8Exception(block.Exception()); | |
398 return ImplType(); | |
399 } | |
400 | |
401 // "4.2.3. Let typedValue be value converted to an IDL value of type V." | |
402 typename ImplType::ValueType::second_type typedValue = | |
403 NativeValueTraits<V>::nativeValue(isolate, value, exceptionState); | |
404 if (exceptionState.hadException()) | |
405 return ImplType(); | |
406 | |
407 if (seenKeys.contains(typedKey)) { | |
408 // "4.2.4. If typedKey is already a key in result, set its value to | |
409 // typedValue. | |
410 // Note: This can happen when O is a proxy object." | |
411 const size_t pos = seenKeys.at(typedKey); | |
412 result[pos] = std::make_pair(typedKey, typedValue); | |
413 } else { | |
414 // "4.2.5. Otherwise, append to result a mapping (typedKey, | |
415 // typedValue)." | |
416 // Note we can take this shortcut because we are always appending. | |
417 const size_t pos = result.size(); | |
418 seenKeys.set(typedKey, pos); | |
419 result.uncheckedAppend(std::make_pair(typedKey, typedValue)); | |
420 } | |
421 } | |
422 // "5. Return result." | |
423 return result; | |
424 } | |
425 | |
426 private: | |
427 static bool isPropertyEnumerable(v8::Isolate* isolate, | |
428 v8::Local<v8::Value> descriptor, | |
429 ExceptionState& exceptionState) { | |
430 if (!descriptor->IsObject()) | |
431 return false; | |
432 v8::TryCatch block(isolate); | |
433 v8::Local<v8::Value> enumerable; | |
434 if (!v8::Local<v8::Object>::Cast(descriptor) | |
435 ->Get(isolate->GetCurrentContext(), | |
436 v8String(isolate, "enumerable")) | |
437 .ToLocal(&enumerable)) { | |
Yuki
2017/03/10 06:54:32
You can safely use ToLocalChecked() here because |
Raphael Kubo da Costa (rakuco)
2017/03/10 08:19:09
Are you sure about this? One of the tests I added
Yuki
2017/03/10 09:52:21
I didn't test anything for myself, so I can be wro
| |
438 exceptionState.rethrowV8Exception(block.Exception()); | |
439 return false; | |
440 } | |
441 return toBoolean(isolate, enumerable, exceptionState); | |
Yuki
2017/03/10 06:54:32
This is okay, but you can write
return enumera
Raphael Kubo da Costa (rakuco)
2017/03/10 08:19:09
Same here: while V8 itself performs some boolean c
Yuki
2017/03/10 09:52:21
IIUC, there is no way for author script to install
Raphael Kubo da Costa (rakuco)
2017/03/10 10:42:37
You're right about those 2 points: while the proxy
| |
442 } | |
443 }; | |
444 | |
308 } // namespace blink | 445 } // namespace blink |
309 | 446 |
310 #endif // NativeValueTraitsImpl_h | 447 #endif // NativeValueTraitsImpl_h |
OLD | NEW |