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 9, 2021
1 parent cbc4723 commit fe415e5
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 48 deletions.
97 changes: 75 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,36 +647,79 @@ static Value builtinRegExpUnicodeGetter(ExecutionState& state, Value thisValue,
return builtinRegExpOptionGetterHelper(state, thisValue, RegExpObject::Option::Unicode);
}

static Value getLegacyRegExpStaticProperty(ExecutionState& state, Value thisValue, Value stringVal)
{
if (!thisValue.isPointerValue() || thisValue.asPointerValue() != state.context()->globalObject()->regexp()) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "thisValue is not RegExp");
}
if (stringVal.isEmpty()) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "Value is empty");
}
return stringVal;
}

static Value setLegacyRegExpStaticProperty(ExecutionState& state, Value thisValue, Value stringVal, StringView* strView)
{
if (!thisValue.isPointerValue() || thisValue.asPointerValue() != state.context()->globalObject()->regexp()) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "thisValue is not RegExp");
}
strView = new StringView(stringVal.toString(state));
return Value();
}

// 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;
return getLegacyRegExpStaticProperty(state, thisValue, state.resolveCallee()->codeBlock()->context()->regexpStatus().input);
}

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, "thisValue is not RegExp");
}
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));
return getLegacyRegExpStaticProperty(state, thisValue, Value(new StringView(state.resolveCallee()->codeBlock()->context()->regexpStatus().lastMatch)));
}

static Value builtinRegExpLastMatchSetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
return setLegacyRegExpStaticProperty(state, thisValue, argv[0], &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));
return getLegacyRegExpStaticProperty(state, thisValue, Value(new StringView(state.resolveCallee()->codeBlock()->context()->regexpStatus().lastParen)));
}

static Value builtinRegExpLastParenSetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
return setLegacyRegExpStaticProperty(state, thisValue, argv[0], &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));
return getLegacyRegExpStaticProperty(state, thisValue, Value(new StringView(state.resolveCallee()->codeBlock()->context()->regexpStatus().leftContext)));
}

static Value builtinRegExpLeftContextSetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
return setLegacyRegExpStaticProperty(state, thisValue, argv[0], &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));
return getLegacyRegExpStaticProperty(state, thisValue, Value(new StringView(state.resolveCallee()->codeBlock()->context()->regexpStatus().rightContext)));
}

static Value builtinRegExpRightContextSetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
return setLegacyRegExpStaticProperty(state, thisValue, argv[0], &state.resolveCallee()->codeBlock()->context()->regexpStatus().rightContext);
}

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

DEFINE_GETTER(1)
Expand Down Expand Up @@ -752,43 +801,47 @@ 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)));
new NativeFunctionObject(state, NativeFunctionInfo(strings->set, builtinRegExpLastMatchSetter, 1, NativeFunctionInfo::Strict));
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)));
new NativeFunctionObject(state, NativeFunctionInfo(strings->set, builtinRegExpLastParenSetter, 1, NativeFunctionInfo::Strict));
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)));
new NativeFunctionObject(state, NativeFunctionInfo(strings->set, builtinRegExpLeftContextSetter, 1, NativeFunctionInfo::Strict));
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)));
new NativeFunctionObject(state, NativeFunctionInfo(strings->set, builtinRegExpRightContextSetter, 1, NativeFunctionInfo::Strict));
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.resolveCallee()->codeBlock()->context()->regexpStatus().input = String::emptyString;
state.resolveCallee()->codeBlock()->context()->regexpStatus().lastMatch = StringView();
state.resolveCallee()->codeBlock()->context()->regexpStatus().lastParen = StringView();
state.resolveCallee()->codeBlock()->context()->regexpStatus().leftContext = StringView();
state.resolveCallee()->codeBlock()->context()->regexpStatus().rightContext = StringView();
for (size_t i = 0; i < 9; i++) {
state.resolveCallee()->codeBlock()->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 is_enabled)
{
m_legacyFeaturesEnabled = is_enabled;
}

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
Loading

0 comments on commit fe415e5

Please sign in to comment.