Skip to content

Commit

Permalink
Implement Legacy RegExp features
Browse files Browse the repository at this point in the history
Signed-off-by: bence gabor kis <[email protected]>
  • Loading branch information
Bence Gabor Kis committed Feb 15, 2021
1 parent cbc4723 commit 2ddd965
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 48 deletions.
65 changes: 43 additions & 22 deletions src/runtime/GlobalObjectBuiltinRegExp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ static Value builtinRegExpConstructor(ExecutionState& state, Value thisValue, si
return constructorRealm->globalObject()->regexpPrototype();
});
RegExpObject* regexp = new RegExpObject(state, proto, source, option);
Context* thisRealm = state.context();
if (newTarget == thisRealm->globalObject()->regexp()) {
regexp->setLegacyFeaturesEnabled(true);
} else {
regexp->setLegacyFeaturesEnabled(false);
}

// TODO http://www.ecma-international.org/ecma-262/6.0/index.html#sec-escaperegexppattern
return regexp;
Expand Down Expand Up @@ -641,38 +647,53 @@ static Value builtinRegExpUnicodeGetter(ExecutionState& state, Value thisValue,
return builtinRegExpOptionGetterHelper(state, thisValue, RegExpObject::Option::Unicode);
}

#define DEFINE_GETTER_LEGACY_REGEXP_PROPERTY(stringView) \
if (!thisValue.isPointerValue() || thisValue.asPointerValue() != state.context()->globalObject()->regexp()) { \
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, ErrorObject::Messages::GlobalObject_ThisNotRegExpObject); \
} \
if (stringView.isEmpty()) { \
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, ErrorObject::Messages::GlobalObject_ThisNotRegExpObject); \
} \
return stringView;

// For non-standard, read-only properties of RegExp
static Value builtinRegExpInputGetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
return state.resolveCallee()->codeBlock()->context()->regexpStatus().input;
StringView* stringView = new StringView(state.resolveCallee()->codeBlock()->context()->regexpStatus().input);
DEFINE_GETTER_LEGACY_REGEXP_PROPERTY(Value(stringView));
}

static Value builtinRegExpInputSetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
if (!thisValue.isPointerValue() || thisValue.asPointerValue() != state.context()->globalObject()->regexp()) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, ErrorObject::Messages::GlobalObject_ThisNotRegExpObject);
}
state.resolveCallee()->codeBlock()->context()->regexpStatus().input = argv[0].toString(state);
return Value();
}

static Value builtinRegExpLastMatchGetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
return Value(new StringView(state.resolveCallee()->codeBlock()->context()->regexpStatus().lastMatch));
DEFINE_GETTER_LEGACY_REGEXP_PROPERTY(Value(new StringView(state.resolveCallee()->codeBlock()->context()->regexpStatus().lastMatch)))
}

static Value builtinRegExpLastParenGetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
return Value(new StringView(state.resolveCallee()->codeBlock()->context()->regexpStatus().lastParen));
DEFINE_GETTER_LEGACY_REGEXP_PROPERTY(Value(new StringView(state.resolveCallee()->codeBlock()->context()->regexpStatus().lastParen)))
}


static Value builtinRegExpLeftContextGetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
return Value(new StringView(state.resolveCallee()->codeBlock()->context()->regexpStatus().leftContext));
DEFINE_GETTER_LEGACY_REGEXP_PROPERTY(Value(new StringView(state.resolveCallee()->codeBlock()->context()->regexpStatus().leftContext)))
}

static Value builtinRegExpRightContextGetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
return Value(new StringView(state.resolveCallee()->codeBlock()->context()->regexpStatus().rightContext));
DEFINE_GETTER_LEGACY_REGEXP_PROPERTY(Value(new StringView(state.resolveCallee()->codeBlock()->context()->regexpStatus().rightContext)))
}


static Value builtinRegExpStringIteratorNext(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
if (!thisValue.isObject() || !thisValue.asObject()->isRegExpStringIteratorObject()) {
Expand All @@ -687,9 +708,9 @@ static Value builtinRegExpStringIteratorNext(ExecutionState& state, Value thisVa
{ \
auto& status = state.resolveCallee()->codeBlock()->context()->regexpStatus(); \
if (status.dollarCount < number) { \
return Value(String::emptyString); \
DEFINE_GETTER_LEGACY_REGEXP_PROPERTY(Value(new StringView(String::emptyString))) \
} \
return Value(new StringView(status.dollars[number - 1])); \
DEFINE_GETTER_LEGACY_REGEXP_PROPERTY(Value(new StringView(status.dollars[number - 1]))) \
}

DEFINE_GETTER(1)
Expand Down Expand Up @@ -752,43 +773,43 @@ void GlobalObject::installRegExp(ExecutionState& state)
JSGetterSetter gs(
new NativeFunctionObject(state, NativeFunctionInfo(strings->get, builtinRegExpInputGetter, 0, NativeFunctionInfo::Strict)),
new NativeFunctionObject(state, NativeFunctionInfo(strings->set, builtinRegExpInputSetter, 1, NativeFunctionInfo::Strict)));
m_regexp->defineOwnProperty(state, strings->input, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::EnumerablePresent)));
m_regexp->defineOwnProperty(state, strings->$_, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::NotPresent)));
m_regexp->defineOwnProperty(state, strings->input, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
m_regexp->defineOwnProperty(state, strings->$_, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
}

{
JSGetterSetter gs(
new NativeFunctionObject(state, NativeFunctionInfo(strings->get, builtinRegExpLastMatchGetter, 0, NativeFunctionInfo::Strict)), Value(Value::EmptyValue));
m_regexp->defineOwnProperty(state, strings->lastMatch, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::EnumerablePresent)));
m_regexp->defineOwnProperty(state, strings->$Ampersand, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::NotPresent)));
m_regexp->defineOwnProperty(state, strings->lastMatch, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
m_regexp->defineOwnProperty(state, strings->$Ampersand, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
}

{
JSGetterSetter gs(
new NativeFunctionObject(state, NativeFunctionInfo(strings->get, builtinRegExpLastParenGetter, 0, NativeFunctionInfo::Strict)), Value(Value::EmptyValue));
m_regexp->defineOwnProperty(state, strings->lastParen, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::EnumerablePresent)));
m_regexp->defineOwnProperty(state, strings->$PlusSign, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::NotPresent)));
m_regexp->defineOwnProperty(state, strings->lastParen, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
m_regexp->defineOwnProperty(state, strings->$PlusSign, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
}

{
JSGetterSetter gs(
new NativeFunctionObject(state, NativeFunctionInfo(strings->get, builtinRegExpLeftContextGetter, 0, NativeFunctionInfo::Strict)), Value(Value::EmptyValue));
m_regexp->defineOwnProperty(state, strings->leftContext, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::EnumerablePresent)));
m_regexp->defineOwnProperty(state, strings->$GraveAccent, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::NotPresent)));
m_regexp->defineOwnProperty(state, strings->leftContext, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
m_regexp->defineOwnProperty(state, strings->$GraveAccent, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
}

{
JSGetterSetter gs(
new NativeFunctionObject(state, NativeFunctionInfo(strings->get, builtinRegExpRightContextGetter, 0, NativeFunctionInfo::Strict)), Value(Value::EmptyValue));
m_regexp->defineOwnProperty(state, strings->rightContext, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::EnumerablePresent)));
m_regexp->defineOwnProperty(state, strings->$Apostrophe, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::NotPresent)));
m_regexp->defineOwnProperty(state, strings->rightContext, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
m_regexp->defineOwnProperty(state, strings->$Apostrophe, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
}

#define DEFINE_ATTR(number) \
{ \
JSGetterSetter gs( \
new NativeFunctionObject(state, NativeFunctionInfo(strings->get, builtinRegExpDollar##number##Getter, 0, NativeFunctionInfo::Strict)), Value(Value::EmptyValue)); \
m_regexp->defineOwnProperty(state, strings->$##number, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::EnumerablePresent))); \
#define DEFINE_ATTR(number) \
{ \
JSGetterSetter gs( \
new NativeFunctionObject(state, NativeFunctionInfo(strings->get, builtinRegExpDollar##number##Getter, 0, NativeFunctionInfo::Strict)), Value(Value::EmptyValue)); \
m_regexp->defineOwnProperty(state, strings->$##number, ObjectPropertyDescriptor(gs, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent))); \
}

DEFINE_ATTR(1)
Expand Down
20 changes: 20 additions & 0 deletions src/runtime/RegExpObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ RegExpObject::RegExpObject(ExecutionState& state, Object* proto, bool hasLastInd
, m_bytecodePattern(NULL)
, m_lastIndex(Value(0))
, m_lastExecutedString(NULL)
, m_legacyFeaturesEnabled(true)
{
initRegExpObject(state, hasLastIndex);
}
Expand Down Expand Up @@ -457,6 +458,18 @@ void RegExpObject::createRegexMatchResult(ExecutionState& state, String* str, Re
} while (testResult);
}

static void invalidateLegacyRegExpStaticProperties(ExecutionState& state)
{
state.context()->regexpStatus().input = String::emptyString;
state.context()->regexpStatus().lastMatch = StringView();
state.context()->regexpStatus().lastParen = StringView();
state.context()->regexpStatus().leftContext = StringView();
state.context()->regexpStatus().rightContext = StringView();
for (size_t i = 0; i < 9; i++) {
state.context()->regexpStatus().dollars[i] = StringView();
}
}

ArrayObject* RegExpObject::createRegExpMatchedArray(ExecutionState& state, const RegexMatchResult& result, String* input)
{
uint64_t len = 0;
Expand Down Expand Up @@ -496,6 +509,13 @@ ArrayObject* RegExpObject::createRegExpMatchedArray(ExecutionState& state, const
}
arr->defineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().groups), ObjectPropertyDescriptor(Value(groups), ObjectPropertyDescriptor::AllPresent));
}
Context* thisRealm = state.context();
Context* rRealm = this->getFunctionRealm(state);
if (thisRealm == rRealm) {
if (!this->legacyFeaturesEnabled()) {
invalidateLegacyRegExpStaticProperties(state);
}
}
return arr;
}

Expand Down
14 changes: 13 additions & 1 deletion src/runtime/RegExpObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,18 @@ class RegExpObject : public Object {
}

void setLastIndex(ExecutionState& state, const Value& v);


bool legacyFeaturesEnabled()
{
return m_legacyFeaturesEnabled;
}

void setLegacyFeaturesEnabled(bool isEnabled)
{
m_legacyFeaturesEnabled = isEnabled;
}

virtual bool defineOwnProperty(ExecutionState& state, const ObjectPropertyName& P, const ObjectPropertyDescriptor& desc) override;

virtual bool isRegExpObject() const override
Expand All @@ -165,14 +177,14 @@ class RegExpObject : public Object {

void parseOption(ExecutionState& state, String* optionString);


String* m_source;
String* m_optionString;
Option m_option;
JSC::Yarr::YarrPattern* m_yarrPattern;
JSC::Yarr::BytecodePattern* m_bytecodePattern;
EncodedValue m_lastIndex;
const String* m_lastExecutedString;
bool m_legacyFeaturesEnabled;
};
class RegExpStringIteratorObject : public IteratorObject {
public:
Expand Down
1 change: 1 addition & 0 deletions tools/test/spidermonkey/excludelist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ non262/BigInt/mod.js
non262/BigInt/Number-conversion-rounding.js
non262/class/newTargetDVG.js
non262/RegExp/flags.js
non262/regress/regress-591846.js


# Passed in 64bit but, Failed in 32bit binary due to value expression bug in 32bit mode
Expand Down
26 changes: 1 addition & 25 deletions tools/test/test262/excludelist.orig.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,30 +31,6 @@
<test id="intl402/NumberFormat/prototype/formatToParts/signDisplay-ko-KR"><reason>ICU 67 returns wrong result with -0.0001</reason></test>
<test id="intl402/NumberFormat/prototype/formatToParts/signDisplay-zh-TW"><reason>ICU 67 returns wrong result with -0.0001</reason></test>

<test id="annexB/built-ins/RegExp/legacy-accessors/index/prop-desc"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/index/this-cross-realm-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/index/this-not-regexp-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/index/this-subclass-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/input/prop-desc"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/input/this-cross-realm-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/input/this-not-regexp-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/input/this-subclass-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/lastMatch/prop-desc"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/lastMatch/this-cross-realm-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/lastMatch/this-not-regexp-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/lastMatch/this-subclass-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/lastParen/prop-desc"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/lastParen/this-cross-realm-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/lastParen/this-not-regexp-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/lastParen/this-subclass-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/leftContext/prop-desc"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/leftContext/this-cross-realm-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/leftContext/this-not-regexp-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/leftContext/this-subclass-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/rightContext/prop-desc"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/rightContext/this-cross-realm-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/rightContext/this-not-regexp-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/legacy-accessors/rightContext/this-subclass-constructor"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/named-groups/non-unicode-malformed"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/named-groups/non-unicode-malformed-lookbehind"><reason>TODO</reason></test>
<test id="annexB/built-ins/RegExp/prototype/compile/this-cross-realm-instance"><reason>TODO</reason></test>
Expand Down Expand Up @@ -5737,4 +5713,4 @@
<test id="language/statements/with/decl-cls"><reason>TODO</reason></test>
<test id="language/statements/with/labelled-fn-stmt"><reason>TODO</reason></test>
<test id="language/statements/with/let-array-with-newline"><reason>TODO</reason></test>
</excludeList>
</excludeList>
3 changes: 3 additions & 0 deletions tools/test/v8/v8.mjsunit.status
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@

##############################################################################

# ES6 ESNEXT difference regexp.
'fuzz-accessors': [SKIP],

# ES6 ES10 difference proxy.
'es6/proxies-define-property': [SKIP],
'es6/proxies-get-own-property-descriptor': [SKIP],
Expand Down

0 comments on commit 2ddd965

Please sign in to comment.