1 # HG changeset patch |
|
2 # Parent 58dd942011a81f3149d9bc34e808806bda099056 |
|
3 # User Landry Breuil <landry@openbsd.org> |
|
4 Use YARR interpreter instead of PCRE on platforms where YARR JIT is not |
|
5 supported |
|
6 |
|
7 diff --git a/js/src/Makefile.in b/js/src/Makefile.in |
1 diff --git a/js/src/Makefile.in b/js/src/Makefile.in |
8 --- a/js/src/Makefile.in |
2 --- a/js/src/Makefile.in |
9 +++ b/js/src/Makefile.in |
3 +++ b/js/src/Makefile.in |
10 @@ -335,25 +335,29 @@ CPPSRCS += checks.cc \ |
4 @@ -332,30 +332,33 @@ CPPSRCS += checks.cc \ |
|
5 platform.cc \ |
|
6 utils.cc \ |
|
7 $(NONE) |
|
8 |
|
9 # |
11 # END enclude sources for V8 dtoa |
10 # END enclude sources for V8 dtoa |
12 ############################################# |
11 ############################################# |
13 |
12 |
14 # For architectures without YARR JIT, PCRE is faster than the YARR |
13 -# For architectures without YARR JIT, PCRE is faster than the YARR |
15 # interpreter (bug 684559). |
14 -# interpreter (bug 684559). |
16 |
15 - |
17 ifeq (,$(filter arm% sparc %86 x86_64 mips%,$(TARGET_CPU))) |
16 ifeq (,$(filter arm% sparc %86 x86_64 mips%,$(TARGET_CPU))) |
18 |
17 |
19 -VPATH += $(srcdir)/yarr/pcre \ |
18 -VPATH += $(srcdir)/yarr/pcre \ |
20 +VPATH += $(srcdir)/assembler \ |
19 +VPATH += $(srcdir)/assembler \ |
21 + $(srcdir)/assembler/wtf \ |
20 + $(srcdir)/assembler/wtf \ |
22 + $(srcdir)/yarr \ |
21 + $(srcdir)/assembler/jit \ |
|
22 + $(srcdir)/yarr \ |
23 $(NULL) |
23 $(NULL) |
24 |
24 |
25 CPPSRCS += \ |
25 -CPPSRCS += \ |
26 - pcre_compile.cpp \ |
26 - pcre_compile.cpp \ |
27 - pcre_exec.cpp \ |
27 - pcre_exec.cpp \ |
28 - pcre_tables.cpp \ |
28 - pcre_tables.cpp \ |
29 - pcre_xclass.cpp \ |
29 - pcre_xclass.cpp \ |
30 - pcre_ucp_searchfuncs.cpp \ |
30 - pcre_ucp_searchfuncs.cpp \ |
31 + OSAllocatorOS2.cpp \ |
31 +CPPSRCS += ExecutableAllocator.cpp \ |
32 + OSAllocatorPosix.cpp \ |
32 + ExecutableAllocatorPosix.cpp \ |
33 + OSAllocatorWin.cpp \ |
33 + OSAllocatorOS2.cpp \ |
34 + PageBlock.cpp \ |
34 + OSAllocatorPosix.cpp \ |
35 + YarrInterpreter.cpp \ |
35 + OSAllocatorWin.cpp \ |
36 + YarrPattern.cpp \ |
36 + PageBlock.cpp \ |
37 + YarrSyntaxChecker.cpp \ |
37 + YarrInterpreter.cpp \ |
|
38 + YarrPattern.cpp \ |
|
39 + YarrSyntaxChecker.cpp \ |
38 $(NULL) |
40 $(NULL) |
39 else |
41 else |
40 |
42 |
41 ############################################### |
43 ############################################### |
42 # BEGIN include sources for the Nitro assembler |
44 # BEGIN include sources for the Nitro assembler |
43 # |
45 # |
44 |
46 |
45 ENABLE_YARR_JIT = 1 |
47 ENABLE_YARR_JIT = 1 |
46 @@ -878,20 +882,20 @@ endif |
48 diff --git a/js/src/assembler/jit/ExecutableAllocator.h b/js/src/assembler/jit/ExecutableAllocator.h |
47 |
49 --- a/js/src/assembler/jit/ExecutableAllocator.h |
48 ############################################### |
50 +++ b/js/src/assembler/jit/ExecutableAllocator.h |
49 # BEGIN kludges for the Nitro assembler |
51 @@ -462,18 +462,16 @@ public: |
50 # |
52 : "r" (code), "r" (reinterpret_cast<char*>(code) + size) |
51 |
53 : "r0", "r1", "r2"); |
52 # Needed to "configure" it correctly. Unfortunately these |
54 } |
53 # flags wind up being applied to all code in js/src, not just |
55 #elif WTF_CPU_SPARC |
54 # the code in js/src/assembler. |
56 static void cacheFlush(void* code, size_t size) |
55 -CXXFLAGS += -DUSE_SYSTEM_MALLOC=1 -DENABLE_ASSEMBLER=1 |
57 { |
56 +CXXFLAGS += -DUSE_SYSTEM_MALLOC=1 |
58 sync_instruction_memory((caddr_t)code, size); |
57 |
59 } |
58 ifneq (,$(ENABLE_YARR_JIT)$(ENABLE_METHODJIT)) |
60 -#else |
59 -CXXFLAGS += -DENABLE_JIT=1 |
61 - #error "The cacheFlush support is missing on this platform." |
60 +CXXFLAGS += -DENABLE_JIT=1 -DENABLE_ASSEMBLER=1 |
|
61 endif |
|
62 |
|
63 INCLUDES += -I$(srcdir)/assembler -I$(srcdir)/yarr |
|
64 |
|
65 ifdef ENABLE_METHODJIT |
|
66 # Build a standalone test program that exercises the assembler |
|
67 # sources a bit. |
|
68 TESTMAIN_OBJS = \ |
|
69 diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp |
|
70 --- a/js/src/jsapi.cpp |
|
71 +++ b/js/src/jsapi.cpp |
|
72 @@ -696,17 +696,19 @@ JS_IsBuiltinFunctionConstructor(JSFuncti |
|
73 static JSBool js_NewRuntimeWasCalled = JS_FALSE; |
|
74 |
|
75 JSRuntime::JSRuntime() |
|
76 : atomsCompartment(NULL), |
|
77 #ifdef JS_THREADSAFE |
|
78 ownerThread_(NULL), |
|
79 #endif |
62 #endif |
80 tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), |
63 |
81 +#if ENABLE_ASSEMBLER |
64 private: |
82 execAlloc_(NULL), |
65 |
83 +#endif |
66 #if ENABLE_ASSEMBLER_WX_EXCLUSIVE |
84 bumpAlloc_(NULL), |
67 static void reprotectRegion(void*, size_t, ProtectionSetting); |
85 nativeStackBase(0), |
68 #endif |
86 nativeStackQuota(0), |
|
87 interpreterFrames(NULL), |
|
88 cxCallback(NULL), |
|
89 compartmentCallback(NULL), |
|
90 activityCallback(NULL), |
|
91 activityCallbackArg(NULL), |
|
92 @@ -851,17 +853,19 @@ JSRuntime::init(uint32_t maxbytes) |
|
93 nativeStackBase = GetNativeStackBase(); |
|
94 return true; |
|
95 } |
|
96 |
|
97 JSRuntime::~JSRuntime() |
|
98 { |
|
99 JS_ASSERT(onOwnerThread()); |
|
100 |
|
101 +#if ENABLE_ASSEMBLER |
|
102 delete_<JSC::ExecutableAllocator>(execAlloc_); |
|
103 +#endif |
|
104 delete_<WTF::BumpPointerAllocator>(bumpAlloc_); |
|
105 |
|
106 #ifdef DEBUG |
|
107 /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */ |
|
108 if (!JS_CLIST_IS_EMPTY(&contextList)) { |
|
109 JSContext *cx, *iter = NULL; |
|
110 uintN cxcount = 0; |
|
111 while ((cx = js_ContextIterator(this, JS_TRUE, &iter)) != NULL) { |
|
112 diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp |
|
113 --- a/js/src/jscntxt.cpp |
|
114 +++ b/js/src/jscntxt.cpp |
|
115 @@ -100,19 +100,21 @@ JSRuntime::sizeOfExcludingThis(JSMallocS |
|
116 if (normal) |
|
117 *normal = mallocSizeOf(dtoaState); |
|
118 |
|
119 if (temporary) |
|
120 *temporary = tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf); |
|
121 |
|
122 if (regexpCode) { |
|
123 size_t method = 0, regexp = 0, unused = 0; |
|
124 +#if ENABLE_ASSEMBLER |
|
125 if (execAlloc_) |
|
126 execAlloc_->sizeOfCode(&method, ®exp, &unused); |
|
127 JS_ASSERT(method == 0); /* this execAlloc is only used for regexp code */ |
|
128 +#endif |
|
129 *regexpCode = regexp + unused; |
|
130 } |
|
131 |
|
132 if (stackCommitted) |
|
133 *stackCommitted = stackSpace.sizeOfCommitted(); |
|
134 } |
|
135 |
|
136 JS_FRIEND_API(void) |
|
137 @@ -124,33 +126,37 @@ JSRuntime::triggerOperationCallback() |
|
138 */ |
|
139 JS_ATOMIC_SET(&interrupt, 1); |
|
140 } |
|
141 |
|
142 void |
|
143 JSRuntime::setJitHardening(bool enabled) |
|
144 { |
|
145 jitHardening = enabled; |
|
146 +#if ENABLE_ASSEMBLER |
|
147 if (execAlloc_) |
|
148 execAlloc_->setRandomize(enabled); |
|
149 +#endif |
|
150 } |
|
151 |
|
152 +#if ENABLE_ASSEMBLER |
|
153 JSC::ExecutableAllocator * |
|
154 JSRuntime::createExecutableAllocator(JSContext *cx) |
|
155 { |
|
156 JS_ASSERT(!execAlloc_); |
|
157 JS_ASSERT(cx->runtime == this); |
|
158 |
|
159 JSC::AllocationBehavior randomize = |
|
160 jitHardening ? JSC::AllocationCanRandomize : JSC::AllocationDeterministic; |
|
161 execAlloc_ = new_<JSC::ExecutableAllocator>(randomize); |
|
162 if (!execAlloc_) |
|
163 js_ReportOutOfMemory(cx); |
|
164 return execAlloc_; |
|
165 } |
|
166 +#endif |
|
167 |
|
168 WTF::BumpPointerAllocator * |
|
169 JSRuntime::createBumpPointerAllocator(JSContext *cx) |
|
170 { |
|
171 JS_ASSERT(!bumpAlloc_); |
|
172 JS_ASSERT(cx->runtime == this); |
|
173 |
|
174 bumpAlloc_ = new_<WTF::BumpPointerAllocator>(); |
|
175 diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h |
|
176 --- a/js/src/jscntxt.h |
|
177 +++ b/js/src/jscntxt.h |
|
178 @@ -219,26 +219,32 @@ struct JSRuntime : js::RuntimeFriendFiel |
|
179 static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; |
|
180 js::LifoAlloc tempLifoAlloc; |
|
181 |
|
182 private: |
|
183 /* |
|
184 * Both of these allocators are used for regular expression code which is shared at the |
|
185 * thread-data level. |
|
186 */ |
|
187 +#if ENABLE_ASSEMBLER |
|
188 JSC::ExecutableAllocator *execAlloc_; |
|
189 +#endif |
|
190 WTF::BumpPointerAllocator *bumpAlloc_; |
|
191 |
|
192 +#if ENABLE_ASSEMBLER |
|
193 JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx); |
|
194 +#endif |
|
195 WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx); |
|
196 |
|
197 public: |
|
198 +#if ENABLE_ASSEMBLER |
|
199 JSC::ExecutableAllocator *getExecutableAllocator(JSContext *cx) { |
|
200 return execAlloc_ ? execAlloc_ : createExecutableAllocator(cx); |
|
201 } |
|
202 +#endif |
|
203 WTF::BumpPointerAllocator *getBumpPointerAllocator(JSContext *cx) { |
|
204 return bumpAlloc_ ? bumpAlloc_ : createBumpPointerAllocator(cx); |
|
205 } |
|
206 |
|
207 /* Base address of the native stack for the current thread. */ |
|
208 uintptr_t nativeStackBase; |
|
209 |
|
210 /* The native stack size limit that runtime should not exceed. */ |
|
211 diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h |
|
212 --- a/js/src/jsprvtd.h |
|
213 +++ b/js/src/jsprvtd.h |
|
214 @@ -313,22 +313,23 @@ typedef Handle<BaseShape*> Handl |
|
215 typedef Handle<types::TypeObject*> HandleTypeObject; |
|
216 typedef Handle<JSString*> HandleString; |
|
217 typedef Handle<JSAtom*> HandleAtom; |
|
218 typedef Handle<jsid> HandleId; |
|
219 typedef Handle<Value> HandleValue; |
|
220 |
|
221 } /* namespace js */ |
|
222 |
|
223 +#if ENABLE_ASSEMBLER |
|
224 namespace JSC { |
|
225 |
|
226 class ExecutableAllocator; |
|
227 |
|
228 } /* namespace JSC */ |
|
229 - |
|
230 +#endif |
|
231 namespace WTF { |
|
232 |
|
233 class BumpPointerAllocator; |
|
234 |
|
235 } /* namespace WTF */ |
|
236 |
|
237 } /* export "C++" */ |
|
238 |
69 |
239 diff --git a/js/src/vm/RegExpObject-inl.h b/js/src/vm/RegExpObject-inl.h |
70 diff --git a/js/src/vm/RegExpObject-inl.h b/js/src/vm/RegExpObject-inl.h |
240 --- a/js/src/vm/RegExpObject-inl.h |
71 --- a/js/src/vm/RegExpObject-inl.h |
241 +++ b/js/src/vm/RegExpObject-inl.h |
72 +++ b/js/src/vm/RegExpObject-inl.h |
242 @@ -132,26 +132,28 @@ RegExpObject::setMultiline(bool enabled) |
73 @@ -132,16 +132,17 @@ RegExpObject::setMultiline(bool enabled) |
243 } |
74 } |
244 |
75 |
245 inline void |
76 inline void |
246 RegExpObject::setSticky(bool enabled) |
77 RegExpObject::setSticky(bool enabled) |
247 { |
78 { |
333 -} |
153 -} |
334 - |
154 - |
335 -#endif /* ENABLE_YARR_JIT */ |
155 -#endif /* ENABLE_YARR_JIT */ |
336 - |
156 - |
337 bool |
157 bool |
338 RegExpCode::compile(JSContext *cx, JSLinearString &pattern, uintN *parenCount, RegExpFlag flags) |
158 RegExpCode::compile(JSContext *cx, JSLinearString &pattern, unsigned *parenCount, RegExpFlag flags) |
339 { |
159 { |
340 -#if ENABLE_YARR_JIT |
160 #if ENABLE_YARR_JIT |
341 /* Parse the pattern. */ |
161 /* Parse the pattern. */ |
342 ErrorCode yarrError; |
162 ErrorCode yarrError; |
343 YarrPattern yarrPattern(pattern, bool(flags & IgnoreCaseFlag), bool(flags & MultilineFlag), |
163 YarrPattern yarrPattern(pattern, bool(flags & IgnoreCaseFlag), bool(flags & MultilineFlag), |
344 &yarrError); |
164 &yarrError); |
345 if (yarrError) { |
|
346 reportYarrError(cx, NULL, yarrError); |
|
347 return false; |
|
348 } |
|
349 *parenCount = yarrPattern.m_numSubpatterns; |
|
350 |
|
351 /* |
|
352 * The YARR JIT compiler attempts to compile the parsed pattern. If |
|
353 * it cannot, it informs us via |codeBlock.isFallBack()|, in which |
|
354 * case we have to bytecode compile it. |
|
355 */ |
|
356 |
|
357 -#ifdef JS_METHODJIT |
|
358 +#if ENABLE_YARR_JIT && defined(JS_METHODJIT) |
|
359 if (isJITRuntimeEnabled(cx) && !yarrPattern.m_containsBackreferences) { |
|
360 JSC::ExecutableAllocator *execAlloc = cx->runtime->getExecutableAllocator(cx); |
|
361 if (!execAlloc) { |
|
362 js_ReportOutOfMemory(cx); |
|
363 return false; |
|
364 } |
|
365 |
|
366 JSGlobalData globalData(execAlloc); |
|
367 @@ -271,58 +233,41 @@ RegExpCode::compile(JSContext *cx, JSLin |
|
368 #endif |
|
369 |
|
370 WTF::BumpPointerAllocator *bumpAlloc = cx->runtime->getBumpPointerAllocator(cx); |
|
371 if (!bumpAlloc) { |
|
372 js_ReportOutOfMemory(cx); |
|
373 return false; |
|
374 } |
|
375 |
|
376 +#if ENABLE_YARR_JIT |
|
377 codeBlock.setFallBack(true); |
|
378 +#endif |
|
379 byteCode = byteCompile(yarrPattern, bumpAlloc).get(); |
|
380 return true; |
|
381 -#else /* !defined(ENABLE_YARR_JIT) */ |
|
382 - int error = 0; |
|
383 - compiled = jsRegExpCompile(pattern.chars(), pattern.length(), |
|
384 - ignoreCase() ? JSRegExpIgnoreCase : JSRegExpDoNotIgnoreCase, |
|
385 - multiline() ? JSRegExpMultiline : JSRegExpSingleLine, |
|
386 - parenCount, &error); |
|
387 - if (error) { |
|
388 - reportPCREError(cx, error); |
|
389 - return false; |
|
390 - } |
|
391 - return true; |
|
392 -#endif |
|
393 } |
|
394 |
|
395 RegExpRunStatus |
|
396 RegExpCode::execute(JSContext *cx, const jschar *chars, size_t length, size_t start, |
|
397 int *output, size_t outputCount) |
|
398 { |
|
399 int result; |
|
400 #if ENABLE_YARR_JIT |
|
401 (void) cx; /* Unused. */ |
|
402 if (codeBlock.isFallBack()) |
|
403 result = JSC::Yarr::interpret(byteCode, chars, start, length, output); |
|
404 else |
|
405 result = JSC::Yarr::execute(codeBlock, chars, start, length, output); |
|
406 #else |
|
407 - result = jsRegExpExecute(cx, compiled, chars, length, start, output, outputCount); |
|
408 + result = JSC::Yarr::interpret(byteCode, chars, start, length, output); |
|
409 #endif |
|
410 |
|
411 if (result == -1) |
|
412 return RegExpRunStatus_Success_NotFound; |
|
413 |
|
414 -#if !ENABLE_YARR_JIT |
|
415 - if (result < 0) { |
|
416 - reportPCREError(cx, result); |
|
417 - return RegExpRunStatus_Error; |
|
418 - } |
|
419 -#endif |
|
420 - |
|
421 JS_ASSERT(result >= 0); |
|
422 return RegExpRunStatus_Success; |
|
423 } |
|
424 |
|
425 /* RegExpObject */ |
|
426 |
|
427 static void |
|
428 regexp_trace(JSTracer *trc, JSObject *obj) |
|
429 diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h |
165 diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h |
430 --- a/js/src/vm/RegExpObject.h |
166 --- a/js/src/vm/RegExpObject.h |
431 +++ b/js/src/vm/RegExpObject.h |
167 +++ b/js/src/vm/RegExpObject.h |
432 @@ -46,20 +46,18 @@ |
168 @@ -46,20 +46,18 @@ |
433 #include <stddef.h> |
169 #include <stddef.h> |
449 * |
185 * |
450 * There are several engine concepts associated with a single logical regexp: |
186 * There are several engine concepts associated with a single logical regexp: |
451 * |
187 * |
452 * RegExpObject - The JS-visible object whose .[[Class]] equals "RegExp" |
188 * RegExpObject - The JS-visible object whose .[[Class]] equals "RegExp" |
453 * |
189 * |
454 @@ -107,78 +105,61 @@ class RegExpObjectBuilder |
|
455 |
|
456 JSObject * |
|
457 CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto); |
|
458 |
|
459 namespace detail { |
|
460 |
|
461 class RegExpCode |
|
462 { |
|
463 -#if ENABLE_YARR_JIT |
|
464 typedef JSC::Yarr::BytecodePattern BytecodePattern; |
|
465 typedef JSC::Yarr::ErrorCode ErrorCode; |
|
466 + typedef JSC::Yarr::YarrPattern YarrPattern; |
|
467 +#if ENABLE_YARR_JIT |
|
468 typedef JSC::Yarr::JSGlobalData JSGlobalData; |
|
469 typedef JSC::Yarr::YarrCodeBlock YarrCodeBlock; |
|
470 - typedef JSC::Yarr::YarrPattern YarrPattern; |
|
471 |
|
472 /* Note: Native code is valid only if |codeBlock.isFallBack() == false|. */ |
|
473 YarrCodeBlock codeBlock; |
|
474 +#endif |
|
475 BytecodePattern *byteCode; |
|
476 -#else |
|
477 - JSRegExp *compiled; |
|
478 -#endif |
|
479 |
|
480 public: |
|
481 RegExpCode() |
|
482 : |
|
483 #if ENABLE_YARR_JIT |
|
484 codeBlock(), |
|
485 +#endif |
|
486 byteCode(NULL) |
|
487 -#else |
|
488 - compiled(NULL) |
|
489 -#endif |
|
490 { } |
|
491 |
|
492 ~RegExpCode() { |
|
493 #if ENABLE_YARR_JIT |
|
494 codeBlock.release(); |
|
495 +#endif |
|
496 if (byteCode) |
|
497 Foreground::delete_<BytecodePattern>(byteCode); |
|
498 -#else |
|
499 - if (compiled) |
|
500 - jsRegExpFree(compiled); |
|
501 -#endif |
|
502 } |
|
503 |
|
504 static bool checkSyntax(JSContext *cx, TokenStream *tokenStream, JSLinearString *source) { |
|
505 -#if ENABLE_YARR_JIT |
|
506 ErrorCode error = JSC::Yarr::checkSyntax(*source); |
|
507 if (error == JSC::Yarr::NoError) |
|
508 return true; |
|
509 |
|
510 reportYarrError(cx, tokenStream, error); |
|
511 return false; |
|
512 -#else |
|
513 -# error "Syntax checking not implemented for !ENABLE_YARR_JIT" |
|
514 -#endif |
|
515 } |
|
516 |
|
517 #if ENABLE_YARR_JIT |
|
518 static inline bool isJITRuntimeEnabled(JSContext *cx); |
|
519 +#endif |
|
520 static void reportYarrError(JSContext *cx, TokenStream *ts, JSC::Yarr::ErrorCode error); |
|
521 -#else |
|
522 - static void reportPCREError(JSContext *cx, int error); |
|
523 -#endif |
|
524 |
|
525 static size_t getOutputSize(size_t pairCount) { |
|
526 -#if ENABLE_YARR_JIT |
|
527 return pairCount * 2; |
|
528 -#else |
|
529 - return pairCount * 3; /* Should be x2, but PCRE has... needs. */ |
|
530 -#endif |
|
531 } |
|
532 |
|
533 bool compile(JSContext *cx, JSLinearString &pattern, uintN *parenCount, RegExpFlag flags); |
|
534 |
|
535 |
|
536 RegExpRunStatus |
|
537 execute(JSContext *cx, const jschar *chars, size_t length, size_t start, |
|
538 int *output, size_t outputCount); |
|
539 diff --git a/js/src/yarr/wtfbridge.h b/js/src/yarr/wtfbridge.h |
190 diff --git a/js/src/yarr/wtfbridge.h b/js/src/yarr/wtfbridge.h |
540 --- a/js/src/yarr/wtfbridge.h |
191 --- a/js/src/yarr/wtfbridge.h |
541 +++ b/js/src/yarr/wtfbridge.h |
192 +++ b/js/src/yarr/wtfbridge.h |
542 @@ -44,19 +44,17 @@ |
193 @@ -44,19 +44,17 @@ |
543 * WTF compatibility layer. This file provides various type and data |
194 * WTF compatibility layer. This file provides various type and data |