]>
Commit | Line | Data |
---|---|---|
9dae56ea A |
1 | /* |
2 | * Copyright (C) 2008 Apple Inc. All rights reserved. | |
3 | * | |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
7 | * 1. Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * 2. Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * | |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 | */ | |
25 | ||
26 | #include "config.h" | |
27 | #include "WREC.h" | |
28 | ||
29 | #if ENABLE(WREC) | |
30 | ||
31 | #include "CharacterClassConstructor.h" | |
32 | #include "Interpreter.h" | |
33 | #include "WRECFunctors.h" | |
34 | #include "WRECParser.h" | |
35 | #include "pcre_internal.h" | |
36 | ||
37 | using namespace WTF; | |
38 | ||
39 | namespace JSC { namespace WREC { | |
40 | ||
41 | void Generator::generateEnter() | |
42 | { | |
43 | #if PLATFORM(X86_64) | |
44 | // On x86-64 edi and esi are caller preserved, so nothing to do here. | |
45 | // The four arguments have been passed in the registers %rdi, %rsi, | |
46 | // %rdx, %rcx - shuffle these into the expected locations. | |
47 | move(X86::edi, input); // (arg 1) edi -> eax | |
48 | move(X86::ecx, output); // (arg 4) ecx -> edi | |
49 | move(X86::edx, length); // (arg 3) edx -> ecx | |
50 | move(X86::esi, index); // (arg 2) esi -> edx | |
51 | ||
52 | #else | |
53 | // On x86 edi & esi are callee preserved registers. | |
54 | push(X86::edi); | |
55 | push(X86::esi); | |
56 | ||
57 | #if COMPILER(MSVC) | |
58 | // Move the arguments into registers. | |
59 | peek(input, 3); | |
60 | peek(index, 4); | |
61 | peek(length, 5); | |
62 | peek(output, 6); | |
63 | #else | |
64 | // On gcc the function is regparm(3), so the input, index, and length registers | |
65 | // (eax, edx, and ecx respectively) already contain the appropriate values. | |
66 | // Just load the fourth argument (output) into edi | |
67 | peek(output, 3); | |
68 | #endif | |
69 | #endif | |
70 | ||
71 | #ifndef NDEBUG | |
72 | // ASSERT that the output register is not null. | |
73 | Jump outputNotNull = jnzPtr(output); | |
74 | breakpoint(); | |
75 | outputNotNull.link(this); | |
76 | #endif | |
77 | } | |
78 | ||
79 | void Generator::generateReturnSuccess() | |
80 | { | |
81 | // Set return value. | |
82 | pop(X86::eax); // match begin | |
83 | store32(X86::eax, output); | |
84 | store32(index, Address(output, 4)); // match end | |
85 | ||
86 | // Restore callee save registers. | |
87 | #if !PLATFORM(X86_64) | |
88 | pop(X86::esi); | |
89 | pop(X86::edi); | |
90 | #endif | |
91 | ret(); | |
92 | } | |
93 | ||
94 | void Generator::generateSaveIndex() | |
95 | { | |
96 | push(index); | |
97 | } | |
98 | ||
99 | void Generator::generateIncrementIndex(Jump* failure) | |
100 | { | |
101 | peek(index); | |
102 | if (failure) | |
103 | *failure = je32(length, index); | |
104 | add32(Imm32(1), index); | |
105 | poke(index); | |
106 | } | |
107 | ||
108 | void Generator::generateLoadCharacter(JumpList& failures) | |
109 | { | |
110 | failures.append(je32(length, index)); | |
111 | load16(BaseIndex(input, index, TimesTwo), character); | |
112 | } | |
113 | ||
114 | // For the sake of end-of-line assertions, we treat one-past-the-end as if it | |
115 | // were part of the input string. | |
116 | void Generator::generateJumpIfNotEndOfInput(Label target) | |
117 | { | |
118 | jle32(index, length, target); | |
119 | } | |
120 | ||
121 | void Generator::generateReturnFailure() | |
122 | { | |
123 | pop(); | |
124 | move(Imm32(-1), X86::eax); | |
125 | #if !PLATFORM(X86_64) | |
126 | pop(X86::esi); | |
127 | pop(X86::edi); | |
128 | #endif | |
129 | ret(); | |
130 | } | |
131 | ||
132 | void Generator::generateBacktrack1() | |
133 | { | |
134 | sub32(Imm32(1), index); | |
135 | } | |
136 | ||
137 | void Generator::generateBacktrackBackreference(unsigned subpatternId) | |
138 | { | |
139 | sub32(Address(output, (2 * subpatternId + 1) * sizeof(int)), index); | |
140 | add32(Address(output, (2 * subpatternId) * sizeof(int)), index); | |
141 | } | |
142 | ||
143 | void Generator::generateBackreferenceQuantifier(JumpList& failures, Quantifier::Type quantifierType, unsigned subpatternId, unsigned min, unsigned max) | |
144 | { | |
145 | GenerateBackreferenceFunctor functor(subpatternId); | |
146 | ||
147 | load32(Address(output, (2 * subpatternId) * sizeof(int)), character); | |
148 | Jump skipIfEmpty = je32(Address(output, ((2 * subpatternId) + 1) * sizeof(int)), character); | |
149 | ||
150 | ASSERT(quantifierType == Quantifier::Greedy || quantifierType == Quantifier::NonGreedy); | |
151 | if (quantifierType == Quantifier::Greedy) | |
152 | generateGreedyQuantifier(failures, functor, min, max); | |
153 | else | |
154 | generateNonGreedyQuantifier(failures, functor, min, max); | |
155 | ||
156 | skipIfEmpty.link(this); | |
157 | } | |
158 | ||
159 | void Generator::generateNonGreedyQuantifier(JumpList& failures, GenerateAtomFunctor& functor, unsigned min, unsigned max) | |
160 | { | |
161 | JumpList atomFailedList; | |
162 | JumpList alternativeFailedList; | |
163 | ||
164 | // (0) Setup: Save, then init repeatCount. | |
165 | push(repeatCount); | |
166 | move(Imm32(0), repeatCount); | |
167 | Jump start = jump(); | |
168 | ||
169 | // (4) Quantifier failed: No more atom reading possible. | |
170 | Label quantifierFailed(this); | |
171 | pop(repeatCount); | |
172 | failures.append(jump()); | |
173 | ||
174 | // (3) Alternative failed: If we can, read another atom, then fall through to (2) to try again. | |
175 | Label alternativeFailed(this); | |
176 | pop(index); | |
177 | if (max != Quantifier::Infinity) | |
178 | je32(repeatCount, Imm32(max), quantifierFailed); | |
179 | ||
180 | // (1) Read an atom. | |
181 | if (min) | |
182 | start.link(this); | |
183 | Label readAtom(this); | |
184 | functor.generateAtom(this, atomFailedList); | |
185 | atomFailedList.linkTo(quantifierFailed, this); | |
186 | add32(Imm32(1), repeatCount); | |
187 | ||
188 | // (2) Keep reading if we're under the minimum. | |
189 | if (min > 1) | |
190 | jl32(repeatCount, Imm32(min), readAtom); | |
191 | ||
192 | // (3) Test the rest of the alternative. | |
193 | if (!min) | |
194 | start.link(this); | |
195 | push(index); | |
196 | m_parser.parseAlternative(alternativeFailedList); | |
197 | alternativeFailedList.linkTo(alternativeFailed, this); | |
198 | ||
199 | pop(); | |
200 | pop(repeatCount); | |
201 | } | |
202 | ||
203 | void Generator::generateGreedyQuantifier(JumpList& failures, GenerateAtomFunctor& functor, unsigned min, unsigned max) | |
204 | { | |
205 | if (!max) | |
206 | return; | |
207 | ||
208 | JumpList doneReadingAtomsList; | |
209 | JumpList alternativeFailedList; | |
210 | ||
211 | // (0) Setup: Save, then init repeatCount. | |
212 | push(repeatCount); | |
213 | move(Imm32(0), repeatCount); | |
214 | ||
215 | // (1) Greedily read as many copies of the atom as possible, then jump to (2). | |
216 | Label readAtom(this); | |
217 | functor.generateAtom(this, doneReadingAtomsList); | |
218 | add32(Imm32(1), repeatCount); | |
219 | if (max == Quantifier::Infinity) | |
220 | jump(readAtom); | |
221 | else if (max == 1) | |
222 | doneReadingAtomsList.append(jump()); | |
223 | else { | |
224 | jne32(repeatCount, Imm32(max), readAtom); | |
225 | doneReadingAtomsList.append(jump()); | |
226 | } | |
227 | ||
228 | // (5) Quantifier failed: No more backtracking possible. | |
229 | Label quantifierFailed(this); | |
230 | pop(repeatCount); | |
231 | failures.append(jump()); | |
232 | ||
233 | // (4) Alternative failed: Backtrack, then fall through to (2) to try again. | |
234 | Label alternativeFailed(this); | |
235 | pop(index); | |
236 | functor.backtrack(this); | |
237 | sub32(Imm32(1), repeatCount); | |
238 | ||
239 | // (2) Verify that we have enough atoms. | |
240 | doneReadingAtomsList.link(this); | |
241 | jl32(repeatCount, Imm32(min), quantifierFailed); | |
242 | ||
243 | // (3) Test the rest of the alternative. | |
244 | push(index); | |
245 | m_parser.parseAlternative(alternativeFailedList); | |
246 | alternativeFailedList.linkTo(alternativeFailed, this); | |
247 | ||
248 | pop(); | |
249 | pop(repeatCount); | |
250 | } | |
251 | ||
252 | void Generator::generatePatternCharacterSequence(JumpList& failures, int* sequence, size_t count) | |
253 | { | |
254 | for (size_t i = 0; i < count;) { | |
255 | if (i < count - 1) { | |
256 | if (generatePatternCharacterPair(failures, sequence[i], sequence[i + 1])) { | |
257 | i += 2; | |
258 | continue; | |
259 | } | |
260 | } | |
261 | ||
262 | generatePatternCharacter(failures, sequence[i]); | |
263 | ++i; | |
264 | } | |
265 | } | |
266 | ||
267 | bool Generator::generatePatternCharacterPair(JumpList& failures, int ch1, int ch2) | |
268 | { | |
269 | if (m_parser.ignoreCase()) { | |
270 | // Non-trivial case folding requires more than one test, so we can't | |
271 | // test as a pair with an adjacent character. | |
272 | if (!isASCII(ch1) && Unicode::toLower(ch1) != Unicode::toUpper(ch1)) | |
273 | return false; | |
274 | if (!isASCII(ch2) && Unicode::toLower(ch2) != Unicode::toUpper(ch2)) | |
275 | return false; | |
276 | } | |
277 | ||
278 | // Optimistically consume 2 characters. | |
279 | add32(Imm32(2), index); | |
280 | failures.append(jg32(index, length)); | |
281 | ||
282 | // Load the characters we just consumed, offset -2 characters from index. | |
283 | load32(BaseIndex(input, index, TimesTwo, -2 * 2), character); | |
284 | ||
285 | if (m_parser.ignoreCase()) { | |
286 | // Convert ASCII alphabet characters to upper case before testing for | |
287 | // equality. (ASCII non-alphabet characters don't require upper-casing | |
288 | // because they have no uppercase equivalents. Unicode characters don't | |
289 | // require upper-casing because we only handle Unicode characters whose | |
290 | // upper and lower cases are equal.) | |
291 | int ch1Mask = 0; | |
292 | if (isASCIIAlpha(ch1)) { | |
293 | ch1 |= 32; | |
294 | ch1Mask = 32; | |
295 | } | |
296 | ||
297 | int ch2Mask = 0; | |
298 | if (isASCIIAlpha(ch2)) { | |
299 | ch2 |= 32; | |
300 | ch2Mask = 32; | |
301 | } | |
302 | ||
303 | int mask = ch1Mask | (ch2Mask << 16); | |
304 | if (mask) | |
305 | or32(Imm32(mask), character); | |
306 | } | |
307 | int pair = ch1 | (ch2 << 16); | |
308 | ||
309 | failures.append(jne32(character, Imm32(pair))); | |
310 | return true; | |
311 | } | |
312 | ||
313 | void Generator::generatePatternCharacter(JumpList& failures, int ch) | |
314 | { | |
315 | generateLoadCharacter(failures); | |
316 | ||
317 | // used for unicode case insensitive | |
318 | bool hasUpper = false; | |
319 | Jump isUpper; | |
320 | ||
321 | // if case insensitive match | |
322 | if (m_parser.ignoreCase()) { | |
323 | UChar lower, upper; | |
324 | ||
325 | // check for ascii case sensitive characters | |
326 | if (isASCIIAlpha(ch)) { | |
327 | or32(Imm32(32), character); | |
328 | ch |= 32; | |
329 | } else if (!isASCII(ch) && ((lower = Unicode::toLower(ch)) != (upper = Unicode::toUpper(ch)))) { | |
330 | // handle unicode case sentitive characters - branch to success on upper | |
331 | isUpper = je32(character, Imm32(upper)); | |
332 | hasUpper = true; | |
333 | ch = lower; | |
334 | } | |
335 | } | |
336 | ||
337 | // checks for ch, or lower case version of ch, if insensitive | |
338 | failures.append(jne32(character, Imm32((unsigned short)ch))); | |
339 | ||
340 | if (m_parser.ignoreCase() && hasUpper) { | |
341 | // for unicode case insensitive matches, branch here if upper matches. | |
342 | isUpper.link(this); | |
343 | } | |
344 | ||
345 | // on success consume the char | |
346 | add32(Imm32(1), index); | |
347 | } | |
348 | ||
349 | void Generator::generateCharacterClassInvertedRange(JumpList& failures, JumpList& matchDest, const CharacterRange* ranges, unsigned count, unsigned* matchIndex, const UChar* matches, unsigned matchCount) | |
350 | { | |
351 | do { | |
352 | // pick which range we're going to generate | |
353 | int which = count >> 1; | |
354 | char lo = ranges[which].begin; | |
355 | char hi = ranges[which].end; | |
356 | ||
357 | // check if there are any ranges or matches below lo. If not, just jl to failure - | |
358 | // if there is anything else to check, check that first, if it falls through jmp to failure. | |
359 | if ((*matchIndex < matchCount) && (matches[*matchIndex] < lo)) { | |
360 | Jump loOrAbove = jge32(character, Imm32((unsigned short)lo)); | |
361 | ||
362 | // generate code for all ranges before this one | |
363 | if (which) | |
364 | generateCharacterClassInvertedRange(failures, matchDest, ranges, which, matchIndex, matches, matchCount); | |
365 | ||
366 | while ((*matchIndex < matchCount) && (matches[*matchIndex] < lo)) { | |
367 | matchDest.append(je32(character, Imm32((unsigned short)matches[*matchIndex]))); | |
368 | ++*matchIndex; | |
369 | } | |
370 | failures.append(jump()); | |
371 | ||
372 | loOrAbove.link(this); | |
373 | } else if (which) { | |
374 | Jump loOrAbove = jge32(character, Imm32((unsigned short)lo)); | |
375 | ||
376 | generateCharacterClassInvertedRange(failures, matchDest, ranges, which, matchIndex, matches, matchCount); | |
377 | failures.append(jump()); | |
378 | ||
379 | loOrAbove.link(this); | |
380 | } else | |
381 | failures.append(jl32(character, Imm32((unsigned short)lo))); | |
382 | ||
383 | while ((*matchIndex < matchCount) && (matches[*matchIndex] <= hi)) | |
384 | ++*matchIndex; | |
385 | ||
386 | matchDest.append(jle32(character, Imm32((unsigned short)hi))); | |
387 | // fall through to here, the value is above hi. | |
388 | ||
389 | // shuffle along & loop around if there are any more matches to handle. | |
390 | unsigned next = which + 1; | |
391 | ranges += next; | |
392 | count -= next; | |
393 | } while (count); | |
394 | } | |
395 | ||
396 | void Generator::generateCharacterClassInverted(JumpList& matchDest, const CharacterClass& charClass) | |
397 | { | |
398 | Jump unicodeFail; | |
399 | if (charClass.numMatchesUnicode || charClass.numRangesUnicode) { | |
400 | Jump isAscii = jle32(character, Imm32(0x7f)); | |
401 | ||
402 | if (charClass.numMatchesUnicode) { | |
403 | for (unsigned i = 0; i < charClass.numMatchesUnicode; ++i) { | |
404 | UChar ch = charClass.matchesUnicode[i]; | |
405 | matchDest.append(je32(character, Imm32(ch))); | |
406 | } | |
407 | } | |
408 | ||
409 | if (charClass.numRangesUnicode) { | |
410 | for (unsigned i = 0; i < charClass.numRangesUnicode; ++i) { | |
411 | UChar lo = charClass.rangesUnicode[i].begin; | |
412 | UChar hi = charClass.rangesUnicode[i].end; | |
413 | ||
414 | Jump below = jl32(character, Imm32(lo)); | |
415 | matchDest.append(jle32(character, Imm32(hi))); | |
416 | below.link(this); | |
417 | } | |
418 | } | |
419 | ||
420 | unicodeFail = jump(); | |
421 | isAscii.link(this); | |
422 | } | |
423 | ||
424 | if (charClass.numRanges) { | |
425 | unsigned matchIndex = 0; | |
426 | JumpList failures; | |
427 | generateCharacterClassInvertedRange(failures, matchDest, charClass.ranges, charClass.numRanges, &matchIndex, charClass.matches, charClass.numMatches); | |
428 | while (matchIndex < charClass.numMatches) | |
429 | matchDest.append(je32(character, Imm32((unsigned short)charClass.matches[matchIndex++]))); | |
430 | ||
431 | failures.link(this); | |
432 | } else if (charClass.numMatches) { | |
433 | // optimization: gather 'a','A' etc back together, can mask & test once. | |
434 | Vector<char> matchesAZaz; | |
435 | ||
436 | for (unsigned i = 0; i < charClass.numMatches; ++i) { | |
437 | char ch = charClass.matches[i]; | |
438 | if (m_parser.ignoreCase()) { | |
439 | if (isASCIILower(ch)) { | |
440 | matchesAZaz.append(ch); | |
441 | continue; | |
442 | } | |
443 | if (isASCIIUpper(ch)) | |
444 | continue; | |
445 | } | |
446 | matchDest.append(je32(character, Imm32((unsigned short)ch))); | |
447 | } | |
448 | ||
449 | if (unsigned countAZaz = matchesAZaz.size()) { | |
450 | or32(Imm32(32), character); | |
451 | for (unsigned i = 0; i < countAZaz; ++i) | |
452 | matchDest.append(je32(character, Imm32(matchesAZaz[i]))); | |
453 | } | |
454 | } | |
455 | ||
456 | if (charClass.numMatchesUnicode || charClass.numRangesUnicode) | |
457 | unicodeFail.link(this); | |
458 | } | |
459 | ||
460 | void Generator::generateCharacterClass(JumpList& failures, const CharacterClass& charClass, bool invert) | |
461 | { | |
462 | generateLoadCharacter(failures); | |
463 | ||
464 | if (invert) | |
465 | generateCharacterClassInverted(failures, charClass); | |
466 | else { | |
467 | JumpList successes; | |
468 | generateCharacterClassInverted(successes, charClass); | |
469 | failures.append(jump()); | |
470 | successes.link(this); | |
471 | } | |
472 | ||
473 | add32(Imm32(1), index); | |
474 | } | |
475 | ||
476 | void Generator::generateParenthesesAssertion(JumpList& failures) | |
477 | { | |
478 | JumpList disjunctionFailed; | |
479 | ||
480 | push(index); | |
481 | m_parser.parseDisjunction(disjunctionFailed); | |
482 | Jump success = jump(); | |
483 | ||
484 | disjunctionFailed.link(this); | |
485 | pop(index); | |
486 | failures.append(jump()); | |
487 | ||
488 | success.link(this); | |
489 | pop(index); | |
490 | } | |
491 | ||
492 | void Generator::generateParenthesesInvertedAssertion(JumpList& failures) | |
493 | { | |
494 | JumpList disjunctionFailed; | |
495 | ||
496 | push(index); | |
497 | m_parser.parseDisjunction(disjunctionFailed); | |
498 | ||
499 | // If the disjunction succeeded, the inverted assertion failed. | |
500 | pop(index); | |
501 | failures.append(jump()); | |
502 | ||
503 | // If the disjunction failed, the inverted assertion succeeded. | |
504 | disjunctionFailed.link(this); | |
505 | pop(index); | |
506 | } | |
507 | ||
508 | void Generator::generateParenthesesNonGreedy(JumpList& failures, Label start, Jump success, Jump fail) | |
509 | { | |
510 | jump(start); | |
511 | success.link(this); | |
512 | failures.append(fail); | |
513 | } | |
514 | ||
515 | Generator::Jump Generator::generateParenthesesResetTrampoline(JumpList& newFailures, unsigned subpatternIdBefore, unsigned subpatternIdAfter) | |
516 | { | |
517 | Jump skip = jump(); | |
518 | newFailures.link(this); | |
519 | for (unsigned i = subpatternIdBefore + 1; i <= subpatternIdAfter; ++i) { | |
520 | store32(Imm32(-1), Address(output, (2 * i) * sizeof(int))); | |
521 | store32(Imm32(-1), Address(output, (2 * i + 1) * sizeof(int))); | |
522 | } | |
523 | ||
524 | Jump newFailJump = jump(); | |
525 | skip.link(this); | |
526 | ||
527 | return newFailJump; | |
528 | } | |
529 | ||
530 | void Generator::generateAssertionBOL(JumpList& failures) | |
531 | { | |
532 | if (m_parser.multiline()) { | |
533 | JumpList previousIsNewline; | |
534 | ||
535 | // begin of input == success | |
536 | previousIsNewline.append(je32(index, Imm32(0))); | |
537 | ||
538 | // now check prev char against newline characters. | |
539 | load16(BaseIndex(input, index, TimesTwo, -2), character); | |
540 | generateCharacterClassInverted(previousIsNewline, CharacterClass::newline()); | |
541 | ||
542 | failures.append(jump()); | |
543 | ||
544 | previousIsNewline.link(this); | |
545 | } else | |
546 | failures.append(jne32(index, Imm32(0))); | |
547 | } | |
548 | ||
549 | void Generator::generateAssertionEOL(JumpList& failures) | |
550 | { | |
551 | if (m_parser.multiline()) { | |
552 | JumpList nextIsNewline; | |
553 | ||
554 | generateLoadCharacter(nextIsNewline); // end of input == success | |
555 | generateCharacterClassInverted(nextIsNewline, CharacterClass::newline()); | |
556 | failures.append(jump()); | |
557 | nextIsNewline.link(this); | |
558 | } else { | |
559 | failures.append(jne32(length, index)); | |
560 | } | |
561 | } | |
562 | ||
563 | void Generator::generateAssertionWordBoundary(JumpList& failures, bool invert) | |
564 | { | |
565 | JumpList wordBoundary; | |
566 | JumpList notWordBoundary; | |
567 | ||
568 | // (1) Check if the previous value was a word char | |
569 | ||
570 | // (1.1) check for begin of input | |
571 | Jump atBegin = je32(index, Imm32(0)); | |
572 | // (1.2) load the last char, and chck if is word character | |
573 | load16(BaseIndex(input, index, TimesTwo, -2), character); | |
574 | JumpList previousIsWord; | |
575 | generateCharacterClassInverted(previousIsWord, CharacterClass::wordchar()); | |
576 | // (1.3) if we get here, previous is not a word char | |
577 | atBegin.link(this); | |
578 | ||
579 | // (2) Handle situation where previous was NOT a \w | |
580 | ||
581 | generateLoadCharacter(notWordBoundary); | |
582 | generateCharacterClassInverted(wordBoundary, CharacterClass::wordchar()); | |
583 | // (2.1) If we get here, neither chars are word chars | |
584 | notWordBoundary.append(jump()); | |
585 | ||
586 | // (3) Handle situation where previous was a \w | |
587 | ||
588 | // (3.0) link success in first match to here | |
589 | previousIsWord.link(this); | |
590 | generateLoadCharacter(wordBoundary); | |
591 | generateCharacterClassInverted(notWordBoundary, CharacterClass::wordchar()); | |
592 | // (3.1) If we get here, this is an end of a word, within the input. | |
593 | ||
594 | // (4) Link everything up | |
595 | ||
596 | if (invert) { | |
597 | // handle the fall through case | |
598 | wordBoundary.append(jump()); | |
599 | ||
600 | // looking for non word boundaries, so link boundary fails to here. | |
601 | notWordBoundary.link(this); | |
602 | ||
603 | failures.append(wordBoundary); | |
604 | } else { | |
605 | // looking for word boundaries, so link successes here. | |
606 | wordBoundary.link(this); | |
607 | ||
608 | failures.append(notWordBoundary); | |
609 | } | |
610 | } | |
611 | ||
612 | void Generator::generateBackreference(JumpList& failures, unsigned subpatternId) | |
613 | { | |
614 | push(index); | |
615 | push(repeatCount); | |
616 | ||
617 | // get the start pos of the backref into repeatCount (multipurpose!) | |
618 | load32(Address(output, (2 * subpatternId) * sizeof(int)), repeatCount); | |
619 | ||
620 | Jump skipIncrement = jump(); | |
621 | Label topOfLoop(this); | |
622 | ||
623 | add32(Imm32(1), index); | |
624 | add32(Imm32(1), repeatCount); | |
625 | skipIncrement.link(this); | |
626 | ||
627 | // check if we're at the end of backref (if we are, success!) | |
628 | Jump endOfBackRef = je32(Address(output, ((2 * subpatternId) + 1) * sizeof(int)), repeatCount); | |
629 | ||
630 | load16(BaseIndex(input, repeatCount, MacroAssembler::TimesTwo), character); | |
631 | ||
632 | // check if we've run out of input (this would be a can o'fail) | |
633 | Jump endOfInput = je32(length, index); | |
634 | ||
635 | je16(character, BaseIndex(input, index, TimesTwo), topOfLoop); | |
636 | ||
637 | endOfInput.link(this); | |
638 | ||
639 | // Failure | |
640 | pop(repeatCount); | |
641 | pop(index); | |
642 | failures.append(jump()); | |
643 | ||
644 | // Success | |
645 | endOfBackRef.link(this); | |
646 | pop(repeatCount); | |
647 | pop(); | |
648 | } | |
649 | ||
650 | void Generator::terminateAlternative(JumpList& successes, JumpList& failures) | |
651 | { | |
652 | successes.append(jump()); | |
653 | ||
654 | failures.link(this); | |
655 | peek(index); | |
656 | } | |
657 | ||
658 | void Generator::terminateDisjunction(JumpList& successes) | |
659 | { | |
660 | successes.link(this); | |
661 | } | |
662 | ||
663 | } } // namespace JSC::WREC | |
664 | ||
665 | #endif // ENABLE(WREC) |