Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(459)

Side by Side Diff: src/jsregexp.cc

Issue 155085: Separate native and interpreted regexp by compile time flag, not runtime. (Closed)
Patch Set: Addressed review comments. Adapted builds scripts. Created 11 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/jsregexp.h ('k') | src/messages.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2006-2009 the V8 project authors. All rights reserved. 1 // Copyright 2006-2009 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after
256 NoHandleAllocation no_handles; 256 NoHandleAllocation no_handles;
257 FixedArray* array = last_match_info->elements(); 257 FixedArray* array = last_match_info->elements();
258 SetAtomLastCapture(array, *subject, value, value + needle->length()); 258 SetAtomLastCapture(array, *subject, value, value + needle->length());
259 } 259 }
260 return last_match_info; 260 return last_match_info;
261 } 261 }
262 262
263 263
264 // Irregexp implementation. 264 // Irregexp implementation.
265 265
266
267 // Ensures that the regexp object contains a compiled version of the 266 // Ensures that the regexp object contains a compiled version of the
268 // source for either ASCII or non-ASCII strings. 267 // source for either ASCII or non-ASCII strings.
269 // If the compiled version doesn't already exist, it is compiled 268 // If the compiled version doesn't already exist, it is compiled
270 // from the source pattern. 269 // from the source pattern.
271 // If compilation fails, an exception is thrown and this function 270 // If compilation fails, an exception is thrown and this function
272 // returns false. 271 // returns false.
273 bool RegExpImpl::EnsureCompiledIrregexp(Handle<JSRegExp> re, bool is_ascii) { 272 bool RegExpImpl::EnsureCompiledIrregexp(Handle<JSRegExp> re, bool is_ascii) {
274 int index; 273 #ifdef V8_NATIVE_REGEXP
275 if (is_ascii) { 274 if (re->DataAt(JSRegExp::code_index(is_ascii))->IsCode()) return true;
276 index = JSRegExp::kIrregexpASCIICodeIndex; 275 #else // ! V8_NATIVE_REGEXP (RegExp interpreter code)
277 } else { 276 if (re->DataAt(JSRegExp::code_index(is_ascii))->IsByteArray()) return true;
278 index = JSRegExp::kIrregexpUC16CodeIndex; 277 #endif
279 } 278 return CompileIrregexp(re, is_ascii);
280 Object* entry = re->DataAt(index); 279 }
281 if (!entry->IsTheHole()) {
282 // A value has already been compiled.
283 if (entry->IsJSObject()) {
284 // If it's a JS value, it's an error.
285 Top::Throw(entry);
286 return false;
287 }
288 return true;
289 }
290 280
281
282 bool RegExpImpl::CompileIrregexp(Handle<JSRegExp> re, bool is_ascii) {
291 // Compile the RegExp. 283 // Compile the RegExp.
292 CompilationZoneScope zone_scope(DELETE_ON_EXIT); 284 CompilationZoneScope zone_scope(DELETE_ON_EXIT);
285 Object* entry = re->DataAt(JSRegExp::code_index(is_ascii));
286 if (entry->IsJSObject()) {
287 // If it's a JSObject, a previous compilation failed and threw this object.
288 // Re-throw the object without trying again.
289 Top::Throw(entry);
290 return false;
291 }
292 ASSERT(entry->IsTheHole());
293 293
294 JSRegExp::Flags flags = re->GetFlags(); 294 JSRegExp::Flags flags = re->GetFlags();
295 295
296 Handle<String> pattern(re->Pattern()); 296 Handle<String> pattern(re->Pattern());
297 if (!pattern->IsFlat()) { 297 if (!pattern->IsFlat()) {
298 FlattenString(pattern); 298 FlattenString(pattern);
299 } 299 }
300 300
301 RegExpCompileData compile_data; 301 RegExpCompileData compile_data;
302 FlatStringReader reader(pattern); 302 FlatStringReader reader(pattern);
303 if (!ParseRegExp(&reader, flags.is_multiline(), &compile_data)) { 303 if (!ParseRegExp(&reader, flags.is_multiline(), &compile_data)) {
304 // Throw an exception if we fail to parse the pattern. 304 // Throw an exception if we fail to parse the pattern.
305 // THIS SHOULD NOT HAPPEN. We already parsed it successfully once. 305 // THIS SHOULD NOT HAPPEN. We already pre-parsed it successfully once.
306 ThrowRegExpException(re, 306 ThrowRegExpException(re,
307 pattern, 307 pattern,
308 compile_data.error, 308 compile_data.error,
309 "malformed_regexp"); 309 "malformed_regexp");
310 return false; 310 return false;
311 } 311 }
312 RegExpEngine::CompilationResult result = 312 RegExpEngine::CompilationResult result =
313 RegExpEngine::Compile(&compile_data, 313 RegExpEngine::Compile(&compile_data,
314 flags.is_ignore_case(), 314 flags.is_ignore_case(),
315 flags.is_multiline(), 315 flags.is_multiline(),
316 pattern, 316 pattern,
317 is_ascii); 317 is_ascii);
318 if (result.error_message != NULL) { 318 if (result.error_message != NULL) {
319 // Unable to compile regexp. 319 // Unable to compile regexp.
320 Handle<JSArray> array = Factory::NewJSArray(2); 320 Handle<JSArray> array = Factory::NewJSArray(2);
321 SetElement(array, 0, pattern); 321 SetElement(array, 0, pattern);
322 SetElement(array, 322 SetElement(array,
323 1, 323 1,
324 Factory::NewStringFromUtf8(CStrVector(result.error_message))); 324 Factory::NewStringFromUtf8(CStrVector(result.error_message)));
325 Handle<Object> regexp_err = 325 Handle<Object> regexp_err =
326 Factory::NewSyntaxError("malformed_regexp", array); 326 Factory::NewSyntaxError("malformed_regexp", array);
327 Top::Throw(*regexp_err); 327 Top::Throw(*regexp_err);
328 re->SetDataAt(index, *regexp_err); 328 re->SetDataAt(JSRegExp::code_index(is_ascii), *regexp_err);
329 return false; 329 return false;
330 } 330 }
331 331
332 NoHandleAllocation no_handles; 332 Handle<FixedArray> data = Handle<FixedArray>(FixedArray::cast(re->data()));
333 333 data->set(JSRegExp::code_index(is_ascii), result.code);
334 FixedArray* data = FixedArray::cast(re->data()); 334 int register_max = IrregexpMaxRegisterCount(*data);
335 data->set(index, result.code);
336 int register_max = IrregexpMaxRegisterCount(data);
337 if (result.num_registers > register_max) { 335 if (result.num_registers > register_max) {
338 SetIrregexpMaxRegisterCount(data, result.num_registers); 336 SetIrregexpMaxRegisterCount(*data, result.num_registers);
339 } 337 }
340 338
341 return true; 339 return true;
342 } 340 }
343 341
344 342
345 int RegExpImpl::IrregexpMaxRegisterCount(FixedArray* re) { 343 int RegExpImpl::IrregexpMaxRegisterCount(FixedArray* re) {
346 return Smi::cast( 344 return Smi::cast(
347 re->get(JSRegExp::kIrregexpMaxRegisterCountIndex))->value(); 345 re->get(JSRegExp::kIrregexpMaxRegisterCountIndex))->value();
348 } 346 }
349 347
350 348
351 void RegExpImpl::SetIrregexpMaxRegisterCount(FixedArray* re, int value) { 349 void RegExpImpl::SetIrregexpMaxRegisterCount(FixedArray* re, int value) {
352 re->set(JSRegExp::kIrregexpMaxRegisterCountIndex, Smi::FromInt(value)); 350 re->set(JSRegExp::kIrregexpMaxRegisterCountIndex, Smi::FromInt(value));
353 } 351 }
354 352
355 353
356 int RegExpImpl::IrregexpNumberOfCaptures(FixedArray* re) { 354 int RegExpImpl::IrregexpNumberOfCaptures(FixedArray* re) {
357 return Smi::cast(re->get(JSRegExp::kIrregexpCaptureCountIndex))->value(); 355 return Smi::cast(re->get(JSRegExp::kIrregexpCaptureCountIndex))->value();
358 } 356 }
359 357
360 358
361 int RegExpImpl::IrregexpNumberOfRegisters(FixedArray* re) { 359 int RegExpImpl::IrregexpNumberOfRegisters(FixedArray* re) {
362 return Smi::cast(re->get(JSRegExp::kIrregexpMaxRegisterCountIndex))->value(); 360 return Smi::cast(re->get(JSRegExp::kIrregexpMaxRegisterCountIndex))->value();
363 } 361 }
364 362
365 363
366 ByteArray* RegExpImpl::IrregexpByteCode(FixedArray* re, bool is_ascii) { 364 ByteArray* RegExpImpl::IrregexpByteCode(FixedArray* re, bool is_ascii) {
367 int index; 365 return ByteArray::cast(re->get(JSRegExp::code_index(is_ascii)));
368 if (is_ascii) {
369 index = JSRegExp::kIrregexpASCIICodeIndex;
370 } else {
371 index = JSRegExp::kIrregexpUC16CodeIndex;
372 }
373 return ByteArray::cast(re->get(index));
374 } 366 }
375 367
376 368
377 Code* RegExpImpl::IrregexpNativeCode(FixedArray* re, bool is_ascii) { 369 Code* RegExpImpl::IrregexpNativeCode(FixedArray* re, bool is_ascii) {
378 int index; 370 return Code::cast(re->get(JSRegExp::code_index(is_ascii)));
379 if (is_ascii) {
380 index = JSRegExp::kIrregexpASCIICodeIndex;
381 } else {
382 index = JSRegExp::kIrregexpUC16CodeIndex;
383 }
384 return Code::cast(re->get(index));
385 } 371 }
386 372
387 373
388 void RegExpImpl::IrregexpPrepare(Handle<JSRegExp> re, 374 void RegExpImpl::IrregexpPrepare(Handle<JSRegExp> re,
389 Handle<String> pattern, 375 Handle<String> pattern,
390 JSRegExp::Flags flags, 376 JSRegExp::Flags flags,
391 int capture_count) { 377 int capture_count) {
392 // Initialize compiled code entries to null. 378 // Initialize compiled code entries to null.
393 Factory::SetRegExpIrregexpData(re, 379 Factory::SetRegExpIrregexpData(re,
394 JSRegExp::IRREGEXP, 380 JSRegExp::IRREGEXP,
395 pattern, 381 pattern,
396 flags, 382 flags,
397 capture_count); 383 capture_count);
398 } 384 }
399 385
400 386
401 Handle<Object> RegExpImpl::IrregexpExec(Handle<JSRegExp> jsregexp, 387 Handle<Object> RegExpImpl::IrregexpExec(Handle<JSRegExp> jsregexp,
402 Handle<String> subject, 388 Handle<String> subject,
403 int previous_index, 389 int previous_index,
404 Handle<JSArray> last_match_info) { 390 Handle<JSArray> last_match_info) {
405 ASSERT_EQ(jsregexp->TypeTag(), JSRegExp::IRREGEXP); 391 ASSERT_EQ(jsregexp->TypeTag(), JSRegExp::IRREGEXP);
406 392
407 // Prepare space for the return values. 393 // Prepare space for the return values.
408 int number_of_capture_registers = 394 int number_of_capture_registers =
409 (IrregexpNumberOfCaptures(FixedArray::cast(jsregexp->data())) + 1) * 2; 395 (IrregexpNumberOfCaptures(FixedArray::cast(jsregexp->data())) + 1) * 2;
410 396
397 #ifndef V8_NATIVE_REGEXP
411 #ifdef DEBUG 398 #ifdef DEBUG
412 if (FLAG_trace_regexp_bytecodes) { 399 if (FLAG_trace_regexp_bytecodes) {
413 String* pattern = jsregexp->Pattern(); 400 String* pattern = jsregexp->Pattern();
414 PrintF("\n\nRegexp match: /%s/\n\n", *(pattern->ToCString())); 401 PrintF("\n\nRegexp match: /%s/\n\n", *(pattern->ToCString()));
415 PrintF("\n\nSubject string: '%s'\n\n", *(subject->ToCString())); 402 PrintF("\n\nSubject string: '%s'\n\n", *(subject->ToCString()));
416 } 403 }
417 #endif 404 #endif
405 #endif
418 406
419 if (!subject->IsFlat()) { 407 if (!subject->IsFlat()) {
420 FlattenString(subject); 408 FlattenString(subject);
421 } 409 }
422 410
423 last_match_info->EnsureSize(number_of_capture_registers + kLastMatchOverhead); 411 last_match_info->EnsureSize(number_of_capture_registers + kLastMatchOverhead);
424 412
425 bool rc; 413 Handle<FixedArray> array;
426 // We have to initialize this with something to make gcc happy but we can't
427 // initialize it with its real value until after the GC-causing things are
428 // over.
429 FixedArray* array = NULL;
430 414
431 // Dispatch to the correct RegExp implementation. 415 // Dispatch to the correct RegExp implementation.
432 Handle<String> original_subject = subject;
433 Handle<FixedArray> regexp(FixedArray::cast(jsregexp->data())); 416 Handle<FixedArray> regexp(FixedArray::cast(jsregexp->data()));
434 if (UseNativeRegexp()) { 417 #ifdef V8_NATIVE_REGEXP
435 #if V8_TARGET_ARCH_IA32 418 #if V8_TARGET_ARCH_IA32
436 OffsetsVector captures(number_of_capture_registers); 419 OffsetsVector captures(number_of_capture_registers);
437 int* captures_vector = captures.vector(); 420 int* captures_vector = captures.vector();
438 RegExpMacroAssemblerIA32::Result res; 421 RegExpMacroAssemblerIA32::Result res;
439 do { 422 do {
440 bool is_ascii = subject->IsAsciiRepresentation();
441 if (!EnsureCompiledIrregexp(jsregexp, is_ascii)) {
442 return Handle<Object>::null();
443 }
444 Handle<Code> code(RegExpImpl::IrregexpNativeCode(*regexp, is_ascii));
445 res = RegExpMacroAssemblerIA32::Match(code,
446 subject,
447 captures_vector,
448 captures.length(),
449 previous_index);
450 // If result is RETRY, the string have changed representation, and we
451 // must restart from scratch.
452 } while (res == RegExpMacroAssemblerIA32::RETRY);
453 if (res == RegExpMacroAssemblerIA32::EXCEPTION) {
454 ASSERT(Top::has_pending_exception());
455 return Handle<Object>::null();
456 }
457 ASSERT(res == RegExpMacroAssemblerIA32::SUCCESS
458 || res == RegExpMacroAssemblerIA32::FAILURE);
459
460 rc = (res == RegExpMacroAssemblerIA32::SUCCESS);
461 if (!rc) return Factory::null_value();
462
463 array = last_match_info->elements();
464 ASSERT(array->length() >= number_of_capture_registers + kLastMatchOverhead);
465 // The captures come in (start, end+1) pairs.
466 for (int i = 0; i < number_of_capture_registers; i += 2) {
467 SetCapture(array, i, captures_vector[i]);
468 SetCapture(array, i + 1, captures_vector[i + 1]);
469 }
470 #else // !V8_TARGET_ARCH_IA32
471 UNREACHABLE();
472 #endif
473 } else {
474 bool is_ascii = subject->IsAsciiRepresentation(); 423 bool is_ascii = subject->IsAsciiRepresentation();
475 if (!EnsureCompiledIrregexp(jsregexp, is_ascii)) { 424 if (!EnsureCompiledIrregexp(jsregexp, is_ascii)) {
476 return Handle<Object>::null(); 425 return Handle<Object>::null();
477 } 426 }
478 // Now that we have done EnsureCompiledIrregexp we can get the number of 427 Handle<Code> code(RegExpImpl::IrregexpNativeCode(*regexp, is_ascii));
479 // registers. 428 res = RegExpMacroAssemblerIA32::Match(code,
480 int number_of_registers = 429 subject,
481 IrregexpNumberOfRegisters(FixedArray::cast(jsregexp->data())); 430 captures_vector,
482 OffsetsVector registers(number_of_registers); 431 captures.length(),
483 int* register_vector = registers.vector(); 432 previous_index);
484 for (int i = number_of_capture_registers - 1; i >= 0; i--) { 433 // If result is RETRY, the string have changed representation, and we
485 register_vector[i] = -1; 434 // must restart from scratch.
486 } 435 } while (res == RegExpMacroAssemblerIA32::RETRY);
487 Handle<ByteArray> byte_codes(IrregexpByteCode(*regexp, is_ascii)); 436 if (res == RegExpMacroAssemblerIA32::EXCEPTION) {
437 ASSERT(Top::has_pending_exception());
438 return Handle<Object>::null();
439 }
440 ASSERT(res == RegExpMacroAssemblerIA32::SUCCESS
441 || res == RegExpMacroAssemblerIA32::FAILURE);
488 442
489 rc = IrregexpInterpreter::Match(byte_codes, 443 if (res != RegExpMacroAssemblerIA32::SUCCESS) return Factory::null_value();
490 subject,
491 register_vector,
492 previous_index);
493 if (!rc) return Factory::null_value();
494 444
495 array = last_match_info->elements(); 445 array = Handle<FixedArray>(last_match_info->elements());
496 ASSERT(array->length() >= number_of_capture_registers + kLastMatchOverhead); 446 ASSERT(array->length() >= number_of_capture_registers + kLastMatchOverhead);
497 // The captures come in (start, end+1) pairs. 447 // The captures come in (start, end+1) pairs.
498 for (int i = 0; i < number_of_capture_registers; i += 2) { 448 for (int i = 0; i < number_of_capture_registers; i += 2) {
499 SetCapture(array, i, register_vector[i]); 449 SetCapture(*array, i, captures_vector[i]);
500 SetCapture(array, i + 1, register_vector[i + 1]); 450 SetCapture(*array, i + 1, captures_vector[i + 1]);
501 } 451 }
452 #else // !V8_TARGET_ARCH_IA32
453 UNREACHABLE();
454 #endif // V8_TARGET_ARCH_IA32
455 #else // !V8_NATIVE_REGEXP
456 bool is_ascii = subject->IsAsciiRepresentation();
457 if (!EnsureCompiledIrregexp(jsregexp, is_ascii)) {
458 return Handle<Object>::null();
459 }
460 // Now that we have done EnsureCompiledIrregexp we can get the number of
461 // registers.
462 int number_of_registers =
463 IrregexpNumberOfRegisters(FixedArray::cast(jsregexp->data()));
464 OffsetsVector registers(number_of_registers);
465 int* register_vector = registers.vector();
466 for (int i = number_of_capture_registers - 1; i >= 0; i--) {
467 register_vector[i] = -1;
468 }
469 Handle<ByteArray> byte_codes(IrregexpByteCode(*regexp, is_ascii));
470
471 if (!IrregexpInterpreter::Match(byte_codes,
472 subject,
473 register_vector,
474 previous_index)) {
475 return Factory::null_value();
502 } 476 }
503 477
504 SetLastCaptureCount(array, number_of_capture_registers); 478 array = Handle<FixedArray>(last_match_info->elements());
505 SetLastSubject(array, *original_subject); 479 ASSERT(array->length() >= number_of_capture_registers + kLastMatchOverhead);
506 SetLastInput(array, *original_subject); 480 // The captures come in (start, end+1) pairs.
481 for (int i = 0; i < number_of_capture_registers; i += 2) {
482 SetCapture(*array, i, register_vector[i]);
483 SetCapture(*array, i + 1, register_vector[i + 1]);
484 }
485 #endif // V8_NATIVE_REGEXP
486
487 SetLastCaptureCount(*array, number_of_capture_registers);
488 SetLastSubject(*array, *subject);
489 SetLastInput(*array, *subject);
507 490
508 return last_match_info; 491 return last_match_info;
509 } 492 }
510 493
511 494
512 // ------------------------------------------------------------------- 495 // -------------------------------------------------------------------
513 // Implementation of the Irregexp regular expression engine. 496 // Implementation of the Irregexp regular expression engine.
514 // 497 //
515 // The Irregexp regular expression engine is intended to be a complete 498 // The Irregexp regular expression engine is intended to be a complete
516 // implementation of ECMAScript regular expressions. It generates either 499 // implementation of ECMAScript regular expressions. It generates either
(...skipping 3950 matching lines...) Expand 10 before | Expand all | Expand 10 after
4467 data->node = node; 4450 data->node = node;
4468 Analysis analysis(ignore_case); 4451 Analysis analysis(ignore_case);
4469 analysis.EnsureAnalyzed(node); 4452 analysis.EnsureAnalyzed(node);
4470 if (analysis.has_failed()) { 4453 if (analysis.has_failed()) {
4471 const char* error_message = analysis.error_message(); 4454 const char* error_message = analysis.error_message();
4472 return CompilationResult(error_message); 4455 return CompilationResult(error_message);
4473 } 4456 }
4474 4457
4475 NodeInfo info = *node->info(); 4458 NodeInfo info = *node->info();
4476 4459
4477 if (RegExpImpl::UseNativeRegexp()) { 4460 #ifdef V8_NATIVE_REGEXP
4478 #ifdef V8_TARGET_ARCH_ARM 4461 #ifdef V8_TARGET_ARCH_ARM
4479 UNREACHABLE(); 4462 // ARM native regexp not implemented yet.
4463 UNREACHABLE();
4480 #endif 4464 #endif
4481 #ifdef V8_TARGET_ARCH_X64 4465 #ifdef V8_TARGET_ARCH_X64
4482 UNREACHABLE(); 4466 // X64 native regexp not implemented yet.
4467 UNREACHABLE();
4483 #endif 4468 #endif
4484 #ifdef V8_TARGET_ARCH_IA32 4469 #ifdef V8_TARGET_ARCH_IA32
4485 RegExpMacroAssemblerIA32::Mode mode; 4470 RegExpMacroAssemblerIA32::Mode mode;
4486 if (is_ascii) { 4471 if (is_ascii) {
4487 mode = RegExpMacroAssemblerIA32::ASCII; 4472 mode = RegExpMacroAssemblerIA32::ASCII;
4488 } else { 4473 } else {
4489 mode = RegExpMacroAssemblerIA32::UC16; 4474 mode = RegExpMacroAssemblerIA32::UC16;
4490 } 4475 }
4491 RegExpMacroAssemblerIA32 macro_assembler(mode, 4476 RegExpMacroAssemblerIA32 macro_assembler(mode,
4492 (data->capture_count + 1) * 2); 4477 (data->capture_count + 1) * 2);
4493 return compiler.Assemble(&macro_assembler, 4478 return compiler.Assemble(&macro_assembler,
4494 node, 4479 node,
4495 data->capture_count, 4480 data->capture_count,
4496 pattern); 4481 pattern);
4497 #endif 4482 #endif
4498 } 4483 #else // ! V8_NATIVE_REGEXP
4484 // Interpreted regexp.
4499 EmbeddedVector<byte, 1024> codes; 4485 EmbeddedVector<byte, 1024> codes;
4500 RegExpMacroAssemblerIrregexp macro_assembler(codes); 4486 RegExpMacroAssemblerIrregexp macro_assembler(codes);
4501 return compiler.Assemble(&macro_assembler, 4487 return compiler.Assemble(&macro_assembler,
4502 node, 4488 node,
4503 data->capture_count, 4489 data->capture_count,
4504 pattern); 4490 pattern);
4491 #endif // V8_NATIVE_REGEXP
4505 } 4492 }
4506 4493
4507
4508 }} // namespace v8::internal 4494 }} // namespace v8::internal
OLDNEW
« no previous file with comments | « src/jsregexp.h ('k') | src/messages.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698