]> git.saurik.com Git - apple/javascriptcore.git/blob - inspector/InspectorValues.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / inspector / InspectorValues.cpp
1 /*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2014 University of Washington. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "config.h"
33 #include "InspectorValues.h"
34
35 #include <wtf/DecimalNumber.h>
36 #include <wtf/dtoa.h>
37 #include <wtf/text/StringBuilder.h>
38
39 namespace Inspector {
40
41 namespace {
42
43 static const int stackLimit = 1000;
44
45 enum Token {
46 OBJECT_BEGIN,
47 OBJECT_END,
48 ARRAY_BEGIN,
49 ARRAY_END,
50 STRING,
51 NUMBER,
52 BOOL_TRUE,
53 BOOL_FALSE,
54 NULL_TOKEN,
55 LIST_SEPARATOR,
56 OBJECT_PAIR_SEPARATOR,
57 INVALID_TOKEN,
58 };
59
60 const char* const nullString = "null";
61 const char* const trueString = "true";
62 const char* const falseString = "false";
63
64 bool parseConstToken(const UChar* start, const UChar* end, const UChar** tokenEnd, const char* token)
65 {
66 while (start < end && *token != '\0' && *start++ == *token++) { }
67
68 if (*token != '\0')
69 return false;
70
71 *tokenEnd = start;
72 return true;
73 }
74
75 bool readInt(const UChar* start, const UChar* end, const UChar** tokenEnd, bool canHaveLeadingZeros)
76 {
77 if (start == end)
78 return false;
79
80 bool haveLeadingZero = '0' == *start;
81 int length = 0;
82 while (start < end && '0' <= *start && *start <= '9') {
83 ++start;
84 ++length;
85 }
86
87 if (!length)
88 return false;
89
90 if (!canHaveLeadingZeros && length > 1 && haveLeadingZero)
91 return false;
92
93 *tokenEnd = start;
94 return true;
95 }
96
97 bool parseNumberToken(const UChar* start, const UChar* end, const UChar** tokenEnd)
98 {
99 // We just grab the number here. We validate the size in DecodeNumber.
100 // According to RFC4627, a valid number is: [minus] int [frac] [exp]
101 if (start == end)
102 return false;
103
104 UChar c = *start;
105 if ('-' == c)
106 ++start;
107
108 if (!readInt(start, end, &start, false))
109 return false;
110
111 if (start == end) {
112 *tokenEnd = start;
113 return true;
114 }
115
116 // Optional fraction part
117 c = *start;
118 if ('.' == c) {
119 ++start;
120 if (!readInt(start, end, &start, true))
121 return false;
122 if (start == end) {
123 *tokenEnd = start;
124 return true;
125 }
126 c = *start;
127 }
128
129 // Optional exponent part
130 if ('e' == c || 'E' == c) {
131 ++start;
132 if (start == end)
133 return false;
134 c = *start;
135 if ('-' == c || '+' == c) {
136 ++start;
137 if (start == end)
138 return false;
139 }
140 if (!readInt(start, end, &start, true))
141 return false;
142 }
143
144 *tokenEnd = start;
145 return true;
146 }
147
148 bool readHexDigits(const UChar* start, const UChar* end, const UChar** tokenEnd, int digits)
149 {
150 if (end - start < digits)
151 return false;
152
153 for (int i = 0; i < digits; ++i) {
154 UChar c = *start++;
155 if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')))
156 return false;
157 }
158
159 *tokenEnd = start;
160 return true;
161 }
162
163 bool parseStringToken(const UChar* start, const UChar* end, const UChar** tokenEnd)
164 {
165 while (start < end) {
166 UChar c = *start++;
167 if ('\\' == c) {
168 c = *start++;
169 // Make sure the escaped char is valid.
170 switch (c) {
171 case 'x':
172 if (!readHexDigits(start, end, &start, 2))
173 return false;
174 break;
175 case 'u':
176 if (!readHexDigits(start, end, &start, 4))
177 return false;
178 break;
179 case '\\':
180 case '/':
181 case 'b':
182 case 'f':
183 case 'n':
184 case 'r':
185 case 't':
186 case 'v':
187 case '"':
188 break;
189 default:
190 return false;
191 }
192 } else if ('"' == c) {
193 *tokenEnd = start;
194 return true;
195 }
196 }
197
198 return false;
199 }
200
201 Token parseToken(const UChar* start, const UChar* end, const UChar** tokenStart, const UChar** tokenEnd)
202 {
203 while (start < end && isSpaceOrNewline(*start))
204 ++start;
205
206 if (start == end)
207 return INVALID_TOKEN;
208
209 *tokenStart = start;
210
211 switch (*start) {
212 case 'n':
213 if (parseConstToken(start, end, tokenEnd, nullString))
214 return NULL_TOKEN;
215 break;
216 case 't':
217 if (parseConstToken(start, end, tokenEnd, trueString))
218 return BOOL_TRUE;
219 break;
220 case 'f':
221 if (parseConstToken(start, end, tokenEnd, falseString))
222 return BOOL_FALSE;
223 break;
224 case '[':
225 *tokenEnd = start + 1;
226 return ARRAY_BEGIN;
227 case ']':
228 *tokenEnd = start + 1;
229 return ARRAY_END;
230 case ',':
231 *tokenEnd = start + 1;
232 return LIST_SEPARATOR;
233 case '{':
234 *tokenEnd = start + 1;
235 return OBJECT_BEGIN;
236 case '}':
237 *tokenEnd = start + 1;
238 return OBJECT_END;
239 case ':':
240 *tokenEnd = start + 1;
241 return OBJECT_PAIR_SEPARATOR;
242 case '0':
243 case '1':
244 case '2':
245 case '3':
246 case '4':
247 case '5':
248 case '6':
249 case '7':
250 case '8':
251 case '9':
252 case '-':
253 if (parseNumberToken(start, end, tokenEnd))
254 return NUMBER;
255 break;
256 case '"':
257 if (parseStringToken(start + 1, end, tokenEnd))
258 return STRING;
259 break;
260 }
261
262 return INVALID_TOKEN;
263 }
264
265 inline int hexToInt(UChar c)
266 {
267 if ('0' <= c && c <= '9')
268 return c - '0';
269 if ('A' <= c && c <= 'F')
270 return c - 'A' + 10;
271 if ('a' <= c && c <= 'f')
272 return c - 'a' + 10;
273
274 ASSERT_NOT_REACHED();
275 return 0;
276 }
277
278 bool decodeString(const UChar* start, const UChar* end, StringBuilder& output)
279 {
280 while (start < end) {
281 UChar c = *start++;
282 if ('\\' != c) {
283 output.append(c);
284 continue;
285 }
286 c = *start++;
287 switch (c) {
288 case '"':
289 case '/':
290 case '\\':
291 break;
292 case 'b':
293 c = '\b';
294 break;
295 case 'f':
296 c = '\f';
297 break;
298 case 'n':
299 c = '\n';
300 break;
301 case 'r':
302 c = '\r';
303 break;
304 case 't':
305 c = '\t';
306 break;
307 case 'v':
308 c = '\v';
309 break;
310 case 'x':
311 c = (hexToInt(*start) << 4) +
312 hexToInt(*(start + 1));
313 start += 2;
314 break;
315 case 'u':
316 c = (hexToInt(*start) << 12) +
317 (hexToInt(*(start + 1)) << 8) +
318 (hexToInt(*(start + 2)) << 4) +
319 hexToInt(*(start + 3));
320 start += 4;
321 break;
322 default:
323 return false;
324 }
325 output.append(c);
326 }
327
328 return true;
329 }
330
331 bool decodeString(const UChar* start, const UChar* end, String& output)
332 {
333 if (start == end) {
334 output = emptyString();
335 return true;
336 }
337
338 if (start > end)
339 return false;
340
341 StringBuilder buffer;
342 buffer.reserveCapacity(end - start);
343 if (!decodeString(start, end, buffer))
344 return false;
345
346 output = buffer.toString();
347 return true;
348 }
349
350 RefPtr<InspectorValue> buildValue(const UChar* start, const UChar* end, const UChar** valueTokenEnd, int depth)
351 {
352 if (depth > stackLimit)
353 return nullptr;
354
355 RefPtr<InspectorValue> result;
356 const UChar* tokenStart;
357 const UChar* tokenEnd;
358 Token token = parseToken(start, end, &tokenStart, &tokenEnd);
359 switch (token) {
360 case INVALID_TOKEN:
361 return nullptr;
362 case NULL_TOKEN:
363 result = InspectorValue::null();
364 break;
365 case BOOL_TRUE:
366 result = InspectorBasicValue::create(true);
367 break;
368 case BOOL_FALSE:
369 result = InspectorBasicValue::create(false);
370 break;
371 case NUMBER: {
372 bool ok;
373 double value = charactersToDouble(tokenStart, tokenEnd - tokenStart, &ok);
374 if (!ok)
375 return nullptr;
376 result = InspectorBasicValue::create(value);
377 break;
378 }
379 case STRING: {
380 String value;
381 bool ok = decodeString(tokenStart + 1, tokenEnd - 1, value);
382 if (!ok)
383 return nullptr;
384 result = InspectorString::create(value);
385 break;
386 }
387 case ARRAY_BEGIN: {
388 Ref<InspectorArray> array = InspectorArray::create();
389 start = tokenEnd;
390 token = parseToken(start, end, &tokenStart, &tokenEnd);
391 while (token != ARRAY_END) {
392 RefPtr<InspectorValue> arrayNode = buildValue(start, end, &tokenEnd, depth + 1);
393 if (!arrayNode)
394 return nullptr;
395 array->pushValue(WTF::move(arrayNode));
396
397 // After a list value, we expect a comma or the end of the list.
398 start = tokenEnd;
399 token = parseToken(start, end, &tokenStart, &tokenEnd);
400 if (token == LIST_SEPARATOR) {
401 start = tokenEnd;
402 token = parseToken(start, end, &tokenStart, &tokenEnd);
403 if (token == ARRAY_END)
404 return nullptr;
405 } else if (token != ARRAY_END) {
406 // Unexpected value after list value. Bail out.
407 return nullptr;
408 }
409 }
410 if (token != ARRAY_END)
411 return nullptr;
412 result = WTF::move(array);
413 break;
414 }
415 case OBJECT_BEGIN: {
416 Ref<InspectorObject> object = InspectorObject::create();
417 start = tokenEnd;
418 token = parseToken(start, end, &tokenStart, &tokenEnd);
419 while (token != OBJECT_END) {
420 if (token != STRING)
421 return nullptr;
422 String key;
423 if (!decodeString(tokenStart + 1, tokenEnd - 1, key))
424 return nullptr;
425 start = tokenEnd;
426
427 token = parseToken(start, end, &tokenStart, &tokenEnd);
428 if (token != OBJECT_PAIR_SEPARATOR)
429 return nullptr;
430 start = tokenEnd;
431
432 RefPtr<InspectorValue> value = buildValue(start, end, &tokenEnd, depth + 1);
433 if (!value)
434 return nullptr;
435 object->setValue(key, WTF::move(value));
436 start = tokenEnd;
437
438 // After a key/value pair, we expect a comma or the end of the
439 // object.
440 token = parseToken(start, end, &tokenStart, &tokenEnd);
441 if (token == LIST_SEPARATOR) {
442 start = tokenEnd;
443 token = parseToken(start, end, &tokenStart, &tokenEnd);
444 if (token == OBJECT_END)
445 return nullptr;
446 } else if (token != OBJECT_END) {
447 // Unexpected value after last object value. Bail out.
448 return nullptr;
449 }
450 }
451 if (token != OBJECT_END)
452 return nullptr;
453 result = WTF::move(object);
454 break;
455 }
456
457 default:
458 // We got a token that's not a value.
459 return nullptr;
460 }
461 *valueTokenEnd = tokenEnd;
462 return result.release();
463 }
464
465 inline bool escapeChar(UChar c, StringBuilder& dst)
466 {
467 switch (c) {
468 case '\b': dst.appendLiteral("\\b"); break;
469 case '\f': dst.appendLiteral("\\f"); break;
470 case '\n': dst.appendLiteral("\\n"); break;
471 case '\r': dst.appendLiteral("\\r"); break;
472 case '\t': dst.appendLiteral("\\t"); break;
473 case '\\': dst.appendLiteral("\\\\"); break;
474 case '"': dst.appendLiteral("\\\""); break;
475 default:
476 return false;
477 }
478 return true;
479 }
480
481 inline void doubleQuoteString(const String& str, StringBuilder& dst)
482 {
483 dst.append('"');
484 for (unsigned i = 0; i < str.length(); ++i) {
485 UChar c = str[i];
486 if (!escapeChar(c, dst)) {
487 if (c < 32 || c > 126 || c == '<' || c == '>') {
488 // 1. Escaping <, > to prevent script execution.
489 // 2. Technically, we could also pass through c > 126 as UTF8, but this
490 // is also optional. It would also be a pain to implement here.
491 dst.append(String::format("\\u%04X", c));
492 } else
493 dst.append(c);
494 }
495 }
496 dst.append('"');
497 }
498
499 } // anonymous namespace
500
501 bool InspectorValue::asBoolean(bool&) const
502 {
503 return false;
504 }
505
506 bool InspectorValue::asDouble(double&) const
507 {
508 return false;
509 }
510
511 bool InspectorValue::asDouble(float&) const
512 {
513 return false;
514 }
515
516 bool InspectorValue::asInteger(int&) const
517 {
518 return false;
519 }
520
521 bool InspectorValue::asInteger(unsigned&) const
522 {
523 return false;
524 }
525
526 bool InspectorValue::asInteger(long&) const
527 {
528 return false;
529 }
530
531 bool InspectorValue::asInteger(long long&) const
532 {
533 return false;
534 }
535
536 bool InspectorValue::asInteger(unsigned long&) const
537 {
538 return false;
539 }
540
541 bool InspectorValue::asInteger(unsigned long long&) const
542 {
543 return false;
544 }
545
546 bool InspectorValue::asString(String&) const
547 {
548 return false;
549 }
550
551 bool InspectorValue::asValue(RefPtr<InspectorValue>& output)
552 {
553 output = this;
554 return true;
555 }
556
557 bool InspectorValue::asObject(RefPtr<InspectorObject>&)
558 {
559 return false;
560 }
561
562 bool InspectorValue::asArray(RefPtr<InspectorArray>&)
563 {
564 return false;
565 }
566
567 bool InspectorValue::parseJSON(const String& jsonInput, RefPtr<InspectorValue>& output)
568 {
569 // FIXME: This whole file should just use StringView instead of UChar/length and avoid upconverting.
570 auto characters = StringView(jsonInput).upconvertedCharacters();
571 const UChar* start = characters;
572 const UChar* end = start + jsonInput.length();
573 const UChar* tokenEnd;
574 RefPtr<InspectorValue> result = buildValue(start, end, &tokenEnd, 0);
575 if (!result || tokenEnd != end)
576 return false;
577
578 output = result.release();
579 return true;
580 }
581
582 String InspectorValue::toJSONString() const
583 {
584 StringBuilder result;
585 result.reserveCapacity(512);
586 writeJSON(result);
587 return result.toString();
588 }
589
590 void InspectorValue::writeJSON(StringBuilder& output) const
591 {
592 ASSERT(m_type == Type::Null);
593
594 output.appendLiteral("null");
595 }
596
597 bool InspectorBasicValue::asBoolean(bool& output) const
598 {
599 if (type() != Type::Boolean)
600 return false;
601
602 output = m_booleanValue;
603 return true;
604 }
605
606 bool InspectorBasicValue::asDouble(double& output) const
607 {
608 if (type() != Type::Double)
609 return false;
610
611 output = m_doubleValue;
612 return true;
613 }
614
615 bool InspectorBasicValue::asDouble(float& output) const
616 {
617 if (type() != Type::Double)
618 return false;
619
620 output = static_cast<float>(m_doubleValue);
621 return true;
622 }
623
624 bool InspectorBasicValue::asInteger(int& output) const
625 {
626 if (type() != Type::Integer && type() != Type::Double)
627 return false;
628
629 output = static_cast<int>(m_doubleValue);
630 return true;
631 }
632
633 bool InspectorBasicValue::asInteger(unsigned& output) const
634 {
635 if (type() != Type::Integer && type() != Type::Double)
636 return false;
637
638 output = static_cast<unsigned>(m_doubleValue);
639 return true;
640 }
641
642 bool InspectorBasicValue::asInteger(long& output) const
643 {
644 if (type() != Type::Integer && type() != Type::Double)
645 return false;
646
647 output = static_cast<long>(m_doubleValue);
648 return true;
649 }
650
651 bool InspectorBasicValue::asInteger(long long& output) const
652 {
653 if (type() != Type::Integer && type() != Type::Double)
654 return false;
655
656 output = static_cast<long long>(m_doubleValue);
657 return true;
658 }
659
660 bool InspectorBasicValue::asInteger(unsigned long& output) const
661 {
662 if (type() != Type::Integer && type() != Type::Double)
663 return false;
664
665 output = static_cast<unsigned long>(m_doubleValue);
666 return true;
667 }
668
669 bool InspectorBasicValue::asInteger(unsigned long long& output) const
670 {
671 if (type() != Type::Integer && type() != Type::Double)
672 return false;
673
674 output = static_cast<unsigned long long>(m_doubleValue);
675 return true;
676 }
677
678 void InspectorBasicValue::writeJSON(StringBuilder& output) const
679 {
680 ASSERT(type() == Type::Boolean || type() == Type::Double || type() == Type::Integer);
681
682 if (type() == Type::Boolean) {
683 if (m_booleanValue)
684 output.appendLiteral("true");
685 else
686 output.appendLiteral("false");
687 } else if (type() == Type::Double || type() == Type::Integer) {
688 NumberToLStringBuffer buffer;
689 if (!std::isfinite(m_doubleValue)) {
690 output.appendLiteral("null");
691 return;
692 }
693 DecimalNumber decimal = m_doubleValue;
694 unsigned length = 0;
695 if (decimal.bufferLengthForStringDecimal() > WTF::NumberToStringBufferLength) {
696 // Not enough room for decimal. Use exponential format.
697 if (decimal.bufferLengthForStringExponential() > WTF::NumberToStringBufferLength) {
698 // Fallback for an abnormal case if it's too little even for exponential.
699 output.appendLiteral("NaN");
700 return;
701 }
702 length = decimal.toStringExponential(buffer, WTF::NumberToStringBufferLength);
703 } else
704 length = decimal.toStringDecimal(buffer, WTF::NumberToStringBufferLength);
705 output.append(buffer, length);
706 }
707 }
708
709 bool InspectorString::asString(String& output) const
710 {
711 output = m_stringValue;
712 return true;
713 }
714
715 void InspectorString::writeJSON(StringBuilder& output) const
716 {
717 ASSERT(type() == Type::String);
718 doubleQuoteString(m_stringValue, output);
719 }
720
721 InspectorObjectBase::~InspectorObjectBase()
722 {
723 }
724
725 bool InspectorObjectBase::asObject(RefPtr<InspectorObject>& output)
726 {
727 COMPILE_ASSERT(sizeof(InspectorObject) == sizeof(InspectorObjectBase), cannot_cast);
728
729 output = static_cast<InspectorObject*>(this);
730 return true;
731 }
732
733 InspectorObject* InspectorObjectBase::openAccessors()
734 {
735 COMPILE_ASSERT(sizeof(InspectorObject) == sizeof(InspectorObjectBase), cannot_cast);
736
737 return static_cast<InspectorObject*>(this);
738 }
739
740 bool InspectorObjectBase::getBoolean(const String& name, bool& output) const
741 {
742 RefPtr<InspectorValue> value;
743 if (!getValue(name, value))
744 return false;
745
746 return value->asBoolean(output);
747 }
748
749 bool InspectorObjectBase::getString(const String& name, String& output) const
750 {
751 RefPtr<InspectorValue> value;
752 if (!getValue(name, value))
753 return false;
754
755 return value->asString(output);
756 }
757
758 bool InspectorObjectBase::getObject(const String& name, RefPtr<InspectorObject>& output) const
759 {
760 RefPtr<InspectorValue> value;
761 if (!getValue(name, value))
762 return false;
763
764 return value->asObject(output);
765 }
766
767 bool InspectorObjectBase::getArray(const String& name, RefPtr<InspectorArray>& output) const
768 {
769 RefPtr<InspectorValue> value;
770 if (!getValue(name, value))
771 return false;
772
773 return value->asArray(output);
774 }
775
776 bool InspectorObjectBase::getValue(const String& name, RefPtr<InspectorValue>& output) const
777 {
778 Dictionary::const_iterator findResult = m_data.find(name);
779 if (findResult == m_data.end())
780 return false;
781
782 output = findResult->value;
783 return true;
784 }
785
786 void InspectorObjectBase::remove(const String& name)
787 {
788 m_data.remove(name);
789 m_order.removeFirst(name);
790 }
791
792 void InspectorObjectBase::writeJSON(StringBuilder& output) const
793 {
794 output.append('{');
795 for (size_t i = 0; i < m_order.size(); ++i) {
796 auto findResult = m_data.find(m_order[i]);
797 ASSERT(findResult != m_data.end());
798 if (i)
799 output.append(',');
800 doubleQuoteString(findResult->key, output);
801 output.append(':');
802 findResult->value->writeJSON(output);
803 }
804 output.append('}');
805 }
806
807 InspectorObjectBase::InspectorObjectBase()
808 : InspectorValue(Type::Object)
809 , m_data()
810 , m_order()
811 {
812 }
813
814 InspectorArrayBase::~InspectorArrayBase()
815 {
816 }
817
818 bool InspectorArrayBase::asArray(RefPtr<InspectorArray>& output)
819 {
820 COMPILE_ASSERT(sizeof(InspectorArrayBase) == sizeof(InspectorArray), cannot_cast);
821 output = static_cast<InspectorArray*>(this);
822 return true;
823 }
824
825 void InspectorArrayBase::writeJSON(StringBuilder& output) const
826 {
827 output.append('[');
828 for (Vector<RefPtr<InspectorValue>>::const_iterator it = m_data.begin(); it != m_data.end(); ++it) {
829 if (it != m_data.begin())
830 output.append(',');
831 (*it)->writeJSON(output);
832 }
833 output.append(']');
834 }
835
836 InspectorArrayBase::InspectorArrayBase()
837 : InspectorValue(Type::Array)
838 , m_data()
839 {
840 }
841
842 RefPtr<InspectorValue> InspectorArrayBase::get(size_t index) const
843 {
844 ASSERT_WITH_SECURITY_IMPLICATION(index < m_data.size());
845 return m_data[index];
846 }
847
848 Ref<InspectorObject> InspectorObject::create()
849 {
850 return adoptRef(*new InspectorObject);
851 }
852
853 Ref<InspectorArray> InspectorArray::create()
854 {
855 return adoptRef(*new InspectorArray);
856 }
857
858 Ref<InspectorValue> InspectorValue::null()
859 {
860 return adoptRef(*new InspectorValue);
861 }
862
863 Ref<InspectorString> InspectorString::create(const String& value)
864 {
865 return adoptRef(*new InspectorString(value));
866 }
867
868 Ref<InspectorString> InspectorString::create(const char* value)
869 {
870 return adoptRef(*new InspectorString(value));
871 }
872
873 Ref<InspectorBasicValue> InspectorBasicValue::create(bool value)
874 {
875 return adoptRef(*new InspectorBasicValue(value));
876 }
877
878 Ref<InspectorBasicValue> InspectorBasicValue::create(int value)
879 {
880 return adoptRef(*new InspectorBasicValue(value));
881 }
882
883 Ref<InspectorBasicValue> InspectorBasicValue::create(double value)
884 {
885 return adoptRef(*new InspectorBasicValue(value));
886 }
887
888 } // namespace Inspector