bool RegExpObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool shouldThrow) { if (propertyName == exec->propertyNames().lastIndex) { RegExpObject* regExp = asRegExpObject(object); if (descriptor.configurablePresent() && descriptor.configurable()) return reject(exec, shouldThrow, "Attempting to change configurable attribute of unconfigurable property."); if (descriptor.enumerablePresent() && descriptor.enumerable()) return reject(exec, shouldThrow, "Attempting to change enumerable attribute of unconfigurable property."); if (descriptor.isAccessorDescriptor()) return reject(exec, shouldThrow, "Attempting to change access mechanism for an unconfigurable property."); if (!regExp->m_lastIndexIsWritable) { if (descriptor.writablePresent() && descriptor.writable()) return reject(exec, shouldThrow, "Attempting to change writable attribute of unconfigurable property."); if (!sameValue(exec, regExp->getLastIndex(), descriptor.value())) return reject(exec, shouldThrow, "Attempting to change value of a readonly property."); return true; } if (descriptor.writablePresent() && !descriptor.writable()) regExp->m_lastIndexIsWritable = false; if (descriptor.value()) regExp->setLastIndex(exec, descriptor.value(), false); return true; } return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow); }
bool RegExpObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow) { VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); if (propertyName == vm.propertyNames->lastIndex) { RegExpObject* regExp = asRegExpObject(object); if (descriptor.configurablePresent() && descriptor.configurable()) return typeError(exec, scope, shouldThrow, ASCIILiteral(UnconfigurablePropertyChangeConfigurabilityError)); if (descriptor.enumerablePresent() && descriptor.enumerable()) return typeError(exec, scope, shouldThrow, ASCIILiteral(UnconfigurablePropertyChangeEnumerabilityError)); if (descriptor.isAccessorDescriptor()) return typeError(exec, scope, shouldThrow, ASCIILiteral(UnconfigurablePropertyChangeAccessMechanismError)); if (!regExp->m_lastIndexIsWritable) { if (descriptor.writablePresent() && descriptor.writable()) return typeError(exec, scope, shouldThrow, ASCIILiteral(UnconfigurablePropertyChangeWritabilityError)); if (!sameValue(exec, regExp->getLastIndex(), descriptor.value())) return typeError(exec, scope, shouldThrow, ASCIILiteral(ReadonlyPropertyChangeError)); return true; } if (descriptor.value()) { regExp->setLastIndex(exec, descriptor.value(), false); RETURN_IF_EXCEPTION(scope, false); } if (descriptor.writablePresent() && !descriptor.writable()) regExp->m_lastIndexIsWritable = false; return true; } scope.release(); return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow); }
bool js::XDRScriptRegExpObject(XDRState<mode> *xdr, HeapPtrObject *objp) { /* NB: Keep this in sync with CloneScriptRegExpObject. */ RootedAtom source(xdr->cx()); uint32_t flagsword = 0; if (mode == XDR_ENCODE) { JS_ASSERT(objp); RegExpObject &reobj = (*objp)->asRegExp(); source = reobj.getSource(); flagsword = reobj.getFlags(); } if (!XDRAtom(xdr, source.address()) || !xdr->codeUint32(&flagsword)) return false; if (mode == XDR_DECODE) { RegExpFlag flags = RegExpFlag(flagsword); RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx(), source, flags, NULL); if (!reobj) return false; if (!reobj->clearParent(xdr->cx())) return false; if (!reobj->clearType(xdr->cx())) return false; objp->init(reobj); } return true; }
EncodedJSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState* exec) { JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&RegExpObject::s_info)) { if (thisValue.inherits(&RegExpPrototype::s_info)) return JSValue::encode(jsNontrivialString(exec, "//")); return throwVMTypeError(exec); } RegExpObject* thisObject = asRegExpObject(thisValue); StringRecursionChecker checker(exec, thisObject); if (EncodedJSValue earlyReturnValue = checker.earlyReturnValue()) return earlyReturnValue; char postfix[5] = { '/', 0, 0, 0, 0 }; int index = 1; if (thisObject->get(exec, exec->propertyNames().global).toBoolean(exec)) postfix[index++] = 'g'; if (thisObject->get(exec, exec->propertyNames().ignoreCase).toBoolean(exec)) postfix[index++] = 'i'; if (thisObject->get(exec, exec->propertyNames().multiline).toBoolean(exec)) postfix[index] = 'm'; UString source = thisObject->get(exec, exec->propertyNames().source).toString(exec); // If source is empty, use "/(?:)/" to avoid colliding with comment syntax return JSValue::encode(jsMakeNontrivialString(exec, "/", source.length() ? source : UString("(?:)"), postfix)); }
// this = argv[0] (ignored) // arg1 = argv[1] // argN = argv[argc] Atom RegExpClass::construct(int argc, Atom* argv) { AvmCore* core = this->core(); Stringp pattern; Atom patternAtom = (argc>0) ? argv[1] : undefinedAtom; Atom optionsAtom = (argc>1) ? argv[2] : undefinedAtom; if (AvmCore::istype(patternAtom, traits()->itraits)) { // Pattern is a RegExp object if (optionsAtom != undefinedAtom) { // ECMA 15.10.4.1 says to throw an error if flags specified toplevel()->throwTypeError(kRegExpFlagsArgumentError); } // Return a clone of the RegExp object RegExpObject* regExpObject = (RegExpObject*)AvmCore::atomToScriptObject(patternAtom); return (new (core->GetGC(), ivtable()->getExtraSize()) RegExpObject(regExpObject))->atom(); } else { if (patternAtom != undefinedAtom) { pattern = core->string(argv[1]); } else { // cn: disable this, breaking ecma3 tests. was: todo look into this. it's what SpiderMonkey does. pattern = core->kEmptyString; //core->newConstantStringLatin1("(?:)"); } } Stringp options = NULL; if (optionsAtom != undefinedAtom) { options = core->string(optionsAtom); } RegExpObject* inst = new (core->GetGC(), ivtable()->getExtraSize()) RegExpObject(this, pattern, options); return inst->atom(); }
bool RegExpObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) { if (propertyName == exec->propertyNames().lastIndex) { RegExpObject* regExp = asRegExpObject(object); slot.setValue(regExp, regExp->getLastIndex()); return true; } return getStaticValueSlot<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), jsCast<RegExpObject*>(object), propertyName, slot); }
bool RegExpObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor) { if (propertyName == exec->propertyNames().lastIndex) { RegExpObject* regExp = asRegExpObject(object); descriptor.setDescriptor(regExp->getLastIndex(), regExp->m_lastIndexIsWritable ? DontDelete | DontEnum : DontDelete | DontEnum | ReadOnly); return true; } return getStaticValueDescriptor<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), jsCast<RegExpObject*>(object), propertyName, descriptor); }
bool RegExpObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) { if (propertyName == exec->propertyNames().lastIndex) { RegExpObject* regExp = asRegExpObject(object); unsigned attributes = regExp->m_lastIndexIsWritable ? DontDelete | DontEnum : DontDelete | DontEnum | ReadOnly; slot.setValue(regExp, attributes, regExp->getLastIndex()); return true; } return Base::getOwnPropertySlot(object, exec, propertyName, slot); }
ArrayObject* StringClass::_split(Stringp in, Atom delimAtom, uint32 limit) { AvmCore* core = this->core(); if (limit == 0) return toplevel()->arrayClass->newArray(); if (in->length() == 0) { ArrayObject* out = toplevel()->arrayClass->newArray(); out->setUintProperty(0,in->atom()); return out; } // handle RegExp case if (AvmCore::istype(delimAtom, core->traits.regexp_itraits)) { RegExpObject *reObj = (RegExpObject*) AvmCore::atomToScriptObject(delimAtom); return reObj->split(in, limit); } ArrayObject *out = toplevel()->arrayClass->newArray(); Stringp delim = core->string(delimAtom); int ilen = in->length(); int dlen = delim->length(); int count = 0; if (dlen <= 0) { // delim is empty string, split on each char for (int i = 0; i < ilen && (unsigned)i < limit; i++) { Stringp sub = in->substr(i, 1); out->setUintProperty(count++, sub->atom()); } return out; } int32_t start = 0; while (start <= in->length()) { if ((limit != 0xFFFFFFFFUL) && (count >= (int) limit)) break; int32_t bgn = in->indexOf(delim, start); if (bgn < 0) // not found, use the string remainder bgn = in->length(); Stringp sub = in->substring(start, bgn); out->setUintProperty(count++, sub->atom()); start = bgn + delim->length(); } return out; }
void RegExpObject::visitChildren(JSCell* cell, SlotVisitor& visitor) { RegExpObject* thisObject = jsCast<RegExpObject*>(cell); ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); Base::visitChildren(thisObject, visitor); visitor.append(&thisObject->m_regExp); visitor.append(&thisObject->m_lastIndex); }
RegExpObject* js::RegExpAlloc(ExclusiveContext* cx, HandleObject proto /* = nullptr */) { // Note: RegExp objects are always allocated in the tenured heap. This is // not strictly required, but simplifies embedding them in jitcode. RegExpObject* regexp = NewObjectWithClassProto<RegExpObject>(cx, proto, TenuredObject); if (!regexp) return nullptr; regexp->initPrivate(nullptr); return regexp; }
void RegExpObject::visitChildren(JSCell* cell, SlotVisitor& visitor) { RegExpObject* thisObject = static_cast<RegExpObject*>(cell); ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); Base::visitChildren(thisObject, visitor); if (thisObject->d->regExp) visitor.append(&thisObject->d->regExp); if (UNLIKELY(!thisObject->d->lastIndex.get().isInt32())) visitor.append(&thisObject->d->lastIndex); }
JSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { UString s = thisValue.toThisString(exec); JSValue a0 = args.at(0); UString u = s; RefPtr<RegExp> reg; RegExpObject* imp = 0; if (a0.isObject(&RegExpObject::info)) reg = asRegExpObject(a0)->regExp(); else { /* * ECMA 15.5.4.12 String.prototype.search (regexp) * If regexp is not an object whose [[Class]] property is "RegExp", it is * replaced with the result of the expression new RegExp(regexp). */ reg = RegExp::create(&exec->globalData(), a0.toString(exec)); } RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); int pos; int matchLength; regExpConstructor->performMatch(reg.get(), u, 0, pos, matchLength); if (!(reg->global())) { // case without 'g' flag is handled like RegExp.prototype.exec if (pos < 0) return jsNull(); return regExpConstructor->arrayOfMatches(exec); } // return array of matches MarkedArgumentBuffer list; int lastIndex = 0; while (pos >= 0) { list.append(jsSubstring(exec, u, pos, matchLength)); lastIndex = pos; pos += matchLength == 0 ? 1 : matchLength; regExpConstructor->performMatch(reg.get(), u, pos, pos, matchLength); } if (imp) imp->setLastIndex(lastIndex); if (list.isEmpty()) { // if there are no matches at all, it's important to return // Null instead of an empty array, because this matches // other browsers and because Null is a false value. return jsNull(); } return constructArray(exec, list); }
JSObject * js::CloneScriptRegExpObject(JSContext *cx, RegExpObject &reobj) { /* NB: Keep this in sync with XDRScriptRegExpObject. */ RootedAtom source(cx, reobj.getSource()); RegExpObject *clone = RegExpObject::createNoStatics(cx, source, reobj.getFlags(), NULL); if (!clone) return NULL; if (!clone->clearParent(cx)) return NULL; if (!clone->clearType(cx)) return NULL; return clone; }
static JSBool regexp_compile(JSContext *cx, uintN argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); bool ok; JSObject *obj = NonGenericMethodGuard(cx, args, regexp_compile, &RegExpClass, &ok); if (!obj) return ok; RegExpObject *reobj = obj->asRegExp(); ok = CompileRegExpObject(cx, reobj, args.length(), args.array(), &args.rval()); JS_ASSERT_IF(ok, reobj->getPrivate()); return ok; }
int StringClass::_search(Stringp in, Atom regexpAtom) { AvmCore* core = this->core(); if (!AvmCore::istype(regexpAtom, core->traits.regexp_itraits)) { // ECMA-262 15.5.4.10 // If the argument is not a RegExp, invoke RegExp(exp) regexpAtom = core->newRegExp(toplevel()->regexpClass(), core->string(regexpAtom), core->kEmptyString)->atom(); } RegExpObject *reObj = (RegExpObject*) AvmCore::atomToScriptObject(regexpAtom); return reObj->search(in); }
Stringp StringClass::_replace(Stringp subject, Atom pattern, Atom replacementAtom) { AvmCore* core = this->core(); ScriptObject *replaceFunction = NULL; Stringp replacement = NULL; if (AvmCore::istype(replacementAtom, core->traits.function_itraits)) { replaceFunction = AvmCore::atomToScriptObject(replacementAtom); } else { replacement = core->string(replacementAtom); } if (AvmCore::istype(pattern, core->traits.regexp_itraits)) { // RegExp mode RegExpObject *reObj = (RegExpObject*) core->atomToScriptObject(pattern); if (replaceFunction) { return core->string(reObj->replace(subject, replaceFunction)); } else { return core->string(reObj->replace(subject, replacement)); } } else { // String replace mode Stringp searchString = core->string(pattern); int index = subject->indexOf(searchString); if (index == -1) { // Search string not found; return input unchanged. return subject; } if (replaceFunction) { // Invoke the replacement function to figure out the // replacement string Atom argv[4] = { undefinedAtom, searchString->atom(), core->uintToAtom(index), subject->atom() }; replacement = core->string(toplevel()->op_call(replaceFunction->atom(), 3, argv)); } Stringp out = subject->substring(0, index); out = String::concatStrings(out, replacement); out = String::concatStrings(out, subject->substring(index + searchString->length(), subject->length())); return out; } }
/* Legacy ExecuteRegExp behavior is baked into the JSAPI. */ bool js::ExecuteRegExpLegacy(JSContext *cx, RegExpStatics *res, RegExpObject &reobj, Handle<JSLinearString*> input, const jschar *chars, size_t length, size_t *lastIndex, bool test, MutableHandleValue rval) { RegExpGuard shared(cx); if (!reobj.getShared(cx, &shared)) return false; ScopedMatchPairs matches(&cx->tempLifoAlloc()); MatchConduit conduit(&matches); RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *shared, input, chars, length, lastIndex, conduit); if (status == RegExpRunStatus_Error) return false; if (status == RegExpRunStatus_Success_NotFound) { /* ExecuteRegExp() previously returned an array or null. */ rval.setNull(); return true; } if (test) { /* Forbid an array, as an optimization. */ rval.setBoolean(true); return true; } return CreateRegExpMatchResult(cx, input, chars, length, matches, rval); }
EncodedJSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState* exec) { JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&RegExpObject::s_info)) return throwVMTypeError(exec); JSValue arg = exec->argument(0); RegExpObject* reg = asRegExpObject(thisValue); JSValue result = jsBoolean(reg->test(exec, arg.toString(exec))); #ifdef ARTEMIS if (arg.isSymbolic()) { result.makeSymbolic(new Symbolic::StringRegexSubmatch((Symbolic::StringExpression*)arg.asSymbolic(), new std::string(reg->regExp()->pattern().ascii().data())), exec->globalData()); } #endif return JSValue::encode(result); }
JSBool js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp) { JSString *source = 0; uint32 flagsword = 0; if (xdr->mode == JSXDR_ENCODE) { JS_ASSERT(objp); RegExpObject *reobj = (*objp)->asRegExp(); source = reobj->getSource(); flagsword = reobj->getFlags(); } if (!JS_XDRString(xdr, &source) || !JS_XDRUint32(xdr, &flagsword)) return false; if (xdr->mode == JSXDR_DECODE) { JSAtom *atom = js_AtomizeString(xdr->cx, source); if (!atom) return false; RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx, atom, RegExpFlag(flagsword), NULL); if (!reobj) return false; if (!reobj->clearParent(xdr->cx)) return false; if (!reobj->clearType(xdr->cx)) return false; *objp = reobj; } return true; }
bool dumpIfTerminal(JSValue value) { if (!value.isCell()) { dumpImmediate(value); return true; } if (value.isString()) { UString str = asString(value)->value(m_exec); dumpString(str); return true; } if (value.isNumber()) { write(DoubleTag); write(value.asNumber()); return true; } if (value.isObject() && asObject(value)->inherits(&DateInstance::s_info)) { write(DateTag); write(asDateInstance(value)->internalNumber()); return true; } if (isArray(value)) return false; // Object cannot be serialized because the act of walking the object creates new objects if (value.isObject() && asObject(value)->inherits(&JSNavigator::s_info)) { fail(); write(NullTag); return true; } if (value.isObject()) { JSObject* obj = asObject(value); if (obj->inherits(&JSFile::s_info)) { write(FileTag); write(toFile(obj)); return true; } if (obj->inherits(&JSFileList::s_info)) { FileList* list = toFileList(obj); write(FileListTag); unsigned length = list->length(); write(length); for (unsigned i = 0; i < length; i++) write(list->item(i)); return true; } if (obj->inherits(&JSBlob::s_info)) { write(BlobTag); Blob* blob = toBlob(obj); write(blob->url()); write(blob->type()); write(blob->size()); return true; } if (obj->inherits(&JSImageData::s_info)) { ImageData* data = toImageData(obj); write(ImageDataTag); write(data->width()); write(data->height()); write(data->data()->length()); write(data->data()->data()->data(), data->data()->length()); return true; } if (obj->inherits(&RegExpObject::s_info)) { RegExpObject* regExp = asRegExpObject(obj); char flags[3]; int flagCount = 0; if (regExp->regExp()->global()) flags[flagCount++] = 'g'; if (regExp->regExp()->ignoreCase()) flags[flagCount++] = 'i'; if (regExp->regExp()->multiline()) flags[flagCount++] = 'm'; write(RegExpTag); write(regExp->regExp()->pattern()); write(UString(flags, flagCount)); return true; } if (obj->inherits(&JSMessagePort::s_info)) { ObjectPool::iterator index = m_transferredMessagePorts.find(obj); if (index != m_transferredMessagePorts.end()) { write(MessagePortReferenceTag); uint32_t i = index->second; write(i); return true; } return false; } CallData unusedData; if (getCallData(value, unusedData) == CallTypeNone) return false; } // Any other types are expected to serialize as null. write(NullTag); return true; }