From 152163c1646b45f5fc5d31a4ec2eb55d7f4a2ffc Mon Sep 17 00:00:00 2001 From: yangguo Date: Wed, 4 Nov 2015 12:42:17 -0800 Subject: [PATCH] Implement flag and source getters on RegExp.prototype. R=littledan@chromium.org BUG=v8:3715, v8:4528 LOG=Y Committed: https://crrev.com/60e8877e161fe6175e19fafce2d6ed1c3999cdb1 Cr-Commit-Position: refs/heads/master@{#31753} Committed: https://crrev.com/b5c80a31ad266eb38a0cf2ff756be59c66d34aa5 Cr-Commit-Position: refs/heads/master@{#31773} Committed: https://crrev.com/85494e90bb63a3a9e19a1bf862cb6bfcb0162ee9 Cr-Commit-Position: refs/heads/master@{#31782} Review URL: https://codereview.chromium.org/1419823010 Cr-Commit-Position: refs/heads/master@{#31804} --- include/v8.h | 4 +- src/api.cc | 2 + src/bootstrapper.cc | 27 +---- src/heap/heap.h | 7 +- src/js/harmony-regexp.js | 11 +- src/js/macros.py | 14 +++ src/js/prologue.js | 1 + src/js/regexp.js | 103 +++++++++++++------ src/js/string.js | 7 +- src/js/symbol.js | 2 - src/messages.h | 4 +- src/objects.h | 8 +- src/runtime/runtime-regexp.cc | 37 ++----- test/mjsunit/es6/regexp-flags.js | 49 +++++++++ test/mjsunit/mirror-regexp.js | 53 ++++------ test/mjsunit/regexp.js | 30 +++--- test/mjsunit/regress/regress-447561.js | 8 +- test/test262/test262.status | 36 ------- test/webkit/fast/regex/toString-expected.txt | 3 +- tools/js2c.py | 2 +- 20 files changed, 222 insertions(+), 186 deletions(-) create mode 100644 test/mjsunit/es6/regexp-flags.js diff --git a/include/v8.h b/include/v8.h index 20a8b0c276..7a6ca982f2 100644 --- a/include/v8.h +++ b/include/v8.h @@ -4001,7 +4001,9 @@ class V8_EXPORT RegExp : public Object { kNone = 0, kGlobal = 1, kIgnoreCase = 2, - kMultiline = 4 + kMultiline = 4, + kSticky = 8, + kUnicode = 16 }; /** diff --git a/src/api.cc b/src/api.cc index edfa3c020b..27411c19a0 100644 --- a/src/api.cc +++ b/src/api.cc @@ -6147,6 +6147,8 @@ REGEXP_FLAG_ASSERT_EQ(kNone, NONE); REGEXP_FLAG_ASSERT_EQ(kGlobal, GLOBAL); REGEXP_FLAG_ASSERT_EQ(kIgnoreCase, IGNORE_CASE); REGEXP_FLAG_ASSERT_EQ(kMultiline, MULTILINE); +REGEXP_FLAG_ASSERT_EQ(kSticky, STICKY); +REGEXP_FLAG_ASSERT_EQ(kUnicode, UNICODE_ESCAPES); #undef REGEXP_FLAG_ASSERT_EQ v8::RegExp::Flags v8::RegExp::GetFlags() const { diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 79e1a79241..0e0cd78b18 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1228,31 +1228,16 @@ void Genesis::InitializeGlobal(Handle global_object, Map::EnsureDescriptorSlack(initial_map, 5); { - // ECMA-262, section 15.10.7.1. - DataDescriptor field(factory->source_string(), + // ES6 21.2.3.2.1 + DataDescriptor field(factory->regexp_source_symbol(), JSRegExp::kSourceFieldIndex, final, Representation::Tagged()); initial_map->AppendDescriptor(&field); } { - // ECMA-262, section 15.10.7.2. - DataDescriptor field(factory->global_string(), - JSRegExp::kGlobalFieldIndex, final, - Representation::Tagged()); - initial_map->AppendDescriptor(&field); - } - { - // ECMA-262, section 15.10.7.3. - DataDescriptor field(factory->ignore_case_string(), - JSRegExp::kIgnoreCaseFieldIndex, final, - Representation::Tagged()); - initial_map->AppendDescriptor(&field); - } - { - // ECMA-262, section 15.10.7.4. - DataDescriptor field(factory->multiline_string(), - JSRegExp::kMultilineFieldIndex, final, - Representation::Tagged()); + DataDescriptor field(factory->regexp_flags_symbol(), + JSRegExp::kFlagsFieldIndex, final, + Representation::Smi()); initial_map->AppendDescriptor(&field); } { @@ -2055,8 +2040,6 @@ void Bootstrapper::ExportExperimentalFromRuntime(Isolate* isolate, isolate->factory()->ToBoolean(FLAG), NONE); \ } - INITIALIZE_FLAG(FLAG_harmony_regexps) - INITIALIZE_FLAG(FLAG_harmony_unicode_regexps) INITIALIZE_FLAG(FLAG_harmony_tostring) INITIALIZE_FLAG(FLAG_harmony_tolength) diff --git a/src/heap/heap.h b/src/heap/heap.h index d204d6246d..9e205caf81 100644 --- a/src/heap/heap.h +++ b/src/heap/heap.h @@ -249,7 +249,6 @@ namespace internal { V(Generator_string, "Generator") \ V(get_string, "get") \ V(global_string, "global") \ - V(ignore_case_string, "ignoreCase") \ V(illegal_access_string, "illegal access") \ V(illegal_argument_string, "illegal argument") \ V(index_string, "index") \ @@ -268,7 +267,6 @@ namespace internal { V(Map_string, "Map") \ V(minus_infinity_string, "-Infinity") \ V(minus_zero_string, "-0") \ - V(multiline_string, "multiline") \ V(name_string, "name") \ V(nan_string, "NaN") \ V(next_string, "next") \ @@ -288,7 +286,6 @@ namespace internal { V(source_string, "source") \ V(source_url_string, "source_url") \ V(stack_string, "stack") \ - V(sticky_string, "sticky") \ V(strict_compare_ic_string, "===") \ V(string_string, "string") \ V(String_string, "String") \ @@ -305,7 +302,6 @@ namespace internal { V(uint8x16_string, "uint8x16") \ V(Uint8x16_string, "Uint8x16") \ V(undefined_string, "undefined") \ - V(unicode_string, "unicode") \ V(valueOf_string, "valueOf") \ V(value_string, "value") \ V(WeakMap_string, "WeakMap") \ @@ -349,6 +345,8 @@ namespace internal { V(promise_raw_symbol) \ V(promise_status_symbol) \ V(promise_value_symbol) \ + V(regexp_flags_symbol) \ + V(regexp_source_symbol) \ V(sealed_symbol) \ V(stack_trace_symbol) \ V(string_iterator_iterated_string_symbol) \ @@ -357,7 +355,6 @@ namespace internal { #define PUBLIC_SYMBOL_LIST(V) \ V(has_instance_symbol, Symbol.hasInstance) \ - V(is_regexp_symbol, Symbol.isRegExp) \ V(iterator_symbol, Symbol.iterator) \ V(match_symbol, Symbol.match) \ V(replace_symbol, Symbol.replace) \ diff --git a/src/js/harmony-regexp.js b/src/js/harmony-regexp.js index 033ee2c9a5..a02aa7bda4 100644 --- a/src/js/harmony-regexp.js +++ b/src/js/harmony-regexp.js @@ -11,6 +11,7 @@ // ------------------------------------------------------------------- // Imports +var GetRegExpFlagGetter = utils.ImportNow("GetRegExpFlagGetter"); var GlobalRegExp = global.RegExp; var MakeTypeError; @@ -24,7 +25,8 @@ utils.Import(function(from) { // + https://bugs.ecmascript.org/show_bug.cgi?id=3423 function RegExpGetFlags() { if (!IS_SPEC_OBJECT(this)) { - throw MakeTypeError(kFlagsGetterNonObject, TO_STRING(this)); + throw MakeTypeError( + kRegExpNonObject, "RegExp.prototype.flags", TO_STRING(this)); } var result = ''; if (this.global) result += 'g'; @@ -39,4 +41,11 @@ function RegExpGetFlags() { RegExpGetFlags, null, DONT_ENUM); %SetNativeFlag(RegExpGetFlags); +%DefineGetterPropertyUnchecked(GlobalRegExp.prototype, "sticky", + GetRegExpFlagGetter("RegExp.prototype.sticky", REGEXP_STICKY_MASK), + DONT_ENUM); + +%DefineGetterPropertyUnchecked(GlobalRegExp.prototype, "unicode", + GetRegExpFlagGetter("RegExp.prototype.unicode", REGEXP_UNICODE_MASK), + DONT_ENUM); }) diff --git a/src/js/macros.py b/src/js/macros.py index 856158aa89..fecccf3d93 100644 --- a/src/js/macros.py +++ b/src/js/macros.py @@ -178,6 +178,20 @@ python macro CHAR_CODE(str) = ord(str[1]); define REGEXP_NUMBER_OF_CAPTURES = 0; define REGEXP_FIRST_CAPTURE = 3; +# Constants and macros for internal slot access. +define REGEXP_GLOBAL_MASK = 1; +define REGEXP_IGNORE_CASE_MASK = 2; +define REGEXP_MULTILINE_MASK = 4; +define REGEXP_STICKY_MASK = 8; +define REGEXP_UNICODE_MASK = 16; + +macro REGEXP_GLOBAL(regexp) = (regexp[regExpFlagsSymbol] & REGEXP_GLOBAL_MASK); +macro REGEXP_IGNORE_CASE(regexp) = (regexp[regExpFlagsSymbol] & REGEXP_IGNORE_CASE_MASK); +macro REGEXP_MULTILINE(regexp) = (regexp[regExpFlagsSymbol] & REGEXP_MULTILINE_MASK); +macro REGEXP_STICKY(regexp) = (regexp[regExpFlagsSymbol] & REGEXP_STICKY_MASK); +macro REGEXP_UNICODE(regexp) = (regexp[regExpFlagsSymbol] & REGEXP_UNICODE_MASK); +macro REGEXP_SOURCE(regexp) = (regexp[regExpSourceSymbol]); + # We can't put macros in macros so we use constants here. # REGEXP_NUMBER_OF_CAPTURES macro NUMBER_OF_CAPTURES(array) = ((array)[0]); diff --git a/src/js/prologue.js b/src/js/prologue.js index e0cf05f186..ee38bc68bd 100644 --- a/src/js/prologue.js +++ b/src/js/prologue.js @@ -175,6 +175,7 @@ function PostNatives(utils) { "FunctionSourceString", "GetIterator", "GetMethod", + "GetRegExpFlagGetter", "InnerArrayEvery", "InnerArrayFilter", "InnerArrayForEach", diff --git a/src/js/regexp.js b/src/js/regexp.js index 51c8ed0d6e..79e38ed272 100644 --- a/src/js/regexp.js +++ b/src/js/regexp.js @@ -9,18 +9,16 @@ // ------------------------------------------------------------------- // Imports -var FLAG_harmony_regexps; var FLAG_harmony_tolength; -var FLAG_harmony_unicode_regexps; var GlobalObject = global.Object; var GlobalRegExp = global.RegExp; var InternalPackedArray = utils.InternalPackedArray; var MakeTypeError; +var regExpFlagsSymbol = utils.ImportNow("regexp_flags_symbol"); +var regExpSourceSymbol = utils.ImportNow("regexp_source_symbol"); utils.ImportFromExperimental(function(from) { - FLAG_harmony_regexps = from.FLAG_harmony_regexps; FLAG_harmony_tolength = from.FLAG_harmony_tolength; - FLAG_harmony_unicode_regexps = from.FLAG_harmony_unicode_regexps; }); utils.Import(function(from) { @@ -51,14 +49,12 @@ function DoConstructRegExp(object, pattern, flags) { // RegExp : Called as constructor; see ECMA-262, section 15.10.4. if (IS_REGEXP(pattern)) { if (!IS_UNDEFINED(flags)) throw MakeTypeError(kRegExpFlags); - flags = (pattern.global ? 'g' : '') - + (pattern.ignoreCase ? 'i' : '') - + (pattern.multiline ? 'm' : ''); - if (FLAG_harmony_unicode_regexps) - flags += (pattern.unicode ? 'u' : ''); - if (FLAG_harmony_regexps) - flags += (pattern.sticky ? 'y' : ''); - pattern = pattern.source; + flags = (REGEXP_GLOBAL(pattern) ? 'g' : '') + + (REGEXP_IGNORE_CASE(pattern) ? 'i' : '') + + (REGEXP_MULTILINE(pattern) ? 'm' : '') + + (REGEXP_UNICODE(pattern) ? 'u' : '') + + (REGEXP_STICKY(pattern) ? 'y' : ''); + pattern = REGEXP_SOURCE(pattern); } pattern = IS_UNDEFINED(pattern) ? '' : TO_STRING(pattern); @@ -142,9 +138,7 @@ function RegExpExecNoTests(regexp, string, start) { var matchInfo = %_RegExpExec(regexp, string, start, RegExpLastMatchInfo); if (matchInfo !== null) { // ES6 21.2.5.2.2 step 18. - if (FLAG_harmony_regexps && regexp.sticky) { - regexp.lastIndex = matchInfo[CAPTURE1]; - } + if (REGEXP_STICKY(regexp)) regexp.lastIndex = matchInfo[CAPTURE1]; RETURN_NEW_RESULT_FROM_MATCH_INFO(matchInfo, string); } regexp.lastIndex = 0; @@ -165,7 +159,7 @@ function RegExpExecJS(string) { // algorithm, step 4) even if the value is discarded for non-global RegExps. var i = TO_LENGTH_OR_INTEGER(lastIndex); - var updateLastIndex = this.global || (FLAG_harmony_regexps && this.sticky); + var updateLastIndex = REGEXP_GLOBAL(this) || REGEXP_STICKY(this); if (updateLastIndex) { if (i < 0 || i > string.length) { this.lastIndex = 0; @@ -212,7 +206,7 @@ function RegExpTest(string) { // algorithm, step 4) even if the value is discarded for non-global RegExps. var i = TO_LENGTH_OR_INTEGER(lastIndex); - if (this.global || (FLAG_harmony_regexps && this.sticky)) { + if (REGEXP_GLOBAL(this) || REGEXP_STICKY(this)) { if (i < 0 || i > string.length) { this.lastIndex = 0; return false; @@ -231,10 +225,11 @@ function RegExpTest(string) { // checks whether this.source starts with '.*' and that the third char is // not a '?'. But see https://code.google.com/p/v8/issues/detail?id=3560 var regexp = this; - if (regexp.source.length >= 3 && - %_StringCharCodeAt(regexp.source, 0) == 46 && // '.' - %_StringCharCodeAt(regexp.source, 1) == 42 && // '*' - %_StringCharCodeAt(regexp.source, 2) != 63) { // '?' + var source = REGEXP_SOURCE(regexp); + if (regexp.length >= 3 && + %_StringCharCodeAt(regexp, 0) == 46 && // '.' + %_StringCharCodeAt(regexp, 1) == 42 && // '*' + %_StringCharCodeAt(regexp, 2) != 63) { // '?' regexp = TrimRegExp(regexp); } // matchIndices is either null or the RegExpLastMatchInfo array. @@ -251,9 +246,10 @@ function TrimRegExp(regexp) { if (!%_ObjectEquals(regexp_key, regexp)) { regexp_key = regexp; regexp_val = - new GlobalRegExp(%_SubString(regexp.source, 2, regexp.source.length), - (regexp.ignoreCase ? regexp.multiline ? "im" : "i" - : regexp.multiline ? "m" : "")); + new GlobalRegExp( + %_SubString(REGEXP_SOURCE(regexp), 2, REGEXP_SOURCE(regexp).length), + (REGEXP_IGNORE_CASE(regexp) ? REGEXP_MULTILINE(regexp) ? "im" : "i" + : REGEXP_MULTILINE(regexp) ? "m" : "")); } return regexp_val; } @@ -264,12 +260,12 @@ function RegExpToString() { throw MakeTypeError(kIncompatibleMethodReceiver, 'RegExp.prototype.toString', this); } - var result = '/' + this.source + '/'; - if (this.global) result += 'g'; - if (this.ignoreCase) result += 'i'; - if (this.multiline) result += 'm'; - if (FLAG_harmony_unicode_regexps && this.unicode) result += 'u'; - if (FLAG_harmony_regexps && this.sticky) result += 'y'; + var result = '/' + REGEXP_SOURCE(this) + '/'; + if (REGEXP_GLOBAL(this)) result += 'g'; + if (REGEXP_IGNORE_CASE(this)) result += 'i'; + if (REGEXP_MULTILINE(this)) result += 'm'; + if (REGEXP_UNICODE(this)) result += 'u'; + if (REGEXP_STICKY(this)) result += 'y'; return result; } @@ -334,6 +330,40 @@ function RegExpMakeCaptureGetter(n) { }; } + +// ES6 21.2.5.4, 21.2.5.5, 21.2.5.7, 21.2.5.12, 21.2.5.15. +function GetRegExpFlagGetter(name, mask) { + var getter = function() { + if (!IS_SPEC_OBJECT(this)) { + throw MakeTypeError(kRegExpNonObject, name, TO_STRING(this)); + } + var flags = this[regExpFlagsSymbol]; + if (IS_UNDEFINED(flags)) { + throw MakeTypeError(kRegExpNonRegExp, TO_STRING(this)); + } + return !!(flags & mask); + }; + %FunctionSetName(getter, name); + %SetNativeFlag(getter); + return getter; +} + + +// ES6 21.2.5.10. +function RegExpGetSource() { + if (!IS_SPEC_OBJECT(this)) { + throw MakeTypeError(kRegExpNonObject, "RegExp.prototype.source", + TO_STRING(this)); + } + var source = this[regExpSourceSymbol]; + if (IS_UNDEFINED(source)) { + throw MakeTypeError(kRegExpNonRegExp, TO_STRING(this)); + } + return source; +} + +%SetNativeFlag(RegExpGetSource); + // ------------------------------------------------------------------- %FunctionSetInstanceClassName(GlobalRegExp, 'RegExp'); @@ -349,6 +379,18 @@ utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [ "compile", RegExpCompileJS ]); +%DefineGetterPropertyUnchecked(GlobalRegExp.prototype, "global", + GetRegExpFlagGetter("RegExp.prototype.global", REGEXP_GLOBAL_MASK), + DONT_ENUM); +%DefineGetterPropertyUnchecked(GlobalRegExp.prototype, "ignoreCase", + GetRegExpFlagGetter("RegExp.prototype.ignoreCase", REGEXP_IGNORE_CASE_MASK), + DONT_ENUM); +%DefineGetterPropertyUnchecked(GlobalRegExp.prototype, "multiline", + GetRegExpFlagGetter("RegExp.prototype.multiline", REGEXP_MULTILINE_MASK), + DONT_ENUM); +%DefineGetterPropertyUnchecked(GlobalRegExp.prototype, "source", + RegExpGetSource, DONT_ENUM); + // The length of compile is 1 in SpiderMonkey. %FunctionSetLength(GlobalRegExp.prototype.compile, 1); @@ -422,6 +464,7 @@ for (var i = 1; i < 10; ++i) { // Exports utils.Export(function(to) { + to.GetRegExpFlagGetter = GetRegExpFlagGetter; to.RegExpExec = DoRegExpExec; to.RegExpExecNoTests = RegExpExecNoTests; to.RegExpLastMatchInfo = RegExpLastMatchInfo; diff --git a/src/js/string.js b/src/js/string.js index 0fdd7ae342..33ac96577e 100644 --- a/src/js/string.js +++ b/src/js/string.js @@ -19,6 +19,7 @@ var MakeRangeError; var MakeTypeError; var RegExpExec; var RegExpExecNoTests; +var regExpFlagsSymbol = utils.ImportNow("regexp_flags_symbol"); var RegExpLastMatchInfo; utils.Import(function(from) { @@ -155,7 +156,7 @@ function StringMatchJS(regexp) { var subject = TO_STRING(this); if (IS_REGEXP(regexp)) { - if (!regexp.global) return RegExpExecNoTests(regexp, subject, 0); + if (!REGEXP_GLOBAL(regexp)) return RegExpExecNoTests(regexp, subject, 0); var result = %StringMatch(subject, regexp, RegExpLastMatchInfo); regexp.lastIndex = 0; return result; @@ -225,7 +226,7 @@ function StringReplace(search, replace) { if (!IS_CALLABLE(replace)) { replace = TO_STRING(replace); - if (!search.global) { + if (!REGEXP_GLOBAL(search)) { // Non-global regexp search, string replace. var match = RegExpExec(search, subject, 0); if (match == null) { @@ -247,7 +248,7 @@ function StringReplace(search, replace) { subject, search, replace, RegExpLastMatchInfo); } - if (search.global) { + if (REGEXP_GLOBAL(search)) { // Global regexp search, function replace. return StringReplaceGlobalRegExpWithFunction(subject, search, replace); } diff --git a/src/js/symbol.js b/src/js/symbol.js index bb03ed211c..62ef0dd216 100644 --- a/src/js/symbol.js +++ b/src/js/symbol.js @@ -16,7 +16,6 @@ var GlobalSymbol = global.Symbol; var hasInstanceSymbol = utils.ImportNow("has_instance_symbol"); var isConcatSpreadableSymbol = utils.ImportNow("is_concat_spreadable_symbol"); -var isRegExpSymbol = utils.ImportNow("is_regexp_symbol"); var iteratorSymbol = utils.ImportNow("iterator_symbol"); var MakeTypeError; var ObjectGetOwnPropertyKeys; @@ -94,7 +93,6 @@ utils.InstallConstants(GlobalSymbol, [ // TODO(rossberg): expose when implemented. // "hasInstance", hasInstanceSymbol, // "isConcatSpreadable", isConcatSpreadableSymbol, - // "isRegExp", isRegExpSymbol, "iterator", iteratorSymbol, // TODO(yangguo): expose when implemented. // "match", matchSymbol, diff --git a/src/messages.h b/src/messages.h index 9e5172c49f..0ac605f717 100644 --- a/src/messages.h +++ b/src/messages.h @@ -117,8 +117,6 @@ class CallSite { "Class extends value % is not a function or null") \ T(FirstArgumentNotRegExp, \ "First argument to % must not be a regular expression") \ - T(FlagsGetterNonObject, \ - "RegExp.prototype.flags getter called on non-object %") \ T(FunctionBind, "Bind must be called on a function") \ T(GeneratorRunning, "Generator is already running") \ T(IllegalInvocation, "Illegal invocation") \ @@ -203,6 +201,8 @@ class CallSite { T(ReduceNoInitial, "Reduce of empty array with no initial value") \ T(RegExpFlags, \ "Cannot supply flags when constructing one RegExp from another") \ + T(RegExpNonObject, "% getter called on non-object %") \ + T(RegExpNonRegExp, "% is not an RegExp object") \ T(ReinitializeIntl, "Trying to re-initialize % object.") \ T(ResolvedOptionsCalledOnNonObject, \ "resolvedOptions method called on a non-object or on a object that is " \ diff --git a/src/objects.h b/src/objects.h index 8093f6668a..f851a57719 100644 --- a/src/objects.h +++ b/src/objects.h @@ -7781,11 +7781,9 @@ class JSRegExp: public JSObject { // In-object fields. static const int kSourceFieldIndex = 0; - static const int kGlobalFieldIndex = 1; - static const int kIgnoreCaseFieldIndex = 2; - static const int kMultilineFieldIndex = 3; - static const int kLastIndexFieldIndex = 4; - static const int kInObjectFieldCount = 5; + static const int kFlagsFieldIndex = 1; + static const int kLastIndexFieldIndex = 2; + static const int kInObjectFieldCount = 3; // The uninitialized value for a regexp code object. static const int kUninitializedValue = -1; diff --git a/src/runtime/runtime-regexp.cc b/src/runtime/runtime-regexp.cc index 58c14bc182..826e1d5864 100644 --- a/src/runtime/runtime-regexp.cc +++ b/src/runtime/runtime-regexp.cc @@ -924,26 +924,14 @@ RUNTIME_FUNCTION(Runtime_RegExpInitializeAndCompile) { ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, escaped_source, EscapeRegExpSource(isolate, source)); - Handle global = factory->ToBoolean(flags.is_global()); - Handle ignore_case = factory->ToBoolean(flags.is_ignore_case()); - Handle multiline = factory->ToBoolean(flags.is_multiline()); - Handle sticky = factory->ToBoolean(flags.is_sticky()); - Handle unicode = factory->ToBoolean(flags.is_unicode()); - Map* map = regexp->map(); Object* constructor = map->GetConstructor(); - if (!FLAG_harmony_regexps && !FLAG_harmony_unicode_regexps && - constructor->IsJSFunction() && + if (constructor->IsJSFunction() && JSFunction::cast(constructor)->initial_map() == map) { // If we still have the original map, set in-object properties directly. regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, *escaped_source); - // Both true and false are immovable immortal objects so no need for write - // barrier. - regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, *global, - SKIP_WRITE_BARRIER); - regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, *ignore_case, - SKIP_WRITE_BARRIER); - regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, *multiline, + regexp->InObjectPropertyAtPut(JSRegExp::kFlagsFieldIndex, + Smi::FromInt(flags.value()), SKIP_WRITE_BARRIER); regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex, Smi::FromInt(0), SKIP_WRITE_BARRIER); @@ -958,22 +946,13 @@ RUNTIME_FUNCTION(Runtime_RegExpInitializeAndCompile) { PropertyAttributes writable = static_cast(DONT_ENUM | DONT_DELETE); Handle zero(Smi::FromInt(0), isolate); - JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->source_string(), - escaped_source, final).Check(); - JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->global_string(), - global, final).Check(); JSObject::SetOwnPropertyIgnoreAttributes( - regexp, factory->ignore_case_string(), ignore_case, final).Check(); + regexp, factory->regexp_source_symbol(), escaped_source, final) + .Check(); JSObject::SetOwnPropertyIgnoreAttributes( - regexp, factory->multiline_string(), multiline, final).Check(); - if (FLAG_harmony_regexps) { - JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->sticky_string(), - sticky, final).Check(); - } - if (FLAG_harmony_unicode_regexps) { - JSObject::SetOwnPropertyIgnoreAttributes( - regexp, factory->unicode_string(), unicode, final).Check(); - } + regexp, factory->regexp_flags_symbol(), + Handle(Smi::FromInt(flags.value()), isolate), final) + .Check(); JSObject::SetOwnPropertyIgnoreAttributes( regexp, factory->last_index_string(), zero, writable).Check(); } diff --git a/test/mjsunit/es6/regexp-flags.js b/test/mjsunit/es6/regexp-flags.js new file mode 100644 index 0000000000..a909076443 --- /dev/null +++ b/test/mjsunit/es6/regexp-flags.js @@ -0,0 +1,49 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-regexps --harmony-unicode-regexps + +var r1 = /abc/gi; +assertEquals("abc", r1.source); +assertTrue(r1.global); +assertTrue(r1.ignoreCase); +assertFalse(r1.multiline); +assertFalse(r1.sticky); +assertFalse(r1.unicode); + +// Internal slot of prototype is not read. +var r2 = { __proto__: r1 }; +assertThrows(function() { r2.source; }, TypeError); +assertThrows(function() { r2.global; }, TypeError); +assertThrows(function() { r2.ignoreCase; }, TypeError); +assertThrows(function() { r2.multiline; }, TypeError); +assertThrows(function() { r2.sticky; }, TypeError); +assertThrows(function() { r2.unicode; }, TypeError); + +var r3 = /I/; +var string = "iIiIi"; +var expected = "iXiIi"; +assertFalse(r3.global); +assertFalse(r3.ignoreCase); +assertEquals("", r3.flags); +assertEquals(expected, string.replace(r3, "X")); + +var get_count = 0; +Object.defineProperty(r3, "global", { + get: function() { get_count++; return true; } +}); +Object.defineProperty(r3, "ignoreCase", { + get: function() { get_count++; return true; } +}); + +assertTrue(r3.global); +assertEquals(1, get_count); +assertTrue(r3.ignoreCase); +assertEquals(2, get_count); +// Overridden flag getters affects the flags getter. +assertEquals("gi", r3.flags); +assertEquals(4, get_count); +// Overridden flag getters do not affect the internal flags. +assertEquals(expected, string.replace(r3, "X")); +assertEquals(4, get_count); diff --git a/test/mjsunit/mirror-regexp.js b/test/mjsunit/mirror-regexp.js index 6c251d4ff6..882af8dd6e 100644 --- a/test/mjsunit/mirror-regexp.js +++ b/test/mjsunit/mirror-regexp.js @@ -25,19 +25,17 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --expose-debug-as debug --harmony-unicode-regexps +// Flags: --expose-debug-as debug --harmony-regexps --harmony-unicode-regexps // Test the mirror object for regular expression values -var all_attributes = debug.PropertyAttribute.ReadOnly | - debug.PropertyAttribute.DontEnum | - debug.PropertyAttribute.DontDelete; -var expected_attributes = { - 'source': all_attributes, - 'global': all_attributes, - 'ignoreCase': all_attributes, - 'multiline': all_attributes, - 'unicode' : all_attributes, - 'lastIndex': debug.PropertyAttribute.DontEnum | debug.PropertyAttribute.DontDelete +var dont_enum = debug.PropertyAttribute.DontEnum; +var dont_delete = debug.PropertyAttribute.DontDelete; +var expected_prototype_attributes = { + 'source': dont_enum, + 'global': dont_enum, + 'ignoreCase': dont_enum, + 'multiline': dont_enum, + 'unicode' : dont_enum, }; function MirrorRefCache(json_refs) { @@ -70,9 +68,12 @@ function testRegExpMirror(r) { assertTrue(mirror.isRegExp()); assertEquals('regexp', mirror.type()); assertFalse(mirror.isPrimitive()); - for (var p in expected_attributes) { - assertEquals(expected_attributes[p], - mirror.property(p).attributes(), + assertEquals(dont_enum | dont_delete, + mirror.property('lastIndex').attributes()); + var proto_mirror = mirror.protoObject(); + for (var p in expected_prototype_attributes) { + assertEquals(expected_prototype_attributes[p], + proto_mirror.property(p).attributes(), p + ' attributes'); } @@ -83,24 +84,12 @@ function testRegExpMirror(r) { var fromJSON = eval('(' + json + ')'); assertEquals('regexp', fromJSON.type); assertEquals('RegExp', fromJSON.className); - for (var p in expected_attributes) { - for (var i = 0; i < fromJSON.properties.length; i++) { - if (fromJSON.properties[i].name == p) { - assertEquals(expected_attributes[p], - fromJSON.properties[i].attributes, - 'Unexpected value for ' + p + ' attributes'); - assertEquals(mirror.property(p).propertyType(), - fromJSON.properties[i].propertyType, - 'Unexpected value for ' + p + ' propertyType'); - assertEquals(mirror.property(p).value().handle(), - fromJSON.properties[i].ref, - 'Unexpected handle for ' + p); - assertEquals(mirror.property(p).value().value(), - refs.lookup(fromJSON.properties[i].ref).value, - 'Unexpected value for ' + p); - } - } - } + assertEquals('lastIndex', fromJSON.properties[0].name); + assertEquals(dont_enum | dont_delete, fromJSON.properties[0].attributes); + assertEquals(mirror.property('lastIndex').propertyType(), + fromJSON.properties[0].propertyType); + assertEquals(mirror.property('lastIndex').value().value(), + refs.lookup(fromJSON.properties[0].ref).value); } diff --git a/test/mjsunit/regexp.js b/test/mjsunit/regexp.js index c2d92823bc..6374296210 100644 --- a/test/mjsunit/regexp.js +++ b/test/mjsunit/regexp.js @@ -605,23 +605,29 @@ assertEquals(["ts", "li"], log); // Check that properties of RegExp have the correct permissions. var re = /x/g; -var desc = Object.getOwnPropertyDescriptor(re, "global"); -assertEquals(true, desc.value); -assertEquals(false, desc.configurable); +var desc = Object.getOwnPropertyDescriptor(re.__proto__, "global"); +assertInstanceof(desc.get, Function); +assertEquals(true, desc.configurable); assertEquals(false, desc.enumerable); -assertEquals(false, desc.writable); + +desc = Object.getOwnPropertyDescriptor(re.__proto__, "multiline"); +assertInstanceof(desc.get, Function); +assertEquals(true, desc.configurable); +assertEquals(false, desc.enumerable); + +desc = Object.getOwnPropertyDescriptor(re.__proto__, "ignoreCase"); +assertInstanceof(desc.get, Function); +assertEquals(true, desc.configurable); +assertEquals(false, desc.enumerable); + +desc = Object.getOwnPropertyDescriptor(re, "global"); +assertEquals(undefined, desc); desc = Object.getOwnPropertyDescriptor(re, "multiline"); -assertEquals(false, desc.value); -assertEquals(false, desc.configurable); -assertEquals(false, desc.enumerable); -assertEquals(false, desc.writable); +assertEquals(undefined, desc); desc = Object.getOwnPropertyDescriptor(re, "ignoreCase"); -assertEquals(false, desc.value); -assertEquals(false, desc.configurable); -assertEquals(false, desc.enumerable); -assertEquals(false, desc.writable); +assertEquals(undefined, desc); desc = Object.getOwnPropertyDescriptor(re, "lastIndex"); assertEquals(0, desc.value); diff --git a/test/mjsunit/regress/regress-447561.js b/test/mjsunit/regress/regress-447561.js index 0d7a321de0..e1a5ba5aa5 100644 --- a/test/mjsunit/regress/regress-447561.js +++ b/test/mjsunit/regress/regress-447561.js @@ -3,8 +3,8 @@ // found in the LICENSE file. __proto__ = /foo/gi; -assertEquals("foo", source); -assertTrue(global); -assertTrue(ignoreCase); -assertFalse(multiline); +assertThrows(function() { source }); +assertThrows(function() { global }); +assertThrows(function() { ignoreCase }); +assertThrows(function() { multiline }); assertEquals(0, lastIndex); diff --git a/test/test262/test262.status b/test/test262/test262.status index a5c4047841..dd9a3a03e0 100644 --- a/test/test262/test262.status +++ b/test/test262/test262.status @@ -144,24 +144,6 @@ 'built-ins/WeakMap/iterator-items-are-not-object-close-iterator': [FAIL], 'built-ins/WeakSet/iterator-close-after-add-failure': [FAIL], - # https://code.google.com/p/v8/issues/detail?id=3715 - 'built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-212': [FAIL], - 'built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-213': [FAIL], - 'built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-214': [FAIL], - 'built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-215': [FAIL], - 'built-ins/RegExp/prototype/global/15.10.7.2-1': [FAIL], - 'built-ins/RegExp/prototype/global/15.10.7.2-2': [FAIL], - 'built-ins/RegExp/prototype/global/S15.10.7.2_A9': [FAIL], - 'built-ins/RegExp/prototype/ignoreCase/15.10.7.3-1': [FAIL], - 'built-ins/RegExp/prototype/ignoreCase/15.10.7.3-2': [FAIL], - 'built-ins/RegExp/prototype/ignoreCase/S15.10.7.3_A9': [FAIL], - 'built-ins/RegExp/prototype/multiline/15.10.7.4-1': [FAIL], - 'built-ins/RegExp/prototype/multiline/15.10.7.4-2': [FAIL], - 'built-ins/RegExp/prototype/multiline/S15.10.7.4_A9': [FAIL], - 'built-ins/RegExp/prototype/source/15.10.7.1-1': [FAIL], - 'built-ins/RegExp/prototype/source/15.10.7.1-2': [FAIL], - 'built-ins/RegExp/prototype/source/S15.10.7.1_A9': [FAIL], - # https://code.google.com/p/v8/issues/detail?id=4243 'built-ins/Promise/race/S25.4.4.3_A3.1_T2': [FAIL], 'built-ins/Promise/reject/S25.4.4.4_A3.1_T1': [FAIL], @@ -181,16 +163,6 @@ 'built-ins/RegExp/from-regexp-like-get-ctor-err': [FAIL], 'built-ins/RegExp/call_with_regexp_not_same_constructor': [FAIL], - # https://code.google.com/p/v8/issues/detail?id=4528 - 'built-ins/RegExp/prototype/source/S15.10.7.1_A8': [FAIL], - 'built-ins/RegExp/prototype/source/S15.10.7.1_A10': [FAIL], - 'built-ins/RegExp/prototype/global/S15.10.7.2_A8': [FAIL], - 'built-ins/RegExp/prototype/global/S15.10.7.2_A10': [FAIL], - 'built-ins/RegExp/prototype/ignoreCase/S15.10.7.3_A8': [FAIL], - 'built-ins/RegExp/prototype/ignoreCase/S15.10.7.3_A10': [FAIL], - 'built-ins/RegExp/prototype/multiline/S15.10.7.4_A8': [FAIL], - 'built-ins/RegExp/prototype/multiline/S15.10.7.4_A10': [FAIL], - # https://code.google.com/p/v8/issues/detail?id=4006 'built-ins/String/prototype/S15.5.4_A1': [FAIL], 'built-ins/String/prototype/S15.5.4_A2': [FAIL], @@ -380,11 +352,7 @@ 'built-ins/RegExp/prototype/test/u-captured-value': [FAIL], 'built-ins/RegExp/prototype/test/u-lastindex-adv': [FAIL], 'built-ins/RegExp/prototype/test/u-lastindex-value': [FAIL], - 'built-ins/RegExp/prototype/unicode/length': [FAIL], 'built-ins/RegExp/prototype/unicode/name': [FAIL], - 'built-ins/RegExp/prototype/unicode/prop-desc': [FAIL], - 'built-ins/RegExp/prototype/unicode/this-invald-obj': [FAIL], - 'built-ins/RegExp/prototype/unicode/this-non-obj': [FAIL], 'built-ins/RegExp/prototype/unicode/this-regexp': [FAIL], 'built-ins/RegExp/unicode_identity_escape': [FAIL], 'language/literals/regexp/u-unicode-esc': [FAIL], @@ -395,9 +363,6 @@ # https://code.google.com/p/v8/issues/detail?id=4342 'built-ins/RegExp/prototype/exec/get-sticky-coerce': [FAIL], 'built-ins/RegExp/prototype/exec/get-sticky-err': [FAIL], - 'built-ins/RegExp/prototype/sticky/prop-desc': [FAIL], - 'built-ins/RegExp/prototype/sticky/this-invalid-obj': [FAIL], - 'built-ins/RegExp/prototype/sticky/this-non-obj': [FAIL], 'built-ins/RegExp/prototype/test/get-sticky-coerce': [FAIL], 'built-ins/RegExp/prototype/test/get-sticky-err': [FAIL], 'built-ins/RegExp/valid-flags-y': [FAIL], @@ -441,7 +406,6 @@ # https://code.google.com/p/v8/issues/detail?id=4346 'built-ins/RegExp/prototype/flags/name': [FAIL], - 'built-ins/RegExp/prototype/flags/y-attr-err': [FAIL], 'built-ins/RegExp/prototype/flags/u': [FAIL], # https://code.google.com/p/v8/issues/detail?id=4347 diff --git a/test/webkit/fast/regex/toString-expected.txt b/test/webkit/fast/regex/toString-expected.txt index 3e661dd879..a404f18dde 100644 --- a/test/webkit/fast/regex/toString-expected.txt +++ b/test/webkit/fast/regex/toString-expected.txt @@ -28,7 +28,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE PASS RegExp('/').source is "\\/" PASS RegExp('').source is "(?:)" -FAIL RegExp.prototype.source should be (?:) (of type string). Was undefined (of type undefined). +FAIL RegExp.prototype.source should be (?:). Threw exception TypeError: Method RegExp.prototype.toString called on incompatible receiver [object Object] PASS RegExp('/').toString() is "/\\//" PASS RegExp('').toString() is "/(?:)/" FAIL RegExp.prototype.toString() should be /(?:)/. Threw exception TypeError: Method RegExp.prototype.toString called on incompatible receiver [object Object] @@ -58,3 +58,4 @@ PASS successfullyParsed is true TEST COMPLETE + diff --git a/tools/js2c.py b/tools/js2c.py index dd7becd5fd..d915133114 100755 --- a/tools/js2c.py +++ b/tools/js2c.py @@ -348,8 +348,8 @@ def BuildFilterChain(macro_filename, message_template_file): if macro_filename: (consts, macros) = ReadMacros(ReadFile(macro_filename)) - filter_chain.append(lambda l: ExpandConstants(l, consts)) filter_chain.append(lambda l: ExpandMacros(l, macros)) + filter_chain.append(lambda l: ExpandConstants(l, consts)) if message_template_file: message_templates = ReadMessageTemplates(ReadFile(message_template_file))