diff options
Diffstat (limited to 'Source/JavaScriptCore/runtime')
81 files changed, 1916 insertions, 1421 deletions
diff --git a/Source/JavaScriptCore/runtime/Arguments.cpp b/Source/JavaScriptCore/runtime/Arguments.cpp index 7a53ec1a4..9a3d7257b 100644 --- a/Source/JavaScriptCore/runtime/Arguments.cpp +++ b/Source/JavaScriptCore/runtime/Arguments.cpp @@ -306,6 +306,10 @@ bool Arguments::defineOwnProperty(JSObject* object, ExecState* exec, const Ident bool isArrayIndex; unsigned i = propertyName.toArrayIndex(isArrayIndex); if (isArrayIndex && i < thisObject->d->numArguments) { + // If the property is not yet present on the object, and is not yet marked as deleted, then add it now. + PropertySlot slot; + if ((!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i]) && !JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot)) + object->putDirect(exec->globalData(), propertyName, thisObject->argument(i).get(), 0); if (!Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow)) return false; @@ -331,35 +335,16 @@ bool Arguments::defineOwnProperty(JSObject* object, ExecState* exec, const Ident thisObject->d->deletedArguments[i] = true; } } - return true; } if (propertyName == exec->propertyNames().length && !thisObject->d->overrodeLength) { + thisObject->putDirect(exec->globalData(), propertyName, jsNumber(thisObject->d->numArguments), DontEnum); thisObject->d->overrodeLength = true; - if (!descriptor.isAccessorDescriptor()) { - if (!descriptor.value()) - descriptor.setValue(jsNumber(thisObject->d->numArguments)); - if (!descriptor.configurablePresent()) - descriptor.setConfigurable(true); - } - if (!descriptor.configurablePresent()) - descriptor.setConfigurable(true); - } - - if (propertyName == exec->propertyNames().callee && !thisObject->d->overrodeCallee) { + } else if (propertyName == exec->propertyNames().callee && !thisObject->d->overrodeCallee) { + thisObject->putDirect(exec->globalData(), propertyName, thisObject->d->callee.get(), DontEnum); thisObject->d->overrodeCallee = true; - if (!descriptor.isAccessorDescriptor()) { - if (!descriptor.value()) - descriptor.setValue(thisObject->d->callee.get()); - if (!descriptor.configurablePresent()) - descriptor.setConfigurable(true); - } - if (!descriptor.configurablePresent()) - descriptor.setConfigurable(true); - } - - if (propertyName == exec->propertyNames().caller && thisObject->d->isStrictMode) + } else if (propertyName == exec->propertyNames().caller && thisObject->d->isStrictMode) thisObject->createStrictModeCallerIfNecessary(exec); return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow); diff --git a/Source/JavaScriptCore/runtime/Arguments.h b/Source/JavaScriptCore/runtime/Arguments.h index 8e7af1844..a1f36de56 100644 --- a/Source/JavaScriptCore/runtime/Arguments.h +++ b/Source/JavaScriptCore/runtime/Arguments.h @@ -158,7 +158,7 @@ namespace JSC { Base::finishCreation(callFrame->globalData()); ASSERT(inherits(&s_info)); - JSFunction* callee = asFunction(callFrame->callee()); + JSFunction* callee = jsCast<JSFunction*>(callFrame->callee()); d->numArguments = callFrame->argumentCount(); d->registers = reinterpret_cast<WriteBarrier<Unknown>*>(callFrame->registers()); d->callee.set(callFrame->globalData(), this, callee); diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp index 2f000fc74..6df58b773 100644 --- a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -29,6 +29,7 @@ #include "Interpreter.h" #include "JIT.h" #include "JSStringBuilder.h" +#include "JSStringJoiner.h" #include "Lookup.h" #include "ObjectPrototype.h" #include "Operations.h" @@ -254,9 +255,27 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec) { JSValue thisValue = exec->hostThisValue(); - bool isRealArray = isJSArray(thisValue); - if (!isRealArray && !thisValue.inherits(&JSArray::s_info)) - return throwVMTypeError(exec); + // 1. Let array be the result of calling ToObject on the this value. + JSObject* thisObject = thisValue.toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + // 2. Let func be the result of calling the [[Get]] internal method of array with argument "join". + JSValue function = JSValue(thisObject).get(exec, exec->propertyNames().join); + + // 3. If IsCallable(func) is false, then let func be the standard built-in method Object.prototype.toString (15.2.4.2). + if (!function.isCell()) + return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable()->className(thisObject), "]")); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable()->className(thisObject), "]")); + + // 4. Return the result of calling the [[Call]] internal method of func providing array as the this value and an empty arguments list. + if (!isJSArray(thisObject) || callType != CallTypeHost || callData.native.function != arrayProtoFuncJoin) + return JSValue::encode(call(exec, function, callType, callData, thisObject, exec->emptyList())); + + ASSERT(isJSArray(thisValue)); JSArray* thisObj = asArray(thisValue); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); @@ -273,7 +292,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec) for (unsigned k = 0; k < length; k++) { JSValue element; - if (isRealArray && thisObj->canGetIndex(k)) + if (thisObj->canGetIndex(k)) element = thisObj->getIndex(k); else element = thisObj->get(exec, k); @@ -281,7 +300,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec) if (element.isUndefinedOrNull()) continue; - UString str = element.toString(exec)->value(exec); + UString str = element.toUString(exec); strBuffer[k] = str.impl(); totalSize += str.length(); allStrings8Bit = allStrings8Bit && str.is8Bit(); @@ -343,11 +362,9 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec) if (JSValue earlyReturnValue = checker.earlyReturnValue()) return JSValue::encode(earlyReturnValue); - JSStringBuilder strBuffer; + UString separator(","); + JSStringJoiner stringJoiner(separator, length); for (unsigned k = 0; k < length; k++) { - if (k >= 1) - strBuffer.append(','); - JSValue element = thisObj->get(exec, k); if (exec->hadException()) return JSValue::encode(jsUndefined()); @@ -360,16 +377,16 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec) CallData callData; CallType callType = getCallData(conversionFunction, callData); if (callType != CallTypeNone) - str = call(exec, conversionFunction, callType, callData, element, exec->emptyList()).toString(exec)->value(exec); + str = call(exec, conversionFunction, callType, callData, element, exec->emptyList()).toUString(exec); else - str = element.toString(exec)->value(exec); + str = element.toUString(exec); if (exec->hadException()) return JSValue::encode(jsUndefined()); - strBuffer.append(str); + stringJoiner.append(str); } } - return JSValue::encode(strBuffer.build(exec)); + return JSValue::encode(stringJoiner.build(exec)); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec) @@ -383,60 +400,39 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec) if (JSValue earlyReturnValue = checker.earlyReturnValue()) return JSValue::encode(earlyReturnValue); - JSStringBuilder strBuffer; - UString separator; if (!exec->argument(0).isUndefined()) - separator = exec->argument(0).toString(exec)->value(exec); + separator = exec->argument(0).toUString(exec); + if (separator.isNull()) + separator = UString(","); + + JSStringJoiner stringJoiner(separator, length); unsigned k = 0; if (isJSArray(thisObj)) { JSArray* array = asArray(thisObj); - if (length) { - if (!array->canGetIndex(k)) - goto skipFirstLoop; + for (; k < length; k++) { + if (!array->canGetIndex(k)) + break; + JSValue element = array->getIndex(k); if (!element.isUndefinedOrNull()) - strBuffer.append(element.toString(exec)->value(exec)); - k++; - } - - if (separator.isNull()) { - for (; k < length; k++) { - if (!array->canGetIndex(k)) - break; - strBuffer.append(','); - JSValue element = array->getIndex(k); - if (!element.isUndefinedOrNull()) - strBuffer.append(element.toString(exec)->value(exec)); - } - } else { - for (; k < length; k++) { - if (!array->canGetIndex(k)) - break; - strBuffer.append(separator); - JSValue element = array->getIndex(k); - if (!element.isUndefinedOrNull()) - strBuffer.append(element.toString(exec)->value(exec)); - } - } - } - skipFirstLoop: - for (; k < length; k++) { - if (k >= 1) { - if (separator.isNull()) - strBuffer.append(','); + stringJoiner.append(element.toUStringInline(exec)); else - strBuffer.append(separator); + stringJoiner.append(UString()); } + } + for (; k < length; k++) { JSValue element = thisObj->get(exec, k); if (!element.isUndefinedOrNull()) - strBuffer.append(element.toString(exec)->value(exec)); + stringJoiner.append(element.toUStringInline(exec)); + else + stringJoiner.append(UString()); } - return JSValue::encode(strBuffer.build(exec)); + return JSValue::encode(stringJoiner.build(exec)); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec) @@ -524,7 +520,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec) thisObj->methodTable()->putByIndex(thisObj, exec, length + n, exec->argument(n), true); else { PutPropertySlot slot; - Identifier propertyName(exec, JSValue(static_cast<int64_t>(length) + static_cast<int64_t>(n)).toString(exec)->value(exec)); + Identifier propertyName(exec, JSValue(static_cast<int64_t>(length) + static_cast<int64_t>(n)).toUString(exec)); thisObj->methodTable()->put(thisObj, exec, propertyName, exec->argument(n), slot); } if (exec->hadException()) @@ -665,7 +661,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec) l.append(minObj); compareResult = call(exec, function, callType, callData, jsUndefined(), l).toNumber(exec); } else - compareResult = (jObj.toString(exec)->value(exec) < minObj.toString(exec)->value(exec)) ? -1 : 1; + compareResult = (jObj.toUStringInline(exec) < minObj.toUStringInline(exec)) ? -1 : 1; if (compareResult < 0) { themin = j; @@ -788,7 +784,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec) unsigned filterIndex = 0; unsigned k = 0; if (callType == CallTypeJS && isJSArray(thisObj)) { - JSFunction* f = asFunction(function); + JSFunction* f = jsCast<JSFunction*>(function); JSArray* array = asArray(thisObj); CachedCall cachedCall(exec, f, 3); for (; k < length && !exec->hadException(); ++k) { @@ -846,7 +842,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec) JSArray* resultArray = constructEmptyArray(exec, length); unsigned k = 0; if (callType == CallTypeJS && isJSArray(thisObj)) { - JSFunction* f = asFunction(function); + JSFunction* f = jsCast<JSFunction*>(function); JSArray* array = asArray(thisObj); CachedCall cachedCall(exec, f, 3); for (; k < length && !exec->hadException(); ++k) { @@ -909,7 +905,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec) unsigned k = 0; if (callType == CallTypeJS && isJSArray(thisObj)) { - JSFunction* f = asFunction(function); + JSFunction* f = jsCast<JSFunction*>(function); JSArray* array = asArray(thisObj); CachedCall cachedCall(exec, f, 3); for (; k < length && !exec->hadException(); ++k) { @@ -965,7 +961,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState* exec) unsigned k = 0; if (callType == CallTypeJS && isJSArray(thisObj)) { - JSFunction* f = asFunction(function); + JSFunction* f = jsCast<JSFunction*>(function); JSArray* array = asArray(thisObj); CachedCall cachedCall(exec, f, 3); for (; k < length && !exec->hadException(); ++k) { @@ -1017,7 +1013,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec) unsigned k = 0; if (callType == CallTypeJS && isJSArray(thisObj)) { - JSFunction* f = asFunction(function); + JSFunction* f = jsCast<JSFunction*>(function); JSArray* array = asArray(thisObj); CachedCall cachedCall(exec, f, 3); for (; k < length && !exec->hadException(); ++k) { @@ -1096,7 +1092,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState* exec) } if (callType == CallTypeJS && array) { - CachedCall cachedCall(exec, asFunction(function), 4); + CachedCall cachedCall(exec, jsCast<JSFunction*>(function), 4); for (; i < length && !exec->hadException(); ++i) { cachedCall.setThis(jsUndefined()); cachedCall.setArgument(0, rv); @@ -1173,7 +1169,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec) } if (callType == CallTypeJS && array) { - CachedCall cachedCall(exec, asFunction(function), 4); + CachedCall cachedCall(exec, jsCast<JSFunction*>(function), 4); for (; i < length && !exec->hadException(); ++i) { unsigned idx = length - i - 1; cachedCall.setThis(jsUndefined()); diff --git a/Source/JavaScriptCore/runtime/ClassInfo.h b/Source/JavaScriptCore/runtime/ClassInfo.h index 214258cc6..eb4a6f9cd 100644 --- a/Source/JavaScriptCore/runtime/ClassInfo.h +++ b/Source/JavaScriptCore/runtime/ClassInfo.h @@ -131,7 +131,6 @@ struct MemberCheck##member { \ &ClassName::defineOwnProperty, \ &ClassName::getOwnPropertyDescriptor, \ }, \ - sizeof(ClassName), \ ClassName::TypedArrayStorageType struct ClassInfo { @@ -180,8 +179,6 @@ struct MemberCheck##member { \ MethodTable methodTable; - size_t cellSize; - TypedArrayType typedArrayStorageType; }; diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.h b/Source/JavaScriptCore/runtime/CommonIdentifiers.h index 0d9580197..e15335ef0 100644 --- a/Source/JavaScriptCore/runtime/CommonIdentifiers.h +++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.h @@ -73,10 +73,11 @@ macro(valueOf) \ macro(writable) \ macro(displayName) \ - macro(undefined) + macro(join) #define JSC_COMMON_IDENTIFIERS_EACH_KEYWORD(macro) \ macro(null) \ + macro(undefined) \ macro(true) \ macro(false) \ macro(break) \ diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.h b/Source/JavaScriptCore/runtime/CommonSlowPaths.h index 345af2ebe..c41ced7ee 100644 --- a/Source/JavaScriptCore/runtime/CommonSlowPaths.h +++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.h @@ -44,7 +44,7 @@ namespace CommonSlowPaths { ALWAYS_INLINE ExecState* arityCheckFor(ExecState* exec, RegisterFile* registerFile, CodeSpecializationKind kind) { - JSFunction* callee = asFunction(exec->callee()); + JSFunction* callee = jsCast<JSFunction*>(exec->callee()); ASSERT(!callee->isHostFunction()); CodeBlock* newCodeBlock = &callee->jsExecutable()->generatedBytecodeFor(kind); int argumentCountIncludingThis = exec->argumentCountIncludingThis(); diff --git a/Source/JavaScriptCore/runtime/DateConstructor.cpp b/Source/JavaScriptCore/runtime/DateConstructor.cpp index 365172294..66f0baea5 100644 --- a/Source/JavaScriptCore/runtime/DateConstructor.cpp +++ b/Source/JavaScriptCore/runtime/DateConstructor.cpp @@ -166,10 +166,8 @@ ConstructType DateConstructor::getConstructData(JSCell*, ConstructData& construc // ECMA 15.9.2 static EncodedJSValue JSC_HOST_CALL callDate(ExecState* exec) { - time_t localTime = time(0); - tm localTM; - getLocalTime(&localTime, &localTM); - GregorianDateTime ts(exec, localTM); + GregorianDateTime ts; + msToGregorianDateTime(exec, currentTimeMS(), false, ts); DateConversionBuffer date; DateConversionBuffer time; formatDate(ts, date); diff --git a/Source/JavaScriptCore/runtime/Error.cpp b/Source/JavaScriptCore/runtime/Error.cpp index 5266c1ebe..bae07448b 100644 --- a/Source/JavaScriptCore/runtime/Error.cpp +++ b/Source/JavaScriptCore/runtime/Error.cpp @@ -79,6 +79,11 @@ JSObject* createTypeError(JSGlobalObject* globalObject, const UString& message) return ErrorInstance::create(globalObject->globalData(), globalObject->typeErrorConstructor()->errorStructure(), message); } +JSObject* createNotEnoughArgumentsError(JSGlobalObject* globalObject) +{ + return createTypeError(globalObject, "Not enough arguments"); +} + JSObject* createURIError(JSGlobalObject* globalObject, const UString& message) { ASSERT(!message.isEmpty()); @@ -115,42 +120,31 @@ JSObject* createTypeError(ExecState* exec, const UString& message) return createTypeError(exec->lexicalGlobalObject(), message); } +JSObject* createNotEnoughArgumentsError(ExecState* exec) +{ + return createNotEnoughArgumentsError(exec->lexicalGlobalObject()); +} + JSObject* createURIError(ExecState* exec, const UString& message) { return createURIError(exec->lexicalGlobalObject(), message); } -JSObject* addErrorInfo(JSGlobalData* globalData, JSObject* error, int line, const SourceCode& source, const Vector<StackFrame>& stackTrace) +JSObject* addErrorInfo(CallFrame* callFrame, JSObject* error, int line, const SourceCode& source) { + JSGlobalData* globalData = &callFrame->globalData(); const UString& sourceURL = source.provider()->url(); if (line != -1) error->putDirect(*globalData, Identifier(globalData, linePropertyName), jsNumber(line), ReadOnly | DontDelete); if (!sourceURL.isNull()) error->putDirect(*globalData, Identifier(globalData, sourceURLPropertyName), jsString(globalData, sourceURL), ReadOnly | DontDelete); - if (!stackTrace.isEmpty()) { - JSGlobalObject* globalObject = 0; - if (isTerminatedExecutionException(error) || isInterruptedExecutionException(error)) - globalObject = globalData->dynamicGlobalObject; - else - globalObject = error->globalObject(); - StringBuilder builder; - for (unsigned i = 0; i < stackTrace.size(); i++) { - builder.append(String(stackTrace[i].toString(globalObject->globalExec()).impl())); - if (i != stackTrace.size() - 1) - builder.append('\n'); - } - - error->putDirect(*globalData, globalData->propertyNames->stack, jsString(globalData, UString(builder.toString().impl())), ReadOnly | DontDelete); - } + + globalData->interpreter->addStackTraceIfNecessary(callFrame, error); return error; } -JSObject* addErrorInfo(ExecState* exec, JSObject* error, int line, const SourceCode& source, const Vector<StackFrame>& stackTrace) -{ - return addErrorInfo(&exec->globalData(), error, line, source, stackTrace); -} bool hasErrorInfo(ExecState* exec, JSObject* error) { @@ -160,12 +154,15 @@ bool hasErrorInfo(ExecState* exec, JSObject* error) JSValue throwError(ExecState* exec, JSValue error) { + if (error.isObject()) + return throwError(exec, asObject(error)); exec->globalData().exception = error; return error; } JSObject* throwError(ExecState* exec, JSObject* error) { + Interpreter::addStackTraceIfNecessary(exec, error); exec->globalData().exception = error; return error; } diff --git a/Source/JavaScriptCore/runtime/Error.h b/Source/JavaScriptCore/runtime/Error.h index 59b39495f..79617655e 100644 --- a/Source/JavaScriptCore/runtime/Error.h +++ b/Source/JavaScriptCore/runtime/Error.h @@ -45,6 +45,7 @@ namespace JSC { JSObject* createReferenceError(JSGlobalObject*, const UString&); JSObject* createSyntaxError(JSGlobalObject*, const UString&); JSObject* createTypeError(JSGlobalObject*, const UString&); + JSObject* createNotEnoughArgumentsError(JSGlobalObject*); JSObject* createURIError(JSGlobalObject*, const UString&); // ExecState wrappers. JS_EXPORT_PRIVATE JSObject* createError(ExecState*, const UString&); @@ -53,13 +54,13 @@ namespace JSC { JS_EXPORT_PRIVATE JSObject* createReferenceError(ExecState*, const UString&); JS_EXPORT_PRIVATE JSObject* createSyntaxError(ExecState*, const UString&); JS_EXPORT_PRIVATE JSObject* createTypeError(ExecState*, const UString&); + JS_EXPORT_PRIVATE JSObject* createNotEnoughArgumentsError(ExecState*); JSObject* createURIError(ExecState*, const UString&); // Methods to add bool hasErrorInfo(ExecState*, JSObject* error); - JSObject* addErrorInfo(JSGlobalData*, JSObject* error, int line, const SourceCode&, const Vector<StackFrame>&); // ExecState wrappers. - JSObject* addErrorInfo(ExecState*, JSObject* error, int line, const SourceCode&, const Vector<StackFrame>&); + JSObject* addErrorInfo(ExecState*, JSObject* error, int line, const SourceCode&); // Methods to throw Errors. JS_EXPORT_PRIVATE JSValue throwError(ExecState*, JSValue); diff --git a/Source/JavaScriptCore/runtime/Executable.cpp b/Source/JavaScriptCore/runtime/Executable.cpp index ea40447e4..934533c46 100644 --- a/Source/JavaScriptCore/runtime/Executable.cpp +++ b/Source/JavaScriptCore/runtime/Executable.cpp @@ -146,6 +146,8 @@ FunctionExecutable::FunctionExecutable(JSGlobalData& globalData, const Identifie , m_name(name) , m_inferredName(inferredName.isNull() ? globalData.propertyNames->emptyIdentifier : inferredName) , m_symbolTable(0) + , m_next(0) + , m_prev(0) { } @@ -157,6 +159,8 @@ FunctionExecutable::FunctionExecutable(ExecState* exec, const Identifier& name, , m_name(name) , m_inferredName(inferredName.isNull() ? exec->globalData().propertyNames->emptyIdentifier : inferredName) , m_symbolTable(0) + , m_next(0) + , m_prev(0) { } @@ -654,7 +658,9 @@ void FunctionExecutable::discardCode() void FunctionExecutable::finalize(JSCell* cell) { - jsCast<FunctionExecutable*>(cell)->clearCode(); + FunctionExecutable* executable = jsCast<FunctionExecutable*>(cell); + Heap::heap(executable)->removeFunctionExecutable(executable); + executable->clearCode(); } inline void FunctionExecutable::clearCode() diff --git a/Source/JavaScriptCore/runtime/Executable.h b/Source/JavaScriptCore/runtime/Executable.h index 08b39fcf0..3b979ba82 100644 --- a/Source/JavaScriptCore/runtime/Executable.h +++ b/Source/JavaScriptCore/runtime/Executable.h @@ -463,9 +463,10 @@ namespace JSC { OwnPtr<ProgramCodeBlock> m_programCodeBlock; }; - class FunctionExecutable : public ScriptExecutable { + class FunctionExecutable : public ScriptExecutable, public DoublyLinkedListNode<FunctionExecutable> { friend class JIT; friend class LLIntOffsetsExtractor; + friend class WTF::DoublyLinkedListNode<FunctionExecutable>; public: typedef ScriptExecutable Base; @@ -473,6 +474,7 @@ namespace JSC { { FunctionExecutable* executable = new (NotNull, allocateCell<FunctionExecutable>(*exec->heap())) FunctionExecutable(exec, name, inferredName, source, forceUsesArguments, parameters, isInStrictContext); executable->finishCreation(exec->globalData(), name, firstLine, lastLine); + exec->globalData().heap.addFunctionExecutable(executable); exec->globalData().heap.addFinalizer(executable, &finalize); return executable; } @@ -481,6 +483,7 @@ namespace JSC { { FunctionExecutable* executable = new (NotNull, allocateCell<FunctionExecutable>(globalData.heap)) FunctionExecutable(globalData, name, inferredName, source, forceUsesArguments, parameters, isInStrictContext); executable->finishCreation(globalData, name, firstLine, lastLine); + globalData.heap.addFunctionExecutable(executable); globalData.heap.addFinalizer(executable, &finalize); return executable; } @@ -567,7 +570,7 @@ namespace JSC { { ASSERT(exec->callee()); ASSERT(exec->callee()->inherits(&JSFunction::s_info)); - ASSERT(asFunction(exec->callee())->jsExecutable() == this); + ASSERT(jsCast<JSFunction*>(exec->callee())->jsExecutable() == this); if (kind == CodeForCall) return compileForCall(exec, scopeChainNode); @@ -579,7 +582,7 @@ namespace JSC { { ASSERT(exec->callee()); ASSERT(exec->callee()->inherits(&JSFunction::s_info)); - ASSERT(asFunction(exec->callee())->jsExecutable() == this); + ASSERT(jsCast<JSFunction*>(exec->callee())->jsExecutable() == this); if (kind == CodeForCall) return compileOptimizedForCall(exec, scopeChainNode); @@ -688,6 +691,8 @@ namespace JSC { Identifier m_inferredName; WriteBarrier<JSString> m_nameValue; SharedSymbolTable* m_symbolTable; + FunctionExecutable* m_next; + FunctionExecutable* m_prev; }; inline FunctionExecutable* JSFunction::jsExecutable() const @@ -716,7 +721,7 @@ namespace JSC { inline bool isHostFunction(JSValue value, NativeFunction nativeFunction) { - JSFunction* function = static_cast<JSFunction*>(getJSFunction(value)); + JSFunction* function = jsCast<JSFunction*>(getJSFunction(value)); if (!function || !function->isHostFunction()) return false; return function->nativeFunction() == nativeFunction; diff --git a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp index 266ddc241..d341b847b 100644 --- a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp +++ b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp @@ -102,7 +102,7 @@ EncodedJSValue JSC_HOST_CALL functionProtoFuncToString(ExecState* exec) { JSValue thisValue = exec->hostThisValue(); if (thisValue.inherits(&JSFunction::s_info)) { - JSFunction* function = asFunction(thisValue); + JSFunction* function = jsCast<JSFunction*>(thisValue); if (function->isHostFunction()) return JSValue::encode(jsMakeNontrivialString(exec, "function ", function->name(exec), "() {\n [native code]\n}")); FunctionExecutable* executable = function->jsExecutable(); diff --git a/Source/JavaScriptCore/runtime/GCActivityCallback.cpp b/Source/JavaScriptCore/runtime/GCActivityCallback.cpp index 308d245a9..6ec538f72 100644 --- a/Source/JavaScriptCore/runtime/GCActivityCallback.cpp +++ b/Source/JavaScriptCore/runtime/GCActivityCallback.cpp @@ -42,7 +42,11 @@ DefaultGCActivityCallback::~DefaultGCActivityCallback() { } -void DefaultGCActivityCallback::operator()() +void DefaultGCActivityCallback::didAllocate(size_t) +{ +} + +void DefaultGCActivityCallback::willCollect() { } @@ -50,5 +54,9 @@ void DefaultGCActivityCallback::synchronize() { } +void DefaultGCActivityCallback::cancel() +{ +} + } diff --git a/Source/JavaScriptCore/runtime/GCActivityCallback.h b/Source/JavaScriptCore/runtime/GCActivityCallback.h index f40ebee5b..1a18a8b45 100644 --- a/Source/JavaScriptCore/runtime/GCActivityCallback.h +++ b/Source/JavaScriptCore/runtime/GCActivityCallback.h @@ -42,9 +42,11 @@ class Heap; class GCActivityCallback { public: - virtual ~GCActivityCallback() {} - virtual void operator()() {} - virtual void synchronize() {} + virtual ~GCActivityCallback() { } + virtual void didAllocate(size_t) { } + virtual void willCollect() { } + virtual void synchronize() { } + virtual void cancel() { } protected: GCActivityCallback() {} @@ -57,10 +59,12 @@ public: static PassOwnPtr<DefaultGCActivityCallback> create(Heap*); DefaultGCActivityCallback(Heap*); - ~DefaultGCActivityCallback(); + virtual ~DefaultGCActivityCallback(); - void operator()(); - void synchronize(); + virtual void didAllocate(size_t); + virtual void willCollect(); + virtual void synchronize(); + virtual void cancel(); #if USE(CF) protected: diff --git a/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp b/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp index 7f45f0746..8b690a480 100644 --- a/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp +++ b/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp @@ -45,21 +45,34 @@ namespace JSC { struct DefaultGCActivityCallbackPlatformData { - static void trigger(CFRunLoopTimerRef, void *info); + static void timerDidFire(CFRunLoopTimerRef, void *info); RetainPtr<CFRunLoopTimerRef> timer; RetainPtr<CFRunLoopRef> runLoop; CFRunLoopTimerContext context; + double delay; }; +const double gcTimeSlicePerMB = 0.01; // Percentage of CPU time we will spend to reclaim 1 MB +const double maxGCTimeSlice = 0.05; // The maximum amount of CPU time we want to use for opportunistic timer-triggered collections. +const double timerSlop = 2.0; // Fudge factor to avoid performance cost of resetting timer. +const double pagingTimeOut = 0.1; // Time in seconds to allow opportunistic timer to iterate over all blocks to see if the Heap is paged out. const CFTimeInterval decade = 60 * 60 * 24 * 365 * 10; +const CFTimeInterval hour = 60 * 60; -void DefaultGCActivityCallbackPlatformData::trigger(CFRunLoopTimerRef timer, void *info) +void DefaultGCActivityCallbackPlatformData::timerDidFire(CFRunLoopTimerRef, void *info) { Heap* heap = static_cast<Heap*>(info); APIEntryShim shim(heap->globalData()); +#if !PLATFORM(IOS) + double startTime = WTF::monotonicallyIncreasingTime(); + if (heap->isPagedOut(startTime + pagingTimeOut)) { + heap->activityCallback()->cancel(); + heap->increaseLastGCLength(pagingTimeOut); + return; + } +#endif heap->collectAllGarbage(); - CFRunLoopTimerSetNextFireDate(timer, CFAbsoluteTimeGetCurrent() + decade); } DefaultGCActivityCallback::DefaultGCActivityCallback(Heap* heap) @@ -76,9 +89,6 @@ DefaultGCActivityCallback::~DefaultGCActivityCallback() { CFRunLoopRemoveTimer(d->runLoop.get(), d->timer.get(), kCFRunLoopCommonModes); CFRunLoopTimerInvalidate(d->timer.get()); - d->context.info = 0; - d->runLoop = 0; - d->timer = 0; } void DefaultGCActivityCallback::commonConstructor(Heap* heap, CFRunLoopRef runLoop) @@ -88,14 +98,41 @@ void DefaultGCActivityCallback::commonConstructor(Heap* heap, CFRunLoopRef runLo memset(&d->context, 0, sizeof(CFRunLoopTimerContext)); d->context.info = heap; d->runLoop = runLoop; - d->timer.adoptCF(CFRunLoopTimerCreate(0, decade, decade, 0, 0, DefaultGCActivityCallbackPlatformData::trigger, &d->context)); + d->timer.adoptCF(CFRunLoopTimerCreate(0, decade, decade, 0, 0, DefaultGCActivityCallbackPlatformData::timerDidFire, &d->context)); + d->delay = decade; CFRunLoopAddTimer(d->runLoop.get(), d->timer.get(), kCFRunLoopCommonModes); } -void DefaultGCActivityCallback::operator()() +static void scheduleTimer(DefaultGCActivityCallbackPlatformData* d, double newDelay) +{ + if (newDelay * timerSlop > d->delay) + return; + double delta = d->delay - newDelay; + d->delay = newDelay; + CFRunLoopTimerSetNextFireDate(d->timer.get(), CFRunLoopTimerGetNextFireDate(d->timer.get()) - delta); +} + +static void cancelTimer(DefaultGCActivityCallbackPlatformData* d) { - CFTimeInterval triggerInterval = static_cast<Heap*>(d->context.info)->lastGCLength() * 100.0; - CFRunLoopTimerSetNextFireDate(d->timer.get(), CFAbsoluteTimeGetCurrent() + triggerInterval); + d->delay = decade; + CFRunLoopTimerSetNextFireDate(d->timer.get(), CFAbsoluteTimeGetCurrent() + decade); +} + +void DefaultGCActivityCallback::didAllocate(size_t bytes) +{ + // The first byte allocated in an allocation cycle will report 0 bytes to didAllocate. + // We pretend it's one byte so that we don't ignore this allocation entirely. + if (!bytes) + bytes = 1; + Heap* heap = static_cast<Heap*>(d->context.info); + double gcTimeSlice = std::min((static_cast<double>(bytes) / MB) * gcTimeSlicePerMB, maxGCTimeSlice); + double newDelay = heap->lastGCLength() / gcTimeSlice; + scheduleTimer(d.get(), newDelay); +} + +void DefaultGCActivityCallback::willCollect() +{ + cancelTimer(d.get()); } void DefaultGCActivityCallback::synchronize() @@ -107,4 +144,9 @@ void DefaultGCActivityCallback::synchronize() CFRunLoopAddTimer(d->runLoop.get(), d->timer.get(), kCFRunLoopCommonModes); } +void DefaultGCActivityCallback::cancel() +{ + cancelTimer(d.get()); +} + } diff --git a/Source/JavaScriptCore/runtime/Identifier.cpp b/Source/JavaScriptCore/runtime/Identifier.cpp index fbc5787ce..182bcc462 100644 --- a/Source/JavaScriptCore/runtime/Identifier.cpp +++ b/Source/JavaScriptCore/runtime/Identifier.cpp @@ -110,11 +110,11 @@ PassRefPtr<StringImpl> Identifier::add(JSGlobalData* globalData, const char* c) if (iter != literalIdentifierTable.end()) return iter->second; - pair<HashSet<StringImpl*>::iterator, bool> addResult = identifierTable.add<const LChar*, IdentifierCStringTranslator>(reinterpret_cast<const LChar*>(c)); + HashSet<StringImpl*>::AddResult addResult = identifierTable.add<const LChar*, IdentifierCStringTranslator>(reinterpret_cast<const LChar*>(c)); // If the string is newly-translated, then we need to adopt it. // The boolean in the pair tells us if that is so. - RefPtr<StringImpl> addedString = addResult.second ? adoptRef(*addResult.first) : *addResult.first; + RefPtr<StringImpl> addedString = addResult.isNewEntry ? adoptRef(*addResult.iterator) : *addResult.iterator; literalIdentifierTable.add(c, addedString.get()); @@ -138,11 +138,11 @@ PassRefPtr<StringImpl> Identifier::add8(JSGlobalData* globalData, const UChar* s if (!length) return StringImpl::empty(); CharBuffer<UChar> buf = {s, length}; - pair<HashSet<StringImpl*>::iterator, bool> addResult = globalData->identifierTable->add<CharBuffer<UChar>, IdentifierLCharFromUCharTranslator >(buf); + HashSet<StringImpl*>::AddResult addResult = globalData->identifierTable->add<CharBuffer<UChar>, IdentifierLCharFromUCharTranslator >(buf); // If the string is newly-translated, then we need to adopt it. // The boolean in the pair tells us if that is so. - return addResult.second ? adoptRef(*addResult.first) : *addResult.first; + return addResult.isNewEntry ? adoptRef(*addResult.iterator) : *addResult.iterator; } template <typename CharType> @@ -210,7 +210,7 @@ PassRefPtr<StringImpl> Identifier::addSlowCase(JSGlobalData* globalData, StringI return r; } - return *globalData->identifierTable->add(r).first; + return *globalData->identifierTable->add(r).iterator; } PassRefPtr<StringImpl> Identifier::addSlowCase(ExecState* exec, StringImpl* r) diff --git a/Source/JavaScriptCore/runtime/Identifier.h b/Source/JavaScriptCore/runtime/Identifier.h index b9e5a1854..14960876b 100644 --- a/Source/JavaScriptCore/runtime/Identifier.h +++ b/Source/JavaScriptCore/runtime/Identifier.h @@ -178,11 +178,11 @@ namespace JSC { if (!length) return StringImpl::empty(); CharBuffer<T> buf = {s, length}; - pair<HashSet<StringImpl*>::iterator, bool> addResult = globalData->identifierTable->add<CharBuffer<T>, IdentifierCharBufferTranslator<T> >(buf); + HashSet<StringImpl*>::AddResult addResult = globalData->identifierTable->add<CharBuffer<T>, IdentifierCharBufferTranslator<T> >(buf); // If the string is newly-translated, then we need to adopt it. // The boolean in the pair tells us if that is so. - return addResult.second ? adoptRef(*addResult.first) : *addResult.first; + return addResult.isNewEntry ? adoptRef(*addResult.iterator) : *addResult.iterator; } inline bool operator==(const Identifier& a, const Identifier& b) @@ -246,10 +246,10 @@ namespace JSC { typedef HashMap<RefPtr<StringImpl>, int, IdentifierRepHash, HashTraits<RefPtr<StringImpl> >, IdentifierMapIndexHashTraits> IdentifierMap; template<typename U, typename V> - std::pair<HashSet<StringImpl*>::iterator, bool> IdentifierTable::add(U value) + HashSet<StringImpl*>::AddResult IdentifierTable::add(U value) { - std::pair<HashSet<StringImpl*>::iterator, bool> result = m_table.add<U, V>(value); - (*result.first)->setIsIdentifier(true); + HashSet<StringImpl*>::AddResult result = m_table.add<U, V>(value); + (*result.iterator)->setIsIdentifier(true); return result; } diff --git a/Source/JavaScriptCore/runtime/InitializeThreading.cpp b/Source/JavaScriptCore/runtime/InitializeThreading.cpp index b6fd6ce1f..4c0e123a4 100644 --- a/Source/JavaScriptCore/runtime/InitializeThreading.cpp +++ b/Source/JavaScriptCore/runtime/InitializeThreading.cpp @@ -57,7 +57,7 @@ static void initializeThreadingOnce() #if ENABLE(WRITE_BARRIER_PROFILING) WriteBarrierCounters::initialize(); #endif -#if ENABLE(JIT) && ENABLE(ASSEMBLER) +#if ENABLE(ASSEMBLER) ExecutableAllocator::initializeAllocator(); #endif RegisterFile::initializeThreading(); diff --git a/Source/JavaScriptCore/runtime/Intrinsic.h b/Source/JavaScriptCore/runtime/Intrinsic.h index 5cc00685f..73244e7c7 100644 --- a/Source/JavaScriptCore/runtime/Intrinsic.h +++ b/Source/JavaScriptCore/runtime/Intrinsic.h @@ -45,6 +45,8 @@ enum Intrinsic { RoundIntrinsic, ExpIntrinsic, LogIntrinsic, + RegExpExecIntrinsic, + RegExpTestIntrinsic, }; } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSActivation.cpp b/Source/JavaScriptCore/runtime/JSActivation.cpp index 3e05738eb..a10361007 100644 --- a/Source/JavaScriptCore/runtime/JSActivation.cpp +++ b/Source/JavaScriptCore/runtime/JSActivation.cpp @@ -45,6 +45,7 @@ JSActivation::JSActivation(CallFrame* callFrame, FunctionExecutable* functionExe : Base(callFrame->globalData(), callFrame->globalData().activationStructure.get(), functionExecutable->symbolTable(), callFrame->registers()) , m_numCapturedArgs(max(callFrame->argumentCount(), functionExecutable->parameterCount())) , m_numCapturedVars(functionExecutable->capturedVariableCount()) + , m_isTornOff(false) , m_requiresDynamicChecks(functionExecutable->usesEval() && !functionExecutable->isStrictMode()) , m_argumentsRegister(functionExecutable->generatedBytecode().argumentsRegister()) { @@ -78,11 +79,15 @@ void JSActivation::visitChildren(JSCell* cell, SlotVisitor& visitor) WriteBarrier<Unknown>* registerArray = thisObject->m_registerArray.get(); if (!registerArray) return; - + visitor.appendValues(registerArray, thisObject->m_numCapturedArgs); - // Skip 'this' and call frame. - visitor.appendValues(registerArray + CallFrame::offsetFor(thisObject->m_numCapturedArgs + 1), thisObject->m_numCapturedVars); + // Skip 'this' and call frame, except for callee and scope chain. + int offset = CallFrame::offsetFor(thisObject->m_numCapturedArgs + 1); + visitor.append(registerArray + offset + RegisterFile::ScopeChain); + visitor.append(registerArray + offset + RegisterFile::Callee); + + visitor.appendValues(registerArray + offset, thisObject->m_numCapturedVars); } inline bool JSActivation::symbolTableGet(const Identifier& propertyName, PropertySlot& slot) @@ -90,7 +95,7 @@ inline bool JSActivation::symbolTableGet(const Identifier& propertyName, Propert SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); if (entry.isNull()) return false; - if (entry.getIndex() >= m_numCapturedVars) + if (m_isTornOff && entry.getIndex() >= m_numCapturedVars) return false; slot.setValue(registerAt(entry.getIndex()).get()); @@ -110,7 +115,7 @@ inline bool JSActivation::symbolTablePut(ExecState* exec, const Identifier& prop throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return true; } - if (entry.getIndex() >= m_numCapturedVars) + if (m_isTornOff && entry.getIndex() >= m_numCapturedVars) return false; registerAt(entry.getIndex()).set(globalData, this, value); diff --git a/Source/JavaScriptCore/runtime/JSActivation.h b/Source/JavaScriptCore/runtime/JSActivation.h index 80c8aa8d0..fd1b2fd7f 100644 --- a/Source/JavaScriptCore/runtime/JSActivation.h +++ b/Source/JavaScriptCore/runtime/JSActivation.h @@ -92,7 +92,8 @@ namespace JSC { NEVER_INLINE PropertySlot::GetValueFunc getArgumentsGetter(); int m_numCapturedArgs; - int m_numCapturedVars : 31; + int m_numCapturedVars : 30; + bool m_isTornOff : 1; bool m_requiresDynamicChecks : 1; int m_argumentsRegister; }; @@ -102,7 +103,7 @@ namespace JSC { inline JSActivation* asActivation(JSValue value) { ASSERT(asObject(value)->inherits(&JSActivation::s_info)); - return static_cast<JSActivation*>(asObject(value)); + return jsCast<JSActivation*>(asObject(value)); } ALWAYS_INLINE JSActivation* Register::activation() const @@ -127,19 +128,13 @@ namespace JSC { OwnArrayPtr<WriteBarrier<Unknown> > registerArray = adoptArrayPtr(new WriteBarrier<Unknown>[registerArraySize]); WriteBarrier<Unknown>* registers = registerArray.get() + registerOffset; - // Copy all arguments that can be captured by name or by the arguments object. - for (int i = 0; i < m_numCapturedArgs; ++i) { - int index = CallFrame::argumentOffset(i); - registers[index].set(globalData, this, m_registers[index].get()); - } - - // Skip 'this' and call frame. - - // Copy all captured vars. - for (int i = 0; i < m_numCapturedVars; ++i) + int from = CallFrame::argumentOffset(m_numCapturedArgs - 1); + int to = m_numCapturedVars; + for (int i = from; i < to; ++i) registers[i].set(globalData, this, m_registers[i].get()); setRegisters(registers, registerArray.release()); + m_isTornOff = true; } } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp index 4244bc31c..aa1b8b7d9 100644 --- a/Source/JavaScriptCore/runtime/JSArray.cpp +++ b/Source/JavaScriptCore/runtime/JSArray.cpp @@ -125,15 +125,6 @@ inline void JSArray::checkConsistency(ConsistencyCheckType) #endif -JSArray::JSArray(JSGlobalData& globalData, Structure* structure) - : JSNonFinalObject(globalData, structure) - , m_indexBias(0) - , m_storage(0) - , m_sparseValueMap(0) - , m_subclassData(0) -{ -} - void JSArray::finishCreation(JSGlobalData& globalData, unsigned initialLength) { Base::finishCreation(globalData); @@ -176,11 +167,12 @@ JSArray* JSArray::tryFinishCreationUninitialized(JSGlobalData& globalData, unsig m_storage = static_cast<ArrayStorage*>(newStorage); m_storage->m_allocBase = m_storage; - m_storage->m_length = 0; + m_storage->m_length = initialLength; m_vectorLength = initialVectorLength; m_storage->m_numValuesInVector = initialLength; #if CHECK_ARRAY_CONSISTENCY + m_storage->m_initializationIndex = 0; m_storage->m_inCompactInitialization = true; #endif @@ -195,10 +187,12 @@ void JSArray::finalize(JSCell* cell) thisObject->deallocateSparseMap(); } -inline std::pair<SparseArrayValueMap::iterator, bool> SparseArrayValueMap::add(JSArray* array, unsigned i) +inline SparseArrayValueMap::AddResult SparseArrayValueMap::add(JSArray* array, unsigned i) { SparseArrayEntry entry; - std::pair<iterator, bool> result = m_map.add(i, entry); + entry.setWithoutWriteBarrier(jsUndefined()); + + AddResult result = m_map.add(i, entry); size_t capacity = m_map.capacity(); if (capacity != m_reportedCapacity) { Heap::heap(array)->reportExtraMemoryCost((capacity - m_reportedCapacity) * (sizeof(unsigned) + sizeof(WriteBarrier<Unknown>))); @@ -209,14 +203,14 @@ inline std::pair<SparseArrayValueMap::iterator, bool> SparseArrayValueMap::add(J inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i, JSValue value, bool shouldThrow) { - std::pair<SparseArrayValueMap::iterator, bool> result = add(array, i); - SparseArrayEntry& entry = result.first->second; + AddResult result = add(array, i); + SparseArrayEntry& entry = result.iterator->second; // To save a separate find & add, we first always add to the sparse map. // In the uncommon case that this is a new property, and the array is not // extensible, this is not the right thing to have done - so remove again. - if (result.second && !array->isExtensible()) { - remove(result.first); + if (result.isNewEntry && !array->isExtensible()) { + remove(result.iterator); if (shouldThrow) throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; @@ -252,14 +246,14 @@ inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i inline bool SparseArrayValueMap::putDirect(ExecState* exec, JSArray* array, unsigned i, JSValue value, bool shouldThrow) { - std::pair<SparseArrayValueMap::iterator, bool> result = add(array, i); - SparseArrayEntry& entry = result.first->second; + AddResult result = add(array, i); + SparseArrayEntry& entry = result.iterator->second; // To save a separate find & add, we first always add to the sparse map. // In the uncommon case that this is a new property, and the array is not // extensible, this is not the right thing to have done - so remove again. - if (result.second && !array->isExtensible()) { - remove(result.first); + if (result.isNewEntry && !array->isExtensible()) { + remove(result.iterator); return reject(exec, shouldThrow, "Attempting to define property on object that is not extensible."); } @@ -355,7 +349,7 @@ void JSArray::enterDictionaryMode(JSGlobalData& globalData) // This will always be a new entry in the map, so no need to check we can write, // and attributes are default so no need to set them. if (value) - map->add(this, i).first->second.set(globalData, this, value); + map->add(this, i).iterator->second.set(globalData, this, value); } void* newRawStorage = 0; @@ -430,15 +424,15 @@ bool JSArray::defineOwnNumericProperty(ExecState* exec, unsigned index, Property ASSERT(map); // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P. - std::pair<SparseArrayValueMap::iterator, bool> result = map->add(this, index); - SparseArrayEntry* entryInMap = &result.first->second; + SparseArrayValueMap::AddResult result = map->add(this, index); + SparseArrayEntry* entryInMap = &result.iterator->second; // 2. Let extensible be the value of the [[Extensible]] internal property of O. // 3. If current is undefined and extensible is false, then Reject. // 4. If current is undefined and extensible is true, then - if (result.second) { + if (result.isNewEntry) { if (!isExtensible()) { - map->remove(result.first); + map->remove(result.iterator); return reject(exec, throwException, "Attempting to define property on object that is not extensible."); } @@ -543,7 +537,7 @@ void JSArray::setLengthWritable(ExecState* exec, bool writable) // Defined in ES5.1 15.4.5.1 bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException) { - JSArray* array = static_cast<JSArray*>(object); + JSArray* array = jsCast<JSArray*>(object); // 3. If P is "length", then if (propertyName == exec->propertyNames().length) { @@ -1388,7 +1382,7 @@ void JSArray::visitChildren(JSCell* cell, SlotVisitor& visitor) visitor.copyAndAppend(reinterpret_cast<void**>(&baseStorage), storageSize(thisObject->m_vectorLength + thisObject->m_indexBias), storage->m_vector->slot(), thisObject->m_vectorLength); if (baseStorage != thisObject->m_storage->m_allocBase) { - thisObject->m_storage = reinterpret_cast<ArrayStorage*>(static_cast<char*>(baseStorage) + sizeof(JSValue) * thisObject->m_indexBias); + thisObject->m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(baseStorage) + sizeof(JSValue) * thisObject->m_indexBias); thisObject->m_storage->m_allocBase = baseStorage; ASSERT(thisObject->m_storage->m_allocBase); } @@ -1473,17 +1467,19 @@ void JSArray::sort(ExecState* exec) Heap::heap(this)->pushTempSortVector(&values); + bool isSortingPrimitiveValues = true; for (size_t i = 0; i < lengthNotIncludingUndefined; i++) { JSValue value = m_storage->m_vector[i].get(); ASSERT(!value.isUndefined()); values[i].first = value; + isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive(); } // FIXME: The following loop continues to call toString on subsequent values even after // a toString call raises an exception. for (size_t i = 0; i < lengthNotIncludingUndefined; i++) - values[i].second = values[i].first.toString(exec)->value(exec); + values[i].second = values[i].first.toUStringInline(exec); if (exec->hadException()) { Heap::heap(this)->popTempSortVector(&values); @@ -1494,7 +1490,10 @@ void JSArray::sort(ExecState* exec) // than O(N log N). #if HAVE(MERGESORT) - mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); + if (isSortingPrimitiveValues) + qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); + else + mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); #else // FIXME: The qsort library function is likely to not be a stable sort. // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort. @@ -1622,7 +1621,7 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, tree.abstractor().m_nodes.grow(nodeCount); if (callType == CallTypeJS) - tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, asFunction(compareFunction), 2)); + tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, jsCast<JSFunction*>(compareFunction), 2)); if (!tree.abstractor().m_nodes.begin()) { throwOutOfMemoryError(exec); @@ -1802,16 +1801,6 @@ unsigned JSArray::compactForSorting(JSGlobalData& globalData) return numDefined; } -void* JSArray::subclassData() const -{ - return m_subclassData; -} - -void JSArray::setSubclassData(void* d) -{ - m_subclassData = d; -} - #if CHECK_ARRAY_CONSISTENCY void JSArray::checkConsistency(ConsistencyCheckType type) @@ -1826,7 +1815,7 @@ void JSArray::checkConsistency(ConsistencyCheckType type) unsigned numValuesInVector = 0; for (unsigned i = 0; i < m_vectorLength; ++i) { - if (JSValue value = storage->m_vector[i]) { + if (JSValue value = storage->m_vector[i].get()) { ASSERT(i < storage->m_length); if (type != DestructorConsistencyCheck) value.isUndefined(); // Likely to crash if the object was deallocated. @@ -1840,15 +1829,15 @@ void JSArray::checkConsistency(ConsistencyCheckType type) ASSERT(numValuesInVector <= storage->m_length); if (m_sparseValueMap) { - SparseArrayValueMap::iterator end = m_sparseValueMap->end(); - for (SparseArrayValueMap::iterator it = m_sparseValueMap->begin(); it != end; ++it) { + SparseArrayValueMap::const_iterator end = m_sparseValueMap->end(); + for (SparseArrayValueMap::const_iterator it = m_sparseValueMap->begin(); it != end; ++it) { unsigned index = it->first; ASSERT(index < storage->m_length); - ASSERT(index >= storage->m_vectorLength); + ASSERT(index >= m_vectorLength); ASSERT(index <= MAX_ARRAY_INDEX); ASSERT(it->second); if (type != DestructorConsistencyCheck) - it->second.isUndefined(); // Likely to crash if the object was deallocated. + it->second.getNonSparseMode().isUndefined(); // Likely to crash if the object was deallocated. } } } diff --git a/Source/JavaScriptCore/runtime/JSArray.h b/Source/JavaScriptCore/runtime/JSArray.h index ad98d6619..17c7f3ed7 100644 --- a/Source/JavaScriptCore/runtime/JSArray.h +++ b/Source/JavaScriptCore/runtime/JSArray.h @@ -55,6 +55,7 @@ namespace JSC { public: typedef Map::iterator iterator; typedef Map::const_iterator const_iterator; + typedef Map::AddResult AddResult; SparseArrayValueMap() : m_flags(Normal) @@ -87,7 +88,7 @@ namespace JSC { // These methods may mutate the contents of the map void put(ExecState*, JSArray*, unsigned, JSValue, bool shouldThrow); bool putDirect(ExecState*, JSArray*, unsigned, JSValue, bool shouldThrow); - std::pair<iterator, bool> add(JSArray*, unsigned); + AddResult add(JSArray*, unsigned); iterator find(unsigned i) { return m_map.find(i); } // This should ASSERT the remove is valid (check the result of the find). void remove(iterator it) { m_map.remove(it); } @@ -118,7 +119,9 @@ namespace JSC { unsigned m_numValuesInVector; void* m_allocBase; // Pointer to base address returned by malloc(). Keeping this pointer does eliminate false positives from the leak detector. #if CHECK_ARRAY_CONSISTENCY - uintptr_t m_inCompactInitialization; // Needs to be a uintptr_t for alignment purposes. + // Needs to be a uintptr_t for alignment purposes. + uintptr_t m_initializationIndex; + uintptr_t m_inCompactInitialization; #else uintptr_t m_padding; #endif @@ -136,7 +139,13 @@ namespace JSC { friend class JIT; protected: - JS_EXPORT_PRIVATE explicit JSArray(JSGlobalData&, Structure*); + explicit JSArray(JSGlobalData& globalData, Structure* structure) + : JSNonFinalObject(globalData, structure) + , m_indexBias(0) + , m_storage(0) + , m_sparseValueMap(0) + { + } JS_EXPORT_PRIVATE void finishCreation(JSGlobalData&, unsigned initialLength = 0); JS_EXPORT_PRIVATE JSArray* tryFinishCreationUninitialized(JSGlobalData&, unsigned initialLength); @@ -218,24 +227,25 @@ namespace JSC { ArrayStorage *storage = m_storage; #if CHECK_ARRAY_CONSISTENCY ASSERT(storage->m_inCompactInitialization); -#endif // Check that we are initializing the next index in sequence. - ASSERT_UNUSED(i, i == storage->m_length); + ASSERT(i == storage->m_initializationIndex); // tryCreateUninitialized set m_numValuesInVector to the initialLength, // check we do not try to initialize more than this number of properties. - ASSERT(storage->m_length < storage->m_numValuesInVector); - // It is improtant that we increment length here, so that all newly added - // values in the array still get marked during the initialization phase. - storage->m_vector[storage->m_length++].set(globalData, this, v); + ASSERT(storage->m_initializationIndex < storage->m_numValuesInVector); + storage->m_initializationIndex++; +#endif + ASSERT(i < storage->m_length); + ASSERT(i < storage->m_numValuesInVector); + storage->m_vector[i].set(globalData, this, v); } inline void completeInitialization(unsigned newLength) { // Check that we have initialized as meny properties as we think we have. ASSERT_UNUSED(newLength, newLength == m_storage->m_length); - // Check that the number of propreties initialized matches the initialLength. - ASSERT(m_storage->m_length == m_storage->m_numValuesInVector); #if CHECK_ARRAY_CONSISTENCY + // Check that the number of propreties initialized matches the initialLength. + ASSERT(m_storage->m_initializationIndex == m_storage->m_numValuesInVector); ASSERT(m_storage->m_inCompactInitialization); m_storage->m_inCompactInitialization = false; #endif @@ -313,10 +323,8 @@ namespace JSC { // FIXME: Maybe SparseArrayValueMap should be put into its own JSCell? SparseArrayValueMap* m_sparseValueMap; - void* m_subclassData; // A JSArray subclass can use this to fill the vector lazily. static ptrdiff_t sparseValueMapOffset() { return OBJECT_OFFSETOF(JSArray, m_sparseValueMap); } - static ptrdiff_t subclassDataOffset() { return OBJECT_OFFSETOF(JSArray, m_subclassData); } static ptrdiff_t indexBiasOffset() { return OBJECT_OFFSETOF(JSArray, m_indexBias); } }; @@ -338,7 +346,7 @@ namespace JSC { inline JSArray* asArray(JSCell* cell) { ASSERT(cell->inherits(&JSArray::s_info)); - return static_cast<JSArray*>(cell); + return jsCast<JSArray*>(cell); } inline JSArray* asArray(JSValue value) diff --git a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp index 8ebf8c638..5fee47c24 100644 --- a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp +++ b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp @@ -38,7 +38,7 @@ const ClassInfo JSBoundFunction::s_info = { "Function", &Base::s_info, 0, 0, CRE EncodedJSValue JSC_HOST_CALL boundFunctionCall(ExecState* exec) { - JSBoundFunction* boundFunction = static_cast<JSBoundFunction*>(exec->callee()); + JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(exec->callee()); ASSERT(isJSArray(boundFunction->boundArgs())); // Currently this is true! JSArray* boundArgs = asArray(boundFunction->boundArgs()); @@ -58,7 +58,7 @@ EncodedJSValue JSC_HOST_CALL boundFunctionCall(ExecState* exec) EncodedJSValue JSC_HOST_CALL boundFunctionConstruct(ExecState* exec) { - JSBoundFunction* boundFunction = static_cast<JSBoundFunction*>(exec->callee()); + JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(exec->callee()); ASSERT(isJSArray(boundFunction->boundArgs())); // Currently this is true! JSArray* boundArgs = asArray(boundFunction->boundArgs()); diff --git a/Source/JavaScriptCore/runtime/JSByteArray.cpp b/Source/JavaScriptCore/runtime/JSByteArray.cpp deleted file mode 100644 index 39ea4d0b9..000000000 --- a/Source/JavaScriptCore/runtime/JSByteArray.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "JSByteArray.h" - -#include "JSGlobalObject.h" -#include "PropertyNameArray.h" - -using namespace WTF; - -namespace JSC { - -const ClassInfo JSByteArray::s_info = { "Uint8ClampedArray", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSByteArray) }; - -JSByteArray::JSByteArray(ExecState* exec, Structure* structure, ByteArray* storage) - : JSNonFinalObject(exec->globalData(), structure) - , m_storage(storage) -{ -} - -JSByteArray::~JSByteArray() -{ - ASSERT(jsCast<JSByteArray*>(this)); -} - -void JSByteArray::destroy(JSCell* cell) -{ - jsCast<JSByteArray*>(cell)->JSByteArray::~JSByteArray(); -} - -Structure* JSByteArray::createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const JSC::ClassInfo* classInfo) -{ - return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), classInfo); -} - -bool JSByteArray::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) -{ - JSByteArray* thisObject = jsCast<JSByteArray*>(cell); - bool ok; - unsigned index = propertyName.toUInt32(ok); - if (ok && thisObject->canAccessIndex(index)) { - slot.setValue(thisObject->getIndex(exec, index)); - return true; - } - return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot); -} - -bool JSByteArray::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) -{ - JSByteArray* thisObject = jsCast<JSByteArray*>(object); - bool ok; - unsigned index = propertyName.toUInt32(ok); - if (ok && thisObject->canAccessIndex(index)) { - descriptor.setDescriptor(thisObject->getIndex(exec, index), DontDelete); - return true; - } - return JSObject::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); -} - -bool JSByteArray::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot) -{ - JSByteArray* thisObject = jsCast<JSByteArray*>(cell); - if (thisObject->canAccessIndex(propertyName)) { - slot.setValue(thisObject->getIndex(exec, propertyName)); - return true; - } - return JSObject::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, propertyName), slot); -} - -void JSByteArray::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) -{ - JSByteArray* thisObject = jsCast<JSByteArray*>(cell); - bool ok; - unsigned index = propertyName.toUInt32(ok); - if (ok) { - thisObject->setIndex(exec, index, value); - return; - } - JSObject::put(thisObject, exec, propertyName, value, slot); -} - -void JSByteArray::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool) -{ - jsCast<JSByteArray*>(cell)->setIndex(exec, propertyName, value); -} - -void JSByteArray::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) -{ - JSByteArray* thisObject = jsCast<JSByteArray*>(object); - unsigned length = thisObject->m_storage->length(); - for (unsigned i = 0; i < length; ++i) - propertyNames.add(Identifier::from(exec, i)); - JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode); -} - -} - diff --git a/Source/JavaScriptCore/runtime/JSByteArray.h b/Source/JavaScriptCore/runtime/JSByteArray.h deleted file mode 100644 index 06181d901..000000000 --- a/Source/JavaScriptCore/runtime/JSByteArray.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef JSByteArray_h -#define JSByteArray_h - -#include "JSObject.h" - -#include <wtf/ByteArray.h> - -namespace JSC { - - class JSByteArray : public JSNonFinalObject { - friend class JSGlobalData; - public: - typedef JSNonFinalObject Base; - - bool canAccessIndex(unsigned i) { return i < m_storage->length(); } - JSValue getIndex(ExecState*, unsigned i) - { - ASSERT(canAccessIndex(i)); - return jsNumber(m_storage->data()[i]); - } - - void setIndex(unsigned i, int value) - { - ASSERT(canAccessIndex(i)); - if (value & ~0xFF) { - if (value < 0) - value = 0; - else - value = 255; - } - m_storage->data()[i] = static_cast<unsigned char>(value); - } - - void setIndex(unsigned i, double value) - { - ASSERT(canAccessIndex(i)); - if (!(value > 0)) // Clamp NaN to 0 - value = 0; - else if (value > 255) - value = 255; - m_storage->data()[i] = static_cast<unsigned char>(value + 0.5); - } - - void setIndex(ExecState* exec, unsigned i, JSValue value) - { - double byteValue = value.toNumber(exec); - if (exec->hadException()) - return; - if (canAccessIndex(i)) - setIndex(i, byteValue); - } - - private: - JS_EXPORT_PRIVATE JSByteArray(ExecState*, Structure*, ByteArray* storage); - - public: - static JSByteArray* create(ExecState* exec, Structure* structure, ByteArray* storage) - { - JSByteArray* array = new (NotNull, allocateCell<JSByteArray>(*exec->heap())) JSByteArray(exec, structure, storage); - array->finishCreation(exec); - return array; - } - - JS_EXPORT_PRIVATE static Structure* createStructure(JSGlobalData&, JSGlobalObject*, JSValue prototype, const JSC::ClassInfo* = &s_info); - - JS_EXPORT_PRIVATE static bool getOwnPropertySlot(JSC::JSCell*, JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertySlot&); - JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSC::JSCell*, JSC::ExecState*, unsigned propertyName, JSC::PropertySlot&); - JS_EXPORT_PRIVATE static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); - JS_EXPORT_PRIVATE static void put(JSC::JSCell*, JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSValue, JSC::PutPropertySlot&); - JS_EXPORT_PRIVATE static void putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned propertyName, JSC::JSValue, bool shouldThrow); - - JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSC::JSObject*, JSC::ExecState*, JSC::PropertyNameArray&, EnumerationMode); - - static JS_EXPORTDATA const ClassInfo s_info; - - size_t length() const { return m_storage->length(); } - - WTF::ByteArray* storage() const { return m_storage.get(); } - - ~JSByteArray(); - JS_EXPORT_PRIVATE static void destroy(JSCell*); - - static size_t offsetOfStorage() { return OBJECT_OFFSETOF(JSByteArray, m_storage); } - - protected: - static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | JSObject::StructureFlags; - - void finishCreation(ExecState* exec) - { - Base::finishCreation(exec->globalData()); - putDirect(exec->globalData(), exec->globalData().propertyNames->length, jsNumber(m_storage->length()), ReadOnly | DontDelete); - } - - private: - RefPtr<WTF::ByteArray> m_storage; - }; - - JSByteArray* asByteArray(JSValue value); - inline JSByteArray* asByteArray(JSValue value) - { - return static_cast<JSByteArray*>(value.asCell()); - } - - inline bool isJSByteArray(JSValue v) { return v.isCell() && v.asCell()->classInfo() == &JSByteArray::s_info; } - -} // namespace JSC - -#endif // JSByteArray_h diff --git a/Source/JavaScriptCore/runtime/JSCell.cpp b/Source/JavaScriptCore/runtime/JSCell.cpp index f08d0260a..7f9ba88a2 100644 --- a/Source/JavaScriptCore/runtime/JSCell.cpp +++ b/Source/JavaScriptCore/runtime/JSCell.cpp @@ -159,7 +159,7 @@ JSObject* JSCell::toObject(ExecState* exec, JSGlobalObject* globalObject) const if (isString()) return static_cast<const JSString*>(this)->toObject(exec, globalObject); ASSERT(isObject()); - return static_cast<JSObject*>(const_cast<JSCell*>(this)); + return jsCast<JSObject*>(const_cast<JSCell*>(this)); } void slowValidateCell(JSCell* cell) diff --git a/Source/JavaScriptCore/runtime/JSCell.h b/Source/JavaScriptCore/runtime/JSCell.h index 2ef359b76..431e67145 100644 --- a/Source/JavaScriptCore/runtime/JSCell.h +++ b/Source/JavaScriptCore/runtime/JSCell.h @@ -178,7 +178,7 @@ namespace JSC { { #if ENABLE(GC_VALIDATION) ASSERT(globalData.isInitializingObject()); - globalData.setInitializingObject(false); + globalData.setInitializingObjectClass(0); #else UNUSED_PARAM(globalData); #endif @@ -328,9 +328,8 @@ namespace JSC { void* allocateCell(Heap& heap) { #if ENABLE(GC_VALIDATION) - ASSERT(sizeof(T) == T::s_info.cellSize); ASSERT(!heap.globalData()->isInitializingObject()); - heap.globalData()->setInitializingObject(true); + heap.globalData()->setInitializingObjectClass(&T::s_info); #endif JSCell* result = 0; if (NeedsDestructor<T>::value) @@ -351,16 +350,29 @@ namespace JSC { template<typename To, typename From> inline To jsCast(From* from) { - ASSERT(from->inherits(&WTF::RemovePointer<To>::Type::s_info)); + ASSERT(!from || from->JSCell::inherits(&WTF::RemovePointer<To>::Type::s_info)); return static_cast<To>(from); } + template<typename To> + inline To jsCast(JSValue from) + { + ASSERT(from.isCell() && from.asCell()->JSCell::inherits(&WTF::RemovePointer<To>::Type::s_info)); + return static_cast<To>(from.asCell()); + } + template<typename To, typename From> inline To jsDynamicCast(From* from) { return from->inherits(&WTF::RemovePointer<To>::Type::s_info) ? static_cast<To>(from) : 0; } + template<typename To> + inline To jsDynamicCast(JSValue from) + { + return from.isCell() && from.asCell()->inherits(&WTF::RemovePointer<To>::Type::s_info) ? static_cast<To>(from.asCell()) : 0; + } + } // namespace JSC #endif // JSCell_h diff --git a/Source/JavaScriptCore/runtime/JSDateMath.cpp b/Source/JavaScriptCore/runtime/JSDateMath.cpp index 882f86fa0..dbe748835 100644 --- a/Source/JavaScriptCore/runtime/JSDateMath.cpp +++ b/Source/JavaScriptCore/runtime/JSDateMath.cpp @@ -72,12 +72,8 @@ #include "config.h" #include "JSDateMath.h" -#include "CurrentTime.h" #include "JSObject.h" -#include "MathExtras.h" #include "ScopeChain.h" -#include "StdLibExtras.h" -#include "StringExtras.h" #include <algorithm> #include <limits.h> @@ -86,17 +82,16 @@ #include <time.h> #include <wtf/ASCIICType.h> #include <wtf/Assertions.h> +#include <wtf/CurrentTime.h> +#include <wtf/MathExtras.h> +#include <wtf/StdLibExtras.h> +#include <wtf/StringExtras.h> #include <wtf/text/StringBuilder.h> #if HAVE(ERRNO_H) #include <errno.h> #endif -#if OS(WINCE) -extern "C" size_t strftime(char * const s, const size_t maxsize, const char * const format, const struct tm * const t); -extern "C" struct tm * localtime(const time_t *timer); -#endif - #if HAVE(SYS_TIME_H) #include <sys/time.h> #endif diff --git a/Source/JavaScriptCore/runtime/JSDateMath.h b/Source/JavaScriptCore/runtime/JSDateMath.h index ba6d647dd..f77cf1e75 100644 --- a/Source/JavaScriptCore/runtime/JSDateMath.h +++ b/Source/JavaScriptCore/runtime/JSDateMath.h @@ -74,33 +74,6 @@ public: { } - GregorianDateTime(ExecState* exec, const tm& inTm) - : second(inTm.tm_sec) - , minute(inTm.tm_min) - , hour(inTm.tm_hour) - , weekDay(inTm.tm_wday) - , monthDay(inTm.tm_mday) - , yearDay(inTm.tm_yday) - , month(inTm.tm_mon) - , year(inTm.tm_year) - , isDST(inTm.tm_isdst) - { - UNUSED_PARAM(exec); -#if HAVE(TM_GMTOFF) - utcOffset = static_cast<int>(inTm.tm_gmtoff); -#else - utcOffset = static_cast<int>(getUTCOffset(exec) / WTF::msPerSecond + (isDST ? WTF::secondsPerHour : 0)); -#endif - -#if HAVE(TM_ZONE) - int inZoneSize = strlen(inTm.tm_zone) + 1; - timeZone = adoptArrayPtr(new char[inZoneSize]); - strncpy(timeZone.get(), inTm.tm_zone, inZoneSize); -#else - timeZone = nullptr; -#endif - } - operator tm() const { tm ret; diff --git a/Source/JavaScriptCore/runtime/JSFunction.cpp b/Source/JavaScriptCore/runtime/JSFunction.cpp index fa798f41a..243946ba9 100644 --- a/Source/JavaScriptCore/runtime/JSFunction.cpp +++ b/Source/JavaScriptCore/runtime/JSFunction.cpp @@ -59,22 +59,25 @@ bool JSFunction::isHostFunctionNonInline() const return isHostFunction(); } -JSFunction* JSFunction::create(ExecState* exec, JSGlobalObject* globalObject, int length, const Identifier& name, NativeFunction nativeFunction, NativeFunction nativeConstructor) +JSFunction* JSFunction::create(ExecState* exec, JSGlobalObject* globalObject, int length, const Identifier& name, NativeFunction nativeFunction, Intrinsic intrinsic, NativeFunction nativeConstructor) { - NativeExecutable* executable = exec->globalData().getHostFunction(nativeFunction, nativeConstructor); + NativeExecutable* executable; +#if !ENABLE(JIT) + UNUSED_PARAM(intrinsic); +#else + if (intrinsic != NoIntrinsic && exec->globalData().canUseJIT()) { + ASSERT(nativeConstructor == callHostFunctionAsConstructor); + executable = exec->globalData().getHostFunction(nativeFunction, intrinsic); + } else +#endif + executable = exec->globalData().getHostFunction(nativeFunction, nativeConstructor); + JSFunction* function = new (NotNull, allocateCell<JSFunction>(*exec->heap())) JSFunction(exec, globalObject, globalObject->functionStructure()); // Can't do this during initialization because getHostFunction might do a GC allocation. function->finishCreation(exec, executable, length, name); return function; } -JSFunction* JSFunction::create(ExecState* exec, JSGlobalObject* globalObject, int length, const Identifier& name, NativeExecutable* nativeExecutable) -{ - JSFunction* function = new (NotNull, allocateCell<JSFunction>(*exec->heap())) JSFunction(exec, globalObject, globalObject->functionStructure()); - function->finishCreation(exec, nativeExecutable, length, name); - return function; -} - JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) : Base(exec->globalData(), structure) , m_executable() @@ -172,21 +175,21 @@ CallType JSFunction::getCallData(JSCell* cell, CallData& callData) JSValue JSFunction::argumentsGetter(ExecState* exec, JSValue slotBase, const Identifier&) { - JSFunction* thisObj = asFunction(slotBase); + JSFunction* thisObj = jsCast<JSFunction*>(slotBase); ASSERT(!thisObj->isHostFunction()); return exec->interpreter()->retrieveArgumentsFromVMCode(exec, thisObj); } JSValue JSFunction::callerGetter(ExecState* exec, JSValue slotBase, const Identifier&) { - JSFunction* thisObj = asFunction(slotBase); + JSFunction* thisObj = jsCast<JSFunction*>(slotBase); ASSERT(!thisObj->isHostFunction()); JSValue caller = exec->interpreter()->retrieveCallerFromVMCode(exec, thisObj); // See ES5.1 15.3.5.4 - Function.caller may not be used to retrieve a strict caller. if (!caller.isObject() || !asObject(caller)->inherits(&JSFunction::s_info)) return caller; - JSFunction* function = asFunction(caller); + JSFunction* function = jsCast<JSFunction*>(caller); if (function->isHostFunction() || !function->jsExecutable()->isStrictMode()) return caller; return throwTypeError(exec, "Function.caller used to retrieve strict caller"); @@ -194,7 +197,7 @@ JSValue JSFunction::callerGetter(ExecState* exec, JSValue slotBase, const Identi JSValue JSFunction::lengthGetter(ExecState*, JSValue slotBase, const Identifier&) { - JSFunction* thisObj = asFunction(slotBase); + JSFunction* thisObj = jsCast<JSFunction*>(slotBase); ASSERT(!thisObj->isHostFunction()); return jsNumber(thisObj->jsExecutable()->parameterCount()); } @@ -369,46 +372,55 @@ bool JSFunction::defineOwnProperty(JSObject* object, ExecState* exec, const Iden // following the rules set out in ECMA-262 8.12.9. PropertySlot slot; thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, propertyName, slot); - } else if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length || propertyName == exec->propertyNames().caller) { - if (!object->isExtensible()) { - if (throwException) - throwError(exec, createTypeError(exec, "Attempting to define property on object that is not extensible.")); - return false; - } - if (descriptor.configurablePresent() && descriptor.configurable()) { - if (throwException) - throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property.")); - return false; - } - if (descriptor.enumerablePresent() && descriptor.enumerable()) { - if (throwException) - throwError(exec, createTypeError(exec, "Attempting to change enumerable attribute of unconfigurable property.")); - return false; - } - if (descriptor.isAccessorDescriptor()) { - if (throwException) - throwError(exec, createTypeError(exec, "Attempting to change access mechanism for an unconfigurable property.")); - return false; + return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); + } + + bool valueCheck; + if (propertyName == exec->propertyNames().arguments) { + if (thisObject->jsExecutable()->isStrictMode()) { + if (!Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor)) + thisObject->putDirectAccessor(exec->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor); + return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); } - if (descriptor.writablePresent() && descriptor.writable()) { - if (throwException) - throwError(exec, createTypeError(exec, "Attempting to change writable attribute of unconfigurable property.")); - return false; + valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), exec->interpreter()->retrieveArgumentsFromVMCode(exec, thisObject)); + } else if (propertyName == exec->propertyNames().caller) { + if (thisObject->jsExecutable()->isStrictMode()) { + if (!Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor)) + thisObject->putDirectAccessor(exec->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor); + return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); } - if (!descriptor.value()) - return true; - if (propertyName == exec->propertyNames().arguments && sameValue(exec, descriptor.value(), exec->interpreter()->retrieveArgumentsFromVMCode(exec, thisObject))) - return true; - if (propertyName == exec->propertyNames().length && sameValue(exec, descriptor.value(), jsNumber(thisObject->jsExecutable()->parameterCount()))) - return true; - if (propertyName == exec->propertyNames().caller && sameValue(exec, descriptor.value(), exec->interpreter()->retrieveCallerFromVMCode(exec, thisObject))) - return true; + valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), exec->interpreter()->retrieveCallerFromVMCode(exec, thisObject)); + } else if (propertyName == exec->propertyNames().length) + valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), jsNumber(thisObject->jsExecutable()->parameterCount())); + else + return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); + + if (descriptor.configurablePresent() && descriptor.configurable()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property.")); + return false; + } + if (descriptor.enumerablePresent() && descriptor.enumerable()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to change enumerable attribute of unconfigurable property.")); + return false; + } + if (descriptor.isAccessorDescriptor()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to change access mechanism for an unconfigurable property.")); + return false; + } + if (descriptor.writablePresent() && descriptor.writable()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to change writable attribute of unconfigurable property.")); + return false; + } + if (!valueCheck) { if (throwException) throwError(exec, createTypeError(exec, "Attempting to change value of a readonly property.")); return false; } - - return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); + return true; } // ECMA 13.2.2 [[Construct]] diff --git a/Source/JavaScriptCore/runtime/JSFunction.h b/Source/JavaScriptCore/runtime/JSFunction.h index 288181060..5553115bf 100644 --- a/Source/JavaScriptCore/runtime/JSFunction.h +++ b/Source/JavaScriptCore/runtime/JSFunction.h @@ -24,6 +24,7 @@ #ifndef JSFunction_h #define JSFunction_h +#include "InternalFunction.h" #include "JSObject.h" namespace JSC { @@ -54,8 +55,7 @@ namespace JSC { public: typedef JSNonFinalObject Base; - JS_EXPORT_PRIVATE static JSFunction* create(ExecState*, JSGlobalObject*, int length, const Identifier& name, NativeFunction nativeFunction, NativeFunction nativeConstructor = callHostFunctionAsConstructor); - static JSFunction* create(ExecState*, JSGlobalObject*, int length, const Identifier& name, NativeExecutable* nativeExecutable); + JS_EXPORT_PRIVATE static JSFunction* create(ExecState*, JSGlobalObject*, int length, const Identifier& name, NativeFunction nativeFunction, Intrinsic = NoIntrinsic, NativeFunction nativeConstructor = callHostFunctionAsConstructor); static JSFunction* create(ExecState* exec, FunctionExecutable* executable, ScopeChainNode* scopeChain) { @@ -154,12 +154,9 @@ namespace JSC { WriteBarrier<ScopeChainNode> m_scopeChain; }; - JSFunction* asFunction(JSValue); - - inline JSFunction* asFunction(JSValue value) + inline bool JSValue::isFunction() const { - ASSERT(asObject(value)->inherits(&JSFunction::s_info)); - return static_cast<JSFunction*>(asObject(value)); + return isCell() && (asCell()->inherits(&JSFunction::s_info) || asCell()->inherits(&InternalFunction::s_info)); } } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.cpp b/Source/JavaScriptCore/runtime/JSGlobalData.cpp index f138e75fb..b08c7dfa2 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalData.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalData.cpp @@ -40,7 +40,6 @@ #include "JSActivation.h" #include "JSAPIValueWrapper.h" #include "JSArray.h" -#include "JSByteArray.h" #include "JSClassRef.h" #include "JSFunction.h" #include "JSLock.h" @@ -62,33 +61,12 @@ #include "RegExp.h" #endif -#if PLATFORM(MAC) +#if USE(CF) #include <CoreFoundation/CoreFoundation.h> #endif using namespace WTF; -namespace { - -using namespace JSC; - -class Recompiler : public MarkedBlock::VoidFunctor { -public: - void operator()(JSCell*); -}; - -inline void Recompiler::operator()(JSCell* cell) -{ - if (!cell->inherits(&JSFunction::s_info)) - return; - JSFunction* function = asFunction(cell); - if (!function->executable() || function->executable()->isHostFunction()) - return; - function->jsExecutable()->discardCode(); -} - -} // namespace - namespace JSC { extern const HashTable arrayConstructorTable; @@ -110,8 +88,34 @@ extern const HashTable regExpPrototypeTable; extern const HashTable stringTable; extern const HashTable stringConstructorTable; +#if ENABLE(ASSEMBLER) && (ENABLE(CLASSIC_INTERPRETER) || ENABLE(LLINT)) +static bool enableAssembler(ExecutableAllocator& executableAllocator) +{ + if (!executableAllocator.isValid() || !Options::useJIT) + return false; + +#if USE(CF) + CFStringRef canUseJITKey = CFStringCreateWithCString(0 , "JavaScriptCoreUseJIT", kCFStringEncodingMacRoman); + CFBooleanRef canUseJIT = (CFBooleanRef)CFPreferencesCopyAppValue(canUseJITKey, kCFPreferencesCurrentApplication); + if (canUseJIT) { + return kCFBooleanTrue == canUseJIT; + CFRelease(canUseJIT); + } + CFRelease(canUseJITKey); +#endif + +#if USE(CF) || OS(UNIX) + char* canUseJITString = getenv("JavaScriptCoreUseJIT"); + return !canUseJITString || atoi(canUseJITString); +#else + return true; +#endif +} +#endif + JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType threadStackType, HeapSize heapSize) - : globalDataType(globalDataType) + : heap(this, heapSize) + , globalDataType(globalDataType) , clientData(0) , topCallFrame(CallFrame::noCaller()) , arrayConstructorTable(fastNew<HashTable>(JSC::arrayConstructorTable)) @@ -141,7 +145,6 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType thread , parserArena(adoptPtr(new ParserArena)) , keywords(adoptPtr(new Keywords(this))) , interpreter(0) - , heap(this, heapSize) , jsArrayClassInfo(&JSArray::s_info) , jsFinalObjectClassInfo(&JSFinalObject::s_info) #if ENABLE(DFG_JIT) @@ -160,8 +163,11 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType thread #if CPU(X86) && ENABLE(JIT) , m_timeoutCount(512) #endif +#if ENABLE(ASSEMBLER) && (ENABLE(CLASSIC_INTERPRETER) || ENABLE(LLINT)) + , m_canUseAssembler(enableAssembler(executableAllocator)) +#endif #if ENABLE(GC_VALIDATION) - , m_isInitializingObject(false) + , m_initializingObjectClass(0) #endif , m_inDefineOwnProperty(false) { @@ -193,33 +199,7 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType thread wtfThreadData().setCurrentIdentifierTable(existingEntryIdentifierTable); -#if ENABLE(JIT) && (ENABLE(CLASSIC_INTERPRETER) || ENABLE(LLINT)) -#if USE(CF) - CFStringRef canUseJITKey = CFStringCreateWithCString(0 , "JavaScriptCoreUseJIT", kCFStringEncodingMacRoman); - CFBooleanRef canUseJIT = (CFBooleanRef)CFPreferencesCopyAppValue(canUseJITKey, kCFPreferencesCurrentApplication); - if (canUseJIT) { - m_canUseJIT = kCFBooleanTrue == canUseJIT; - CFRelease(canUseJIT); - } else { - char* canUseJITString = getenv("JavaScriptCoreUseJIT"); - m_canUseJIT = !canUseJITString || atoi(canUseJITString); - } - CFRelease(canUseJITKey); -#elif OS(UNIX) - char* canUseJITString = getenv("JavaScriptCoreUseJIT"); - m_canUseJIT = !canUseJITString || atoi(canUseJITString); -#else - m_canUseJIT = true; -#endif -#endif #if ENABLE(JIT) -#if ENABLE(CLASSIC_INTERPRETER) || ENABLE(LLINT) - if (m_canUseJIT) - m_canUseJIT = executableAllocator.isValid(); - - if (!Options::useJIT) - m_canUseJIT = false; -#endif jitStubs = adoptPtr(new JITThunks(this)); #endif @@ -232,38 +212,13 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType thread llintData.performAssertions(*this); } -void JSGlobalData::clearBuiltinStructures() -{ - structureStructure.clear(); - debuggerActivationStructure.clear(); - activationStructure.clear(); - interruptedExecutionErrorStructure.clear(); - terminatedExecutionErrorStructure.clear(); - staticScopeStructure.clear(); - strictEvalActivationStructure.clear(); - stringStructure.clear(); - notAnObjectStructure.clear(); - propertyNameIteratorStructure.clear(); - getterSetterStructure.clear(); - apiWrapperStructure.clear(); - scopeChainNodeStructure.clear(); - executableStructure.clear(); - nativeExecutableStructure.clear(); - evalExecutableStructure.clear(); - programExecutableStructure.clear(); - functionExecutableStructure.clear(); - regExpStructure.clear(); - structureChainStructure.clear(); -} - JSGlobalData::~JSGlobalData() { - // By the time this is destroyed, heap.destroy() must already have been called. + heap.lastChanceToFinalize(); delete interpreter; #ifndef NDEBUG - // Zeroing out to make the behavior more predictable when someone attempts to use a deleted instance. - interpreter = 0; + interpreter = reinterpret_cast<Interpreter*>(0xbbadbeef); #endif arrayPrototypeTable->deleteTable(); @@ -443,15 +398,6 @@ void JSGlobalData::dumpSampleData(ExecState* exec) #endif } -void JSGlobalData::recompileAllJSFunctions() -{ - // If JavaScript is running, it's not safe to recompile, since we'll end - // up throwing away code that is live on the stack. - ASSERT(!dynamicGlobalObject); - - heap.objectSpace().forEachCell<Recompiler>(); -} - struct StackPreservingRecompiler : public MarkedBlock::VoidFunctor { HashSet<FunctionExecutable*> currentlyExecutingFunctions; void operator()(JSCell* cell) @@ -478,7 +424,7 @@ void JSGlobalData::releaseExecutableMemory() if (cell->inherits(&ScriptExecutable::s_info)) executable = static_cast<ScriptExecutable*>(*ptr); else if (cell->inherits(&JSFunction::s_info)) { - JSFunction* function = asFunction(*ptr); + JSFunction* function = jsCast<JSFunction*>(*ptr); if (function->isHostFunction()) continue; executable = function->jsExecutable(); diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.h b/Source/JavaScriptCore/runtime/JSGlobalData.h index acbcee816..177d80298 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalData.h +++ b/Source/JavaScriptCore/runtime/JSGlobalData.h @@ -152,6 +152,8 @@ namespace JSC { void makeUsableFromMultipleThreads() { heap.machineThreads().makeUsableFromMultipleThreads(); } + Heap heap; // The heap is our first data member to ensure that it's destructed after all the objects that reference it. + GlobalDataType globalDataType; ClientData* clientData; CallFrame* topCallFrame; @@ -234,7 +236,15 @@ namespace JSC { #elif !ENABLE(CLASSIC_INTERPRETER) && !ENABLE(LLINT) bool canUseJIT() { return true; } // jit only #else - bool canUseJIT() { return m_canUseJIT; } + bool canUseJIT() { return m_canUseAssembler; } +#endif + +#if !ENABLE(YARR_JIT) + bool canUseRegExpJIT() { return false; } // interpreter only +#elif !ENABLE(CLASSIC_INTERPRETER) && !ENABLE(LLINT) + bool canUseRegExpJIT() { return true; } // jit only +#else + bool canUseRegExpJIT() { return m_canUseAssembler; } #endif OwnPtr<ParserArena> parserArena; @@ -252,7 +262,6 @@ namespace JSC { TimeoutChecker timeoutChecker; Terminator terminator; - Heap heap; JSValue exception; @@ -324,20 +333,18 @@ namespace JSC { JS_EXPORT_PRIVATE void startSampling(); JS_EXPORT_PRIVATE void stopSampling(); JS_EXPORT_PRIVATE void dumpSampleData(ExecState* exec); - void recompileAllJSFunctions(); RegExpCache* regExpCache() { return m_regExpCache; } #if ENABLE(REGEXP_TRACING) void addRegExpToTrace(PassRefPtr<RegExp> regExp); #endif JS_EXPORT_PRIVATE void dumpRegExpTrace(); - JS_EXPORT_PRIVATE void clearBuiltinStructures(); bool isCollectorBusy() { return heap.isBusy(); } JS_EXPORT_PRIVATE void releaseExecutableMemory(); #if ENABLE(GC_VALIDATION) bool isInitializingObject() const; - void setInitializingObject(bool); + void setInitializingObjectClass(const ClassInfo*); #endif #if CPU(X86) && ENABLE(JIT) @@ -369,11 +376,11 @@ namespace JSC { JSGlobalData(GlobalDataType, ThreadStackType, HeapSize); static JSGlobalData*& sharedInstanceInternal(); void createNativeThunk(); -#if ENABLE(JIT) && (ENABLE(CLASSIC_INTERPRETER) || ENABLE(LLINT)) - bool m_canUseJIT; +#if ENABLE(ASSEMBLER) && (ENABLE(CLASSIC_INTERPRETER) || ENABLE(LLINT)) + bool m_canUseAssembler; #endif #if ENABLE(GC_VALIDATION) - bool m_isInitializingObject; + const ClassInfo* m_initializingObjectClass; #endif bool m_inDefineOwnProperty; @@ -391,12 +398,12 @@ namespace JSC { #if ENABLE(GC_VALIDATION) inline bool JSGlobalData::isInitializingObject() const { - return m_isInitializingObject; + return !!m_initializingObjectClass; } - inline void JSGlobalData::setInitializingObject(bool initializingObject) + inline void JSGlobalData::setInitializingObjectClass(const ClassInfo* initializingObjectClass) { - m_isInitializingObject = initializingObject; + m_initializingObjectClass = initializingObjectClass; } #endif diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp index 8d3975848..0f74a8061 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp @@ -133,8 +133,7 @@ void JSGlobalObject::init(JSObject* thisValue) structure()->disableSpecificFunctionTracking(); - m_globalData = Heap::heap(this)->globalData(); - m_globalScopeChain.set(*m_globalData, this, ScopeChainNode::create(0, this, m_globalData.get(), this, thisValue)); + m_globalScopeChain.set(globalData(), this, ScopeChainNode::create(0, this, &globalData(), this, thisValue)); JSGlobalObject::globalExec()->init(0, 0, m_globalScopeChain.get(), CallFrame::noCaller(), 0, 0); @@ -460,7 +459,7 @@ DynamicGlobalObjectScope::DynamicGlobalObjectScope(JSGlobalData& globalData, JSG if (!m_dynamicGlobalObjectSlot) { #if ENABLE(ASSEMBLER) if (ExecutableAllocator::underMemoryPressure()) - globalData.recompileAllJSFunctions(); + globalData.heap.discardAllCompiledCode(); #endif m_dynamicGlobalObjectSlot = dynamicGlobalObject; diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h index cbc436e1a..d9fc81dc4 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObject.h +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h @@ -86,8 +86,6 @@ namespace JSC { protected: - RefPtr<JSGlobalData> m_globalData; - size_t m_registerArraySize; Register m_globalCallFrame[RegisterFile::CallFrameHeaderSize]; @@ -302,7 +300,7 @@ namespace JSC { void resetPrototype(JSGlobalData&, JSValue prototype); - JSGlobalData& globalData() const { return *m_globalData.get(); } + JSGlobalData& globalData() const { return *Heap::heap(this)->globalData(); } static Structure* createStructure(JSGlobalData& globalData, JSValue prototype) { @@ -358,7 +356,7 @@ namespace JSC { inline JSGlobalObject* asGlobalObject(JSValue value) { ASSERT(asObject(value)->isGlobalObject()); - return static_cast<JSGlobalObject*>(asObject(value)); + return jsCast<JSGlobalObject*>(asObject(value)); } inline void JSGlobalObject::setRegisters(WriteBarrier<Unknown>* registers, PassOwnArrayPtr<WriteBarrier<Unknown> > registerArray, size_t count) diff --git a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp index 75789e602..e8017b904 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved. * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) * Copyright (C) 2007 Maks Orlovich * @@ -295,17 +295,20 @@ static double parseInt(const UString& s, const CharType* data, int radix) number += digit; ++p; } - if (number >= mantissaOverflowLowerBound) { - if (radix == 10) - number = WTF::strtod<WTF::AllowTrailingJunk>(s.substringSharingImpl(firstDigitPosition, p - firstDigitPosition).utf8().data(), 0); - else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) - number = parseIntOverflow(s.substringSharingImpl(firstDigitPosition, p - firstDigitPosition).utf8().data(), p - firstDigitPosition, radix); - } // 12. If Z is empty, return NaN. if (!sawDigit) return std::numeric_limits<double>::quiet_NaN(); + // Alternate code path for certain large numbers. + if (number >= mantissaOverflowLowerBound) { + if (radix == 10) { + size_t parsedLength; + number = parseDouble(s.characters() + firstDigitPosition, p - firstDigitPosition, parsedLength); + } else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) + number = parseIntOverflow(s.substringSharingImpl(firstDigitPosition, p - firstDigitPosition).utf8().data(), p - firstDigitPosition, radix); + } + // 15. Return sign x number. return sign * number; } @@ -361,20 +364,10 @@ static double jsStrDecimalLiteral(const CharType*& data, const CharType* end) { ASSERT(data < end); - // Copy the sting into a null-terminated byte buffer, and call strtod. - Vector<char, 32> byteBuffer; - for (const CharType* characters = data; characters < end; ++characters) { - CharType character = *characters; - byteBuffer.append(isASCII(character) ? static_cast<char>(character) : 0); - } - byteBuffer.append(0); - char* endOfNumber; - double number = WTF::strtod<WTF::AllowTrailingJunk>(byteBuffer.data(), &endOfNumber); - - // Check if strtod found a number; if so return it. - ptrdiff_t consumed = endOfNumber - byteBuffer.data(); - if (consumed) { - data += consumed; + size_t parsedLength; + double number = parseDouble(data, end - data, parsedLength); + if (parsedLength) { + data += parsedLength; return number; } @@ -505,7 +498,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState* exec) { JSObject* thisObject = exec->hostThisValue().toThisObject(exec); JSObject* unwrappedObject = thisObject->unwrappedObject(); - if (!unwrappedObject->isGlobalObject() || static_cast<JSGlobalObject*>(unwrappedObject)->evalFunction() != exec->callee()) + if (!unwrappedObject->isGlobalObject() || jsCast<JSGlobalObject*>(unwrappedObject)->evalFunction() != exec->callee()) return throwVMError(exec, createEvalError(exec, "The \"this\" value passed to eval must be the global object from which eval originated")); JSValue x = exec->argument(0); @@ -525,11 +518,11 @@ EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState* exec) } EvalExecutable* eval = EvalExecutable::create(exec, makeSource(s), false); - JSObject* error = eval->compile(exec, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain()); + JSObject* error = eval->compile(exec, jsCast<JSGlobalObject*>(unwrappedObject)->globalScopeChain()); if (error) return throwVMError(exec, error); - return JSValue::encode(exec->interpreter()->execute(eval, exec, thisObject, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain())); + return JSValue::encode(exec->interpreter()->execute(eval, exec, thisObject, jsCast<JSGlobalObject*>(unwrappedObject)->globalScopeChain())); } EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec) diff --git a/Source/JavaScriptCore/runtime/JSGlobalThis.cpp b/Source/JavaScriptCore/runtime/JSGlobalThis.cpp index 8b2a7a1ef..abd31ac14 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalThis.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalThis.cpp @@ -48,9 +48,12 @@ void JSGlobalThis::visitChildren(JSCell* cell, SlotVisitor& visitor) visitor.append(&thisObject->m_unwrappedObject); } -JSGlobalObject* JSGlobalThis::unwrappedObject() +void JSGlobalThis::setUnwrappedObject(JSGlobalData& globalData, JSGlobalObject* globalObject) { - return m_unwrappedObject.get(); + ASSERT_ARG(globalObject, globalObject); + m_unwrappedObject.set(globalData, this, globalObject); + setPrototype(globalData, globalObject->prototype()); + resetInheritorID(); } } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSGlobalThis.h b/Source/JavaScriptCore/runtime/JSGlobalThis.h index fa5c2eb34..0ca99414a 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalThis.h +++ b/Source/JavaScriptCore/runtime/JSGlobalThis.h @@ -48,7 +48,7 @@ public: static JS_EXPORTDATA const JSC::ClassInfo s_info; - JSGlobalObject* unwrappedObject(); + JSGlobalObject* unwrappedObject() const { return m_unwrappedObject.get(); } protected: JSGlobalThis(JSGlobalData& globalData, Structure* structure) @@ -65,6 +65,9 @@ protected: JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); + JS_EXPORT_PRIVATE void setUnwrappedObject(JSGlobalData&, JSGlobalObject*); + +private: WriteBarrier<JSGlobalObject> m_unwrappedObject; }; diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp index 1bdb90ff6..500f3891a 100644 --- a/Source/JavaScriptCore/runtime/JSObject.cpp +++ b/Source/JavaScriptCore/runtime/JSObject.cpp @@ -203,7 +203,7 @@ bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prot { JSValue checkFor = this; if (this->isGlobalObject()) - checkFor = static_cast<JSGlobalObject*>(this)->globalExec()->thisValue(); + checkFor = jsCast<JSGlobalObject*>(this)->globalExec()->thisValue(); JSValue nextPrototype = prototype; while (nextPrototype && nextPrototype.isObject()) { @@ -217,7 +217,7 @@ bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prot bool JSObject::allowsAccessFrom(ExecState* exec) { - JSGlobalObject* globalObject = isGlobalThis() ? static_cast<JSGlobalThis*>(this)->unwrappedObject() : this->globalObject(); + JSGlobalObject* globalObject = isGlobalThis() ? jsCast<JSGlobalThis*>(this)->unwrappedObject() : this->globalObject(); return globalObject->globalObjectMethodTable()->allowsAccessFrom(globalObject, exec); } @@ -445,13 +445,13 @@ JSString* JSObject::toString(ExecState* exec) const JSObject* JSObject::toThisObject(JSCell* cell, ExecState*) { - return static_cast<JSObject*>(cell); + return jsCast<JSObject*>(cell); } JSObject* JSObject::unwrappedObject() { if (isGlobalThis()) - return static_cast<JSGlobalThis*>(this)->unwrappedObject(); + return jsCast<JSGlobalThis*>(this)->unwrappedObject(); return this; } @@ -541,12 +541,18 @@ NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarr Structure* JSObject::createInheritorID(JSGlobalData& globalData) { - m_inheritorID.set(globalData, this, createEmptyObjectStructure(globalData, structure()->globalObject(), this)); + JSGlobalObject* globalObject; + if (isGlobalThis()) + globalObject = static_cast<JSGlobalThis*>(this)->unwrappedObject(); + else + globalObject = structure()->globalObject(); + ASSERT(globalObject); + m_inheritorID.set(globalData, this, createEmptyObjectStructure(globalData, globalObject, this)); ASSERT(m_inheritorID->isEmpty()); return m_inheritorID.get(); } -void JSObject::allocatePropertyStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize) +PropertyStorage JSObject::growPropertyStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize) { ASSERT(newSize > oldSize); @@ -574,7 +580,7 @@ void JSObject::allocatePropertyStorage(JSGlobalData& globalData, size_t oldSize, } ASSERT(newPropertyStorage); - m_propertyStorage.set(globalData, this, newPropertyStorage); + return newPropertyStorage; } bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h index 3f3d281cf..d95860d62 100644 --- a/Source/JavaScriptCore/runtime/JSObject.h +++ b/Source/JavaScriptCore/runtime/JSObject.h @@ -212,8 +212,9 @@ namespace JSC { bool staticFunctionsReified() { return structure()->staticFunctionsReified(); } void reifyStaticFunctionsForDelete(ExecState* exec); - JS_EXPORT_PRIVATE void allocatePropertyStorage(JSGlobalData&, size_t oldSize, size_t newSize); + JS_EXPORT_PRIVATE PropertyStorage growPropertyStorage(JSGlobalData&, size_t oldSize, size_t newSize); bool isUsingInlineStorage() const { return static_cast<const void*>(m_propertyStorage.get()) == static_cast<const void*>(this + 1); } + void setPropertyStorage(JSGlobalData&, PropertyStorage, Structure*); void* addressOfPropertyStorage() { @@ -263,6 +264,11 @@ namespace JSC { // To instantiate objects you likely want JSFinalObject, below. // To create derived types you likely want JSNonFinalObject, below. JSObject(JSGlobalData&, Structure*, PropertyStorage inlineStorage); + + void resetInheritorID() + { + m_inheritorID.clear(); + } private: friend class LLIntOffsetsExtractor; @@ -447,6 +453,14 @@ inline bool JSObject::isGlobalThis() const return structure()->typeInfo().type() == GlobalThisType; } +inline void JSObject::setPropertyStorage(JSGlobalData& globalData, PropertyStorage storage, Structure* structure) +{ + ASSERT(storage); + ASSERT(structure); + setStructure(globalData, structure); + m_propertyStorage.set(globalData, this, storage); +} + inline JSObject* constructEmptyObject(ExecState* exec, Structure* structure) { return JSFinalObject::create(exec, structure); @@ -474,7 +488,7 @@ inline Structure* createEmptyObjectStructure(JSGlobalData& globalData, JSGlobalO inline JSObject* asObject(JSCell* cell) { ASSERT(cell->isObject()); - return static_cast<JSObject*>(cell); + return jsCast<JSObject*>(cell); } inline JSObject* asObject(JSValue value) @@ -658,10 +672,11 @@ inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifi if ((mode == PutModePut) && !isExtensible()) return false; - size_t currentCapacity = structure()->propertyStorageCapacity(); + PropertyStorage newStorage = propertyStorage(); + if (structure()->shouldGrowPropertyStorage()) + newStorage = growPropertyStorage(globalData, structure()->propertyStorageCapacity(), structure()->suggestedNewPropertyStorageSize()); offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, specificFunction); - if (currentCapacity != structure()->propertyStorageCapacity()) - allocatePropertyStorage(globalData, currentCapacity, structure()->propertyStorageCapacity()); + setPropertyStorage(globalData, newStorage, structure()); ASSERT(offset < structure()->propertyStorageCapacity()); putDirectOffset(globalData, offset, value); @@ -673,12 +688,13 @@ inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifi size_t offset; size_t currentCapacity = structure()->propertyStorageCapacity(); - if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, specificFunction, offset)) { + if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, specificFunction, offset)) { + PropertyStorage newStorage = propertyStorage(); if (currentCapacity != structure->propertyStorageCapacity()) - allocatePropertyStorage(globalData, currentCapacity, structure->propertyStorageCapacity()); + newStorage = growPropertyStorage(globalData, currentCapacity, structure->propertyStorageCapacity()); ASSERT(offset < structure->propertyStorageCapacity()); - setStructure(globalData, structure); + setPropertyStorage(globalData, newStorage, structure); putDirectOffset(globalData, offset, value); // This is a new property; transitions with specific values are not currently cachable, // so leave the slot in an uncachable state. @@ -722,13 +738,14 @@ inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifi if ((mode == PutModePut) && !isExtensible()) return false; - Structure* structure = Structure::addPropertyTransition(globalData, this->structure(), propertyName, attributes, specificFunction, offset); + PropertyStorage newStorage = propertyStorage(); + if (structure()->shouldGrowPropertyStorage()) + newStorage = growPropertyStorage(globalData, structure()->propertyStorageCapacity(), structure()->suggestedNewPropertyStorageSize()); - if (currentCapacity != structure->propertyStorageCapacity()) - allocatePropertyStorage(globalData, currentCapacity, structure->propertyStorageCapacity()); + Structure* structure = Structure::addPropertyTransition(globalData, this->structure(), propertyName, attributes, specificFunction, offset); ASSERT(offset < structure->propertyStorageCapacity()); - setStructure(globalData, structure); + setPropertyStorage(globalData, newStorage, structure); putDirectOffset(globalData, offset, value); // This is a new property; transitions with specific values are not currently cachable, // so leave the slot in an uncachable state. @@ -762,18 +779,20 @@ inline void JSObject::putDirect(JSGlobalData& globalData, const Identifier& prop inline void JSObject::putDirectWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) { ASSERT(!value.isGetterSetter() && !(attributes & Accessor)); - size_t currentCapacity = structure()->propertyStorageCapacity(); + PropertyStorage newStorage = propertyStorage(); + if (structure()->shouldGrowPropertyStorage()) + newStorage = growPropertyStorage(globalData, structure()->propertyStorageCapacity(), structure()->suggestedNewPropertyStorageSize()); size_t offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, getJSFunction(value)); - if (currentCapacity != structure()->propertyStorageCapacity()) - allocatePropertyStorage(globalData, currentCapacity, structure()->propertyStorageCapacity()); + setPropertyStorage(globalData, newStorage, structure()); putDirectOffset(globalData, offset, value); } inline void JSObject::transitionTo(JSGlobalData& globalData, Structure* newStructure) { + PropertyStorage newStorage = propertyStorage(); if (structure()->propertyStorageCapacity() != newStructure->propertyStorageCapacity()) - allocatePropertyStorage(globalData, structure()->propertyStorageCapacity(), newStructure->propertyStorageCapacity()); - setStructure(globalData, newStructure); + newStorage = growPropertyStorage(globalData, structure()->propertyStorageCapacity(), newStructure->propertyStorageCapacity()); + setPropertyStorage(globalData, newStorage, newStructure); } inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const diff --git a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h index 7530d7532..5b65e59f2 100644 --- a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h +++ b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h @@ -121,7 +121,7 @@ namespace JSC { ALWAYS_INLINE JSPropertyNameIterator* Register::propertyNameIterator() const { - return static_cast<JSPropertyNameIterator*>(jsValue().asCell()); + return jsCast<JSPropertyNameIterator*>(jsValue().asCell()); } } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSString.cpp b/Source/JavaScriptCore/runtime/JSString.cpp index e84ce3620..904cc4d3e 100644 --- a/Source/JavaScriptCore/runtime/JSString.cpp +++ b/Source/JavaScriptCore/runtime/JSString.cpp @@ -36,9 +36,9 @@ static const unsigned substringFromRopeCutoff = 4; const ClassInfo JSString::s_info = { "string", 0, 0, 0, CREATE_METHOD_TABLE(JSString) }; -void JSString::RopeBuilder::expand() +void JSRopeString::RopeBuilder::expand() { - ASSERT(m_index == JSString::s_maxInternalRopeLength); + ASSERT(m_index == JSRopeString::s_maxInternalRopeLength); JSString* jsString = m_jsString; m_jsString = jsStringBuilder(&m_globalData); m_index = 0; @@ -55,11 +55,18 @@ void JSString::visitChildren(JSCell* cell, SlotVisitor& visitor) { JSString* thisObject = jsCast<JSString*>(cell); Base::visitChildren(thisObject, visitor); - for (size_t i = 0; i < s_maxInternalRopeLength && thisObject->m_fibers[i]; ++i) - visitor.append(&thisObject->m_fibers[i]); + + if (thisObject->isRope()) + static_cast<JSRopeString*>(thisObject)->visitFibers(visitor); +} + +void JSRopeString::visitFibers(SlotVisitor& visitor) +{ + for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) + visitor.append(&m_fibers[i]); } -void JSString::resolveRope(ExecState* exec) const +void JSRopeString::resolveRope(ExecState* exec) const { ASSERT(isRope()); @@ -128,7 +135,7 @@ void JSString::resolveRope(ExecState* exec) const // Vector before performing any concatenation, but by working backwards we likely // only fill the queue with the number of substrings at any given level in a // rope-of-ropes.) -void JSString::resolveRopeSlowCase8(LChar* buffer) const +void JSRopeString::resolveRopeSlowCase8(LChar* buffer) const { LChar* position = buffer + m_length; // We will be working backwards over the rope. Vector<JSString*, 32> workQueue; // Putting strings into a Vector is only OK because there are no GC points in this method. @@ -144,8 +151,9 @@ void JSString::resolveRopeSlowCase8(LChar* buffer) const workQueue.removeLast(); if (currentFiber->isRope()) { - for (size_t i = 0; i < s_maxInternalRopeLength && currentFiber->m_fibers[i]; ++i) - workQueue.append(currentFiber->m_fibers[i].get()); + JSRopeString* currentFiberAsRope = static_cast<JSRopeString*>(currentFiber); + for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->m_fibers[i]; ++i) + workQueue.append(currentFiberAsRope->m_fibers[i].get()); continue; } @@ -159,7 +167,7 @@ void JSString::resolveRopeSlowCase8(LChar* buffer) const ASSERT(!isRope()); } -void JSString::resolveRopeSlowCase(UChar* buffer) const +void JSRopeString::resolveRopeSlowCase(UChar* buffer) const { UChar* position = buffer + m_length; // We will be working backwards over the rope. Vector<JSString*, 32> workQueue; // These strings are kept alive by the parent rope, so using a Vector is OK. @@ -172,8 +180,9 @@ void JSString::resolveRopeSlowCase(UChar* buffer) const workQueue.removeLast(); if (currentFiber->isRope()) { - for (size_t i = 0; i < s_maxInternalRopeLength && currentFiber->m_fibers[i]; ++i) - workQueue.append(currentFiber->m_fibers[i].get()); + JSRopeString* currentFiberAsRope = static_cast<JSRopeString*>(currentFiber); + for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->m_fibers[i]; ++i) + workQueue.append(currentFiberAsRope->m_fibers[i].get()); continue; } @@ -187,7 +196,7 @@ void JSString::resolveRopeSlowCase(UChar* buffer) const ASSERT(!isRope()); } -void JSString::outOfMemory(ExecState* exec) const +void JSRopeString::outOfMemory(ExecState* exec) const { for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) m_fibers[i].clear(); @@ -197,7 +206,7 @@ void JSString::outOfMemory(ExecState* exec) const throwOutOfMemoryError(exec); } -JSString* JSString::getIndexSlowCase(ExecState* exec, unsigned i) +JSString* JSRopeString::getIndexSlowCase(ExecState* exec, unsigned i) { ASSERT(isRope()); resolveRope(exec); diff --git a/Source/JavaScriptCore/runtime/JSString.h b/Source/JavaScriptCore/runtime/JSString.h index 32a32788a..10ec799e5 100644 --- a/Source/JavaScriptCore/runtime/JSString.h +++ b/Source/JavaScriptCore/runtime/JSString.h @@ -32,6 +32,7 @@ namespace JSC { class JSString; + class JSRopeString; class LLIntOffsetsExtractor; JSString* jsEmptyString(JSGlobalData*); @@ -58,55 +59,20 @@ namespace JSC { JSString* jsOwnedString(JSGlobalData*, const UString&); JSString* jsOwnedString(ExecState*, const UString&); - JSString* jsStringBuilder(JSGlobalData*); + JSRopeString* jsStringBuilder(JSGlobalData*); class JSString : public JSCell { public: friend class JIT; friend class JSGlobalData; friend class SpecializedThunkJIT; + friend class JSRopeString; friend struct ThunkHelpers; - friend JSString* jsStringBuilder(JSGlobalData*); typedef JSCell Base; static void destroy(JSCell*); - class RopeBuilder { - public: - RopeBuilder(JSGlobalData& globalData) - : m_globalData(globalData) - , m_jsString(jsStringBuilder(&globalData)) - , m_index(0) - { - } - - void append(JSString* jsString) - { - if (m_index == JSString::s_maxInternalRopeLength) - expand(); - m_jsString->m_fibers[m_index++].set(m_globalData, m_jsString, jsString); - m_jsString->m_length += jsString->m_length; - m_jsString->m_is8Bit = m_jsString->m_is8Bit && jsString->m_is8Bit; - } - - JSString* release() - { - JSString* tmp = m_jsString; - m_jsString = 0; - return tmp; - } - - unsigned length() { return m_jsString->m_length; } - - private: - void expand(); - - JSGlobalData& m_globalData; - JSString* m_jsString; - size_t m_index; - }; - private: JSString(JSGlobalData& globalData, PassRefPtr<StringImpl> value) : JSCell(globalData, globalData.stringStructure.get()) @@ -119,13 +85,6 @@ namespace JSC { { } - void finishCreation(JSGlobalData& globalData) - { - Base::finishCreation(globalData); - m_length = 0; - m_is8Bit = true; - } - void finishCreation(JSGlobalData& globalData, size_t length) { ASSERT(!m_value.isNull()); @@ -143,32 +102,14 @@ namespace JSC { Heap::heap(this)->reportExtraMemoryCost(cost); } - void finishCreation(JSGlobalData& globalData, JSString* s1, JSString* s2) - { - Base::finishCreation(globalData); - m_length = s1->length() + s2->length(); - m_is8Bit = (s1->is8Bit() && s2->is8Bit()); - m_fibers[0].set(globalData, this, s1); - m_fibers[1].set(globalData, this, s2); - } - - void finishCreation(JSGlobalData& globalData, JSString* s1, JSString* s2, JSString* s3) + protected: + void finishCreation(JSGlobalData& globalData) { Base::finishCreation(globalData); - m_length = s1->length() + s2->length() + s3->length(); - m_is8Bit = (s1->is8Bit() && s2->is8Bit() && s3->is8Bit()); - m_fibers[0].set(globalData, this, s1); - m_fibers[1].set(globalData, this, s2); - m_fibers[2].set(globalData, this, s3); - } - - static JSString* createNull(JSGlobalData& globalData) - { - JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData); - newString->finishCreation(globalData); - return newString; + m_length = 0; + m_is8Bit = true; } - + public: static JSString* create(JSGlobalData& globalData, PassRefPtr<StringImpl> value) { @@ -179,18 +120,6 @@ namespace JSC { newString->finishCreation(globalData, length, cost); return newString; } - static JSString* create(JSGlobalData& globalData, JSString* s1, JSString* s2) - { - JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData); - newString->finishCreation(globalData, s1, s2); - return newString; - } - static JSString* create(JSGlobalData& globalData, JSString* s1, JSString* s2, JSString* s3) - { - JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData); - newString->finishCreation(globalData, s1, s2, s3); - return newString; - } static JSString* createHasOtherOwner(JSGlobalData& globalData, PassRefPtr<StringImpl> value) { ASSERT(value); @@ -200,18 +129,8 @@ namespace JSC { return newString; } - const UString& value(ExecState* exec) const - { - if (isRope()) - resolveRope(exec); - return m_value; - } - const UString& tryGetValue() const - { - if (isRope()) - resolveRope(0); - return m_value; - } + const UString& value(ExecState*) const; + const UString& tryGetValue() const; unsigned length() { return m_length; } JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; @@ -226,7 +145,6 @@ namespace JSC { bool canGetIndex(unsigned i) { return i < m_length; } JSString* getIndex(ExecState*, unsigned); - JSString* getIndexSlowCase(ExecState*, unsigned); static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) { @@ -240,44 +158,145 @@ namespace JSC { static void visitChildren(JSCell*, SlotVisitor&); + protected: + bool isRope() const { return m_value.isNull(); } + bool is8Bit() const { return m_is8Bit; } + + // A string is represented either by a UString or a rope of fibers. + bool m_is8Bit : 1; + unsigned m_length; + mutable UString m_value; + private: friend class LLIntOffsetsExtractor; - JS_EXPORT_PRIVATE void resolveRope(ExecState*) const; - void resolveRopeSlowCase8(LChar*) const; - void resolveRopeSlowCase(UChar*) const; - void outOfMemory(ExecState*) const; - static JSObject* toThisObject(JSCell*, ExecState*); // Actually getPropertySlot, not getOwnPropertySlot (see JSCell). static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&); static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); - static const unsigned s_maxInternalRopeLength = 3; - - // A string is represented either by a UString or a rope of fibers. - bool m_is8Bit : 1; - unsigned m_length; - mutable UString m_value; - mutable FixedArray<WriteBarrier<JSString>, s_maxInternalRopeLength> m_fibers; - - bool isRope() const { return m_value.isNull(); } - bool is8Bit() const { return m_is8Bit; } UString& string() { ASSERT(!isRope()); return m_value; } friend JSValue jsString(ExecState*, JSString*, JSString*); - friend JSValue jsString(ExecState*, Register*, unsigned count); - friend JSValue jsStringFromArguments(ExecState*, JSValue thisValue); friend JSString* jsSubstring(ExecState*, JSString*, unsigned offset, unsigned length); }; + class JSRopeString : public JSString { + friend class JSString; + + friend JSRopeString* jsStringBuilder(JSGlobalData*); + + class RopeBuilder { + public: + RopeBuilder(JSGlobalData& globalData) + : m_globalData(globalData) + , m_jsString(jsStringBuilder(&globalData)) + , m_index(0) + { + } + + void append(JSString* jsString) + { + if (m_index == JSRopeString::s_maxInternalRopeLength) + expand(); + m_jsString->m_fibers[m_index++].set(m_globalData, m_jsString, jsString); + m_jsString->m_length += jsString->m_length; + m_jsString->m_is8Bit = m_jsString->m_is8Bit && jsString->m_is8Bit; + } + + JSRopeString* release() + { + JSRopeString* tmp = m_jsString; + m_jsString = 0; + return tmp; + } + + unsigned length() { return m_jsString->m_length; } + + private: + void expand(); + + JSGlobalData& m_globalData; + JSRopeString* m_jsString; + size_t m_index; + }; + + private: + JSRopeString(JSGlobalData& globalData) + : JSString(globalData) + { + } + + void finishCreation(JSGlobalData& globalData, JSString* s1, JSString* s2) + { + Base::finishCreation(globalData); + m_length = s1->length() + s2->length(); + m_is8Bit = (s1->is8Bit() && s2->is8Bit()); + m_fibers[0].set(globalData, this, s1); + m_fibers[1].set(globalData, this, s2); + } + + void finishCreation(JSGlobalData& globalData, JSString* s1, JSString* s2, JSString* s3) + { + Base::finishCreation(globalData); + m_length = s1->length() + s2->length() + s3->length(); + m_is8Bit = (s1->is8Bit() && s2->is8Bit() && s3->is8Bit()); + m_fibers[0].set(globalData, this, s1); + m_fibers[1].set(globalData, this, s2); + m_fibers[2].set(globalData, this, s3); + } + + void finishCreation(JSGlobalData& globalData) + { + JSString::finishCreation(globalData); + } + + static JSRopeString* createNull(JSGlobalData& globalData) + { + JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(globalData.heap)) JSRopeString(globalData); + newString->finishCreation(globalData); + return newString; + } + + public: + static JSString* create(JSGlobalData& globalData, JSString* s1, JSString* s2) + { + JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(globalData.heap)) JSRopeString(globalData); + newString->finishCreation(globalData, s1, s2); + return newString; + } + static JSString* create(JSGlobalData& globalData, JSString* s1, JSString* s2, JSString* s3) + { + JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(globalData.heap)) JSRopeString(globalData); + newString->finishCreation(globalData, s1, s2, s3); + return newString; + } + + void visitFibers(SlotVisitor&); + + private: + friend JSValue jsString(ExecState*, Register*, unsigned); + friend JSValue jsStringFromArguments(ExecState*, JSValue); + + JS_EXPORT_PRIVATE void resolveRope(ExecState*) const; + void resolveRopeSlowCase8(LChar*) const; + void resolveRopeSlowCase(UChar*) const; + void outOfMemory(ExecState*) const; + + JSString* getIndexSlowCase(ExecState*, unsigned); + + static const unsigned s_maxInternalRopeLength = 3; + + mutable FixedArray<WriteBarrier<JSString>, s_maxInternalRopeLength> m_fibers; + }; + JSString* asString(JSValue); inline JSString* asString(JSValue value) { ASSERT(value.asCell()->isString()); - return static_cast<JSString*>(value.asCell()); + return jsCast<JSString*>(value.asCell()); } inline JSString* jsEmptyString(JSGlobalData* globalData) @@ -285,14 +304,14 @@ namespace JSC { return globalData->smallStrings.emptyString(globalData); } - inline JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c) + ALWAYS_INLINE JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c) { if (c <= maxSingleCharacterString) return globalData->smallStrings.singleCharacterString(globalData, c); return JSString::create(*globalData, UString(&c, 1).impl()); } - inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset) + ALWAYS_INLINE JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset) { JSGlobalData* globalData = &exec->globalData(); ASSERT(offset < static_cast<unsigned>(s.length())); @@ -316,11 +335,25 @@ namespace JSC { return JSString::create(*globalData, s.impl()); } + inline const UString& JSString::value(ExecState* exec) const + { + if (isRope()) + static_cast<const JSRopeString*>(this)->resolveRope(exec); + return m_value; + } + + inline const UString& JSString::tryGetValue() const + { + if (isRope()) + static_cast<const JSRopeString*>(this)->resolveRope(0); + return m_value; + } + inline JSString* JSString::getIndex(ExecState* exec, unsigned i) { ASSERT(canGetIndex(i)); if (isRope()) - return getIndexSlowCase(exec, i); + return static_cast<JSRopeString*>(this)->getIndexSlowCase(exec, i); ASSERT(i < m_value.length()); return jsSingleCharacterSubstring(exec, m_value, i); } @@ -392,9 +425,9 @@ namespace JSC { return JSString::createHasOtherOwner(*globalData, s.impl()); } - inline JSString* jsStringBuilder(JSGlobalData* globalData) + inline JSRopeString* jsStringBuilder(JSGlobalData* globalData) { - return JSString::createNull(*globalData); + return JSRopeString::createNull(*globalData); } inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->globalData()); } @@ -458,10 +491,43 @@ namespace JSC { inline JSString* JSValue::toString(ExecState* exec) const { if (isString()) - return static_cast<JSString*>(asCell()); + return jsCast<JSString*>(asCell()); return toStringSlowCase(exec); } + inline UString JSValue::toUString(ExecState* exec) const + { + if (isString()) + return static_cast<JSString*>(asCell())->value(exec); + return toUStringSlowCase(exec); + } + + ALWAYS_INLINE UString inlineJSValueNotStringtoUString(const JSValue& value, ExecState* exec) + { + JSGlobalData& globalData = exec->globalData(); + if (value.isInt32()) + return globalData.numericStrings.add(value.asInt32()); + if (value.isDouble()) + return globalData.numericStrings.add(value.asDouble()); + if (value.isTrue()) + return globalData.propertyNames->trueKeyword.ustring(); + if (value.isFalse()) + return globalData.propertyNames->falseKeyword.ustring(); + if (value.isNull()) + return globalData.propertyNames->nullKeyword.ustring(); + if (value.isUndefined()) + return globalData.propertyNames->undefinedKeyword.ustring(); + return value.toString(exec)->value(exec); + } + + ALWAYS_INLINE UString JSValue::toUStringInline(ExecState* exec) const + { + if (isString()) + return static_cast<JSString*>(asCell())->value(exec); + + return inlineJSValueNotStringtoUString(*this, exec); + } + } // namespace JSC #endif // JSString_h diff --git a/Source/JavaScriptCore/runtime/JSStringJoiner.cpp b/Source/JavaScriptCore/runtime/JSStringJoiner.cpp new file mode 100644 index 000000000..ea260243b --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSStringJoiner.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JSStringJoiner.h" + +#include "ExceptionHelpers.h" +#include "JSString.h" +#include "ScopeChain.h" +#include <wtf/text/StringImpl.h> + + +namespace JSC { + +// The destination is 16bits, at least one string is 16 bits. +static inline void appendStringToData(UChar*& data, const UString& string) +{ + if (string.isNull()) + return; + + unsigned length = string.length(); + const StringImpl* stringImpl = string.impl(); + + if (stringImpl->is8Bit()) { + for (unsigned i = 0; i < length; ++i) { + *data = stringImpl->characters8()[i]; + ++data; + } + } else { + for (unsigned i = 0; i < length; ++i) { + *data = stringImpl->characters16()[i]; + ++data; + } + } +} + +// If the destination is 8bits, we know every string has to be 8bit. +static inline void appendStringToData(LChar*& data, const UString& string) +{ + if (string.isNull()) + return; + ASSERT(string.is8Bit()); + + unsigned length = string.length(); + const StringImpl* stringImpl = string.impl(); + + for (unsigned i = 0; i < length; ++i) { + *data = stringImpl->characters8()[i]; + ++data; + } +} + +template<typename CharacterType> +static inline PassRefPtr<StringImpl> joinStrings(const Vector<UString>& strings, const UString& separator, unsigned outputLength) +{ + ASSERT(outputLength); + + CharacterType* data; + RefPtr<StringImpl> outputStringImpl = StringImpl::tryCreateUninitialized(outputLength, data); + if (!outputStringImpl) + return PassRefPtr<StringImpl>(); + + const UString firstString = strings.first(); + appendStringToData(data, firstString); + + for (size_t i = 1; i < strings.size(); ++i) { + appendStringToData(data, separator); + appendStringToData(data, strings[i]); + } + + ASSERT(data == (outputStringImpl->getCharacters<CharacterType>() + outputStringImpl->length())); + return outputStringImpl.release(); +} + +JSValue JSStringJoiner::build(ExecState* exec) +{ + if (!m_isValid) + return throwOutOfMemoryError(exec); + + if (!m_strings.size()) + return jsEmptyString(exec); + + size_t separatorLength = m_separator.length(); + // FIXME: add special cases of joinStrings() for (separatorLength == 0) and (separatorLength == 1). + ASSERT(m_strings.size() > 0); + size_t totalSeparactorsLength = separatorLength * (m_strings.size() - 1); + size_t outputStringSize = totalSeparactorsLength + m_cumulatedStringsLength; + + if (!outputStringSize) + return jsEmptyString(exec); + + RefPtr<StringImpl> outputStringImpl; + if (m_is8Bits) + outputStringImpl = joinStrings<LChar>(m_strings, m_separator, outputStringSize); + else + outputStringImpl = joinStrings<UChar>(m_strings, m_separator, outputStringSize); + + if (!outputStringImpl) + return throwOutOfMemoryError(exec); + + return JSString::create(exec->globalData(), outputStringImpl.release()); +} + +} diff --git a/Source/JavaScriptCore/runtime/JSStringJoiner.h b/Source/JavaScriptCore/runtime/JSStringJoiner.h new file mode 100644 index 000000000..49f846c1f --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSStringJoiner.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSStringJoiner_h +#define JSStringJoiner_h + +#include "JSValue.h" +#include "UString.h" +#include <wtf/Vector.h> + +namespace JSC { + +class ExecState; + + +class JSStringJoiner { +public: + JSStringJoiner(const UString& separator, size_t stringCount); + + void append(const UString&); + JSValue build(ExecState*); + +private: + UString m_separator; + Vector<UString> m_strings; + + unsigned m_cumulatedStringsLength; + bool m_isValid; + bool m_is8Bits; +}; + +inline JSStringJoiner::JSStringJoiner(const UString& separator, size_t stringCount) + : m_separator(separator) + , m_cumulatedStringsLength(0) + , m_isValid(true) + , m_is8Bits(m_separator.is8Bit()) +{ + ASSERT(!m_separator.isNull()); + m_isValid = m_strings.tryReserveCapacity(stringCount); +} + +inline void JSStringJoiner::append(const UString& str) +{ + if (!m_isValid) + return; + + m_strings.uncheckedAppend(str); + if (!str.isNull()) { + m_cumulatedStringsLength += str.length(); + m_is8Bits = m_is8Bits && str.is8Bit(); + } +} + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/JSValue.cpp b/Source/JavaScriptCore/runtime/JSValue.cpp index 36697c60c..088f214b9 100644 --- a/Source/JavaScriptCore/runtime/JSValue.cpp +++ b/Source/JavaScriptCore/runtime/JSValue.cpp @@ -260,19 +260,20 @@ bool JSValue::isValidCallee() JSString* JSValue::toStringSlowCase(ExecState* exec) const { + JSGlobalData& globalData = exec->globalData(); ASSERT(!isString()); if (isInt32()) - return jsString(&exec->globalData(), exec->globalData().numericStrings.add(asInt32())); + return jsString(&globalData, globalData.numericStrings.add(asInt32())); if (isDouble()) - return jsString(&exec->globalData(), exec->globalData().numericStrings.add(asDouble())); + return jsString(&globalData, globalData.numericStrings.add(asDouble())); if (isTrue()) - return jsNontrivialString(exec, exec->propertyNames().trueKeyword.ustring()); + return globalData.smallStrings.trueString(&globalData); if (isFalse()) - return jsNontrivialString(exec, exec->propertyNames().falseKeyword.ustring()); + return globalData.smallStrings.falseString(&globalData); if (isNull()) - return jsNontrivialString(exec, exec->propertyNames().nullKeyword.ustring()); + return globalData.smallStrings.nullString(&globalData); if (isUndefined()) - return jsNontrivialString(exec, exec->propertyNames().undefined.ustring()); + return globalData.smallStrings.undefinedString(&globalData); ASSERT(isCell()); JSValue value = asCell()->toPrimitive(exec, PreferString); @@ -282,4 +283,9 @@ JSString* JSValue::toStringSlowCase(ExecState* exec) const return value.toString(exec); } +UString JSValue::toUStringSlowCase(ExecState* exec) const +{ + return inlineJSValueNotStringtoUString(*this, exec); +} + } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSValue.h b/Source/JavaScriptCore/runtime/JSValue.h index a6f359360..7facb9353 100644 --- a/Source/JavaScriptCore/runtime/JSValue.h +++ b/Source/JavaScriptCore/runtime/JSValue.h @@ -173,6 +173,7 @@ namespace JSC { // Querying the type. bool isEmpty() const; + bool isFunction() const; bool isUndefined() const; bool isNull() const; bool isUndefinedOrNull() const; @@ -202,6 +203,8 @@ namespace JSC { // been set in the ExecState already. double toNumber(ExecState*) const; JSString* toString(ExecState*) const; + UString toUString(ExecState*) const; + UString toUStringInline(ExecState*) const; JSObject* toObject(ExecState*) const; JSObject* toObject(ExecState*, JSGlobalObject*) const; @@ -250,6 +253,7 @@ namespace JSC { inline const JSValue asValue() const { return *this; } JS_EXPORT_PRIVATE double toNumberSlowCase(ExecState*) const; JS_EXPORT_PRIVATE JSString* toStringSlowCase(ExecState*) const; + JS_EXPORT_PRIVATE UString toUStringSlowCase(ExecState*) const; JS_EXPORT_PRIVATE JSObject* toObjectSlowCase(ExecState*, JSGlobalObject*) const; JS_EXPORT_PRIVATE JSObject* toThisObjectSlowCase(ExecState*) const; diff --git a/Source/JavaScriptCore/runtime/LiteralParser.cpp b/Source/JavaScriptCore/runtime/LiteralParser.cpp index 3bde3ff08..e1f85cefe 100644 --- a/Source/JavaScriptCore/runtime/LiteralParser.cpp +++ b/Source/JavaScriptCore/runtime/LiteralParser.cpp @@ -529,16 +529,8 @@ TokenType LiteralParser<CharType>::Lexer::lexNumber(LiteralParserToken<CharType> token.type = TokNumber; token.end = m_ptr; - Vector<char, 64> buffer(token.end - token.start + 1); - int i; - for (i = 0; i < token.end - token.start; i++) { - ASSERT(static_cast<char>(token.start[i]) == token.start[i]); - buffer[i] = static_cast<char>(token.start[i]); - } - buffer[i] = 0; - char* end; - token.numberToken = WTF::strtod<WTF::AllowTrailingJunk>(buffer.data(), &end); - ASSERT(buffer.data() + (token.end - token.start) == end); + size_t parsedLength; + token.numberToken = parseDouble(token.start, token.end - token.start, parsedLength); return TokNumber; } diff --git a/Source/JavaScriptCore/runtime/Lookup.cpp b/Source/JavaScriptCore/runtime/Lookup.cpp index 55c048fa3..b935eb260 100644 --- a/Source/JavaScriptCore/runtime/Lookup.cpp +++ b/Source/JavaScriptCore/runtime/Lookup.cpp @@ -76,15 +76,7 @@ bool setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* if (thisObj->staticFunctionsReified()) return false; - JSFunction* function; - JSGlobalObject* globalObject = thisObj->globalObject(); -#if ENABLE(JIT) - if (exec->globalData().canUseJIT() && entry->intrinsic() != NoIntrinsic) - function = JSFunction::create(exec, globalObject, entry->functionLength(), propertyName, exec->globalData().getHostFunction(entry->function(), entry->intrinsic())); - else -#endif - function = JSFunction::create(exec, globalObject, entry->functionLength(), propertyName, entry->function()); - + JSFunction* function = JSFunction::create(exec, thisObj->globalObject(), entry->functionLength(), propertyName, entry->function(), entry->intrinsic()); thisObj->putDirect(exec->globalData(), propertyName, function, entry->attributes()); location = thisObj->getDirectLocation(exec->globalData(), propertyName); } diff --git a/Source/JavaScriptCore/runtime/MatchResult.h b/Source/JavaScriptCore/runtime/MatchResult.h new file mode 100644 index 000000000..d87c8516b --- /dev/null +++ b/Source/JavaScriptCore/runtime/MatchResult.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MatchResult_h +#define MatchResult_h + +typedef uint64_t EncodedMatchResult; + +struct MatchResult { + ALWAYS_INLINE MatchResult(size_t start, size_t end) + : start(start) + , end(end) + { + } + + explicit ALWAYS_INLINE MatchResult(EncodedMatchResult encoded) + { + union u { + uint64_t encoded; + struct s { + size_t start; + size_t end; + } split; + } value; + value.encoded = encoded; + start = value.split.start; + end = value.split.end; + } + + ALWAYS_INLINE static MatchResult failed() + { + return MatchResult(WTF::notFound, 0); + } + + ALWAYS_INLINE operator bool() + { + return start != WTF::notFound; + } + + ALWAYS_INLINE bool empty() + { + return start == end; + } + + size_t start; + size_t end; +}; + +#endif diff --git a/Source/JavaScriptCore/runtime/NumberPrototype.cpp b/Source/JavaScriptCore/runtime/NumberPrototype.cpp index 8f8c3c00f..060a80107 100644 --- a/Source/JavaScriptCore/runtime/NumberPrototype.cpp +++ b/Source/JavaScriptCore/runtime/NumberPrototype.cpp @@ -148,7 +148,7 @@ static ALWAYS_INLINE bool getIntegerArgumentInRange(ExecState* exec, int low, in typedef char RadixBuffer[2180]; // Mapping from integers 0..35 to digit identifying this value, for radix 2..36. -static const char* const radixDigits = "0123456789abcdefghijklmnopqrstuvwxyz"; +static const char radixDigits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; static char* toStringWithRadix(RadixBuffer& buffer, double number, unsigned radix) { @@ -339,6 +339,31 @@ static char* toStringWithRadix(RadixBuffer& buffer, double number, unsigned radi return startOfResultString; } +static UString toUStringWithRadix(int32_t number, unsigned radix) +{ + LChar buf[1 + 32]; // Worst case is radix == 2, which gives us 32 digits + sign. + LChar* end = buf + WTF_ARRAY_LENGTH(buf); + LChar* p = end; + + bool negative = false; + uint32_t positiveNumber = number; + if (number < 0) { + negative = true; + positiveNumber = -number; + } + + while (positiveNumber) { + uint32_t index = positiveNumber % radix; + ASSERT(index < sizeof(radixDigits)); + *--p = static_cast<LChar>(radixDigits[index]); + positiveNumber /= radix; + } + if (negative) + *--p = '-'; + + return UString(p, static_cast<unsigned>(end - p)); +} + // toExponential converts a number to a string, always formatting as an expoential. // This method takes an optional argument specifying a number of *decimal places* // to round the significand to (or, put another way, this method optionally rounds @@ -431,41 +456,63 @@ EncodedJSValue JSC_HOST_CALL numberProtoFuncToPrecision(ExecState* exec) return JSValue::encode(jsString(exec, UString(numberToFixedPrecisionString(x, significantFigures, buffer)))); } -EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState* exec) +static inline int32_t extractRadixFromArgs(ExecState* exec) { - double x; - if (!toThisNumber(exec->hostThisValue(), x)) - return throwVMTypeError(exec); - JSValue radixValue = exec->argument(0); - int radix; + int32_t radix; if (radixValue.isInt32()) radix = radixValue.asInt32(); else if (radixValue.isUndefined()) radix = 10; else - radix = static_cast<int>(radixValue.toInteger(exec)); // nan -> 0 + radix = static_cast<int32_t>(radixValue.toInteger(exec)); // nan -> 0 - if (radix == 10) - return JSValue::encode(jsNumber(x).toString(exec)); + return radix; +} - // Fast path for number to character conversion. - if (radix == 36) { - unsigned c = static_cast<unsigned>(x); - if (c == x && c < 36) { - JSGlobalData* globalData = &exec->globalData(); - return JSValue::encode(globalData->smallStrings.singleCharacterString(globalData, radixDigits[c])); - } +static inline EncodedJSValue integerValueToString(ExecState* exec, int32_t radix, int32_t value) +{ + // A negative value casted to unsigned would be bigger than 36 (the max radix). + if (static_cast<unsigned>(value) < static_cast<unsigned>(radix)) { + ASSERT(value <= 36); + ASSERT(value >= 0); + JSGlobalData* globalData = &exec->globalData(); + return JSValue::encode(globalData->smallStrings.singleCharacterString(globalData, radixDigits[value])); } + if (radix == 10) { + JSGlobalData* globalData = &exec->globalData(); + return JSValue::encode(jsString(globalData, globalData->numericStrings.add(value))); + } + + return JSValue::encode(jsString(exec, toUStringWithRadix(value, radix))); + +} + +EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState* exec) +{ + double doubleValue; + if (!toThisNumber(exec->hostThisValue(), doubleValue)) + return throwVMTypeError(exec); + + int32_t radix = extractRadixFromArgs(exec); if (radix < 2 || radix > 36) return throwVMError(exec, createRangeError(exec, "toString() radix argument must be between 2 and 36")); - if (!isfinite(x)) - return JSValue::encode(jsString(exec, UString::number(x))); + int32_t integerValue = static_cast<int32_t>(doubleValue); + if (integerValue == doubleValue) + return integerValueToString(exec, radix, integerValue); + + if (radix == 10) { + JSGlobalData* globalData = &exec->globalData(); + return JSValue::encode(jsString(globalData, globalData->numericStrings.add(doubleValue))); + } + + if (!isfinite(doubleValue)) + return JSValue::encode(jsString(exec, UString::number(doubleValue))); RadixBuffer s; - return JSValue::encode(jsString(exec, toStringWithRadix(s, x, radix))); + return JSValue::encode(jsString(exec, toStringWithRadix(s, doubleValue, radix))); } EncodedJSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState* exec) diff --git a/Source/JavaScriptCore/runtime/NumericStrings.h b/Source/JavaScriptCore/runtime/NumericStrings.h index d65f14265..7fa20c44d 100644 --- a/Source/JavaScriptCore/runtime/NumericStrings.h +++ b/Source/JavaScriptCore/runtime/NumericStrings.h @@ -34,7 +34,7 @@ namespace JSC { class NumericStrings { public: - UString add(double d) + ALWAYS_INLINE UString add(double d) { CacheEntry<double>& entry = lookup(d); if (d == entry.key && !entry.value.isNull()) @@ -44,7 +44,7 @@ namespace JSC { return entry.value; } - UString add(int i) + ALWAYS_INLINE UString add(int i) { if (static_cast<unsigned>(i) < cacheSize) return lookupSmallString(static_cast<unsigned>(i)); @@ -56,7 +56,7 @@ namespace JSC { return entry.value; } - UString add(unsigned i) + ALWAYS_INLINE UString add(unsigned i) { if (i < cacheSize) return lookupSmallString(static_cast<unsigned>(i)); @@ -79,7 +79,7 @@ namespace JSC { CacheEntry<double>& lookup(double d) { return doubleCache[WTF::FloatHash<double>::hash(d) & (cacheSize - 1)]; } CacheEntry<int>& lookup(int i) { return intCache[WTF::IntHash<int>::hash(i) & (cacheSize - 1)]; } CacheEntry<unsigned>& lookup(unsigned i) { return unsignedCache[WTF::IntHash<unsigned>::hash(i) & (cacheSize - 1)]; } - const UString& lookupSmallString(unsigned i) + ALWAYS_INLINE const UString& lookupSmallString(unsigned i) { ASSERT(i < cacheSize); if (smallIntCache[i].isNull()) diff --git a/Source/JavaScriptCore/runtime/ObjectPrototype.cpp b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp index 6ad312c7c..e980ac590 100644 --- a/Source/JavaScriptCore/runtime/ObjectPrototype.cpp +++ b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp @@ -255,7 +255,17 @@ EncodedJSValue JSC_HOST_CALL objectProtoFuncToString(ExecState* exec) if (thisValue.isUndefinedOrNull()) return JSValue::encode(jsNontrivialString(exec, thisValue.isUndefined() ? "[object Undefined]" : "[object Null]")); JSObject* thisObject = thisValue.toObject(exec); - return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable()->className(thisObject), "]")); + + JSString* result = thisObject->structure()->objectToStringValue(); + if (!result) { + RefPtr<StringImpl> newString = WTF::tryMakeString("[object ", thisObject->methodTable()->className(thisObject), "]"); + if (!newString) + return JSValue::encode(throwOutOfMemoryError(exec)); + + result = jsNontrivialString(exec, newString.release()); + thisObject->structure()->setObjectToStringValue(exec->globalData(), thisObject, result); + } + return JSValue::encode(result); } } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Operations.cpp b/Source/JavaScriptCore/runtime/Operations.cpp index 459feb466..4cb9de505 100644 --- a/Source/JavaScriptCore/runtime/Operations.cpp +++ b/Source/JavaScriptCore/runtime/Operations.cpp @@ -58,25 +58,26 @@ NEVER_INLINE JSValue jsAddSlowCase(CallFrame* callFrame, JSValue v1, JSValue v2) JSValue jsTypeStringForValue(CallFrame* callFrame, JSValue v) { + JSGlobalData& globalData = callFrame->globalData(); if (v.isUndefined()) - return jsNontrivialString(callFrame, "undefined"); + return globalData.smallStrings.undefinedString(&globalData); if (v.isBoolean()) - return jsNontrivialString(callFrame, "boolean"); + return globalData.smallStrings.booleanString(&globalData); if (v.isNumber()) - return jsNontrivialString(callFrame, "number"); + return globalData.smallStrings.numberString(&globalData); if (v.isString()) - return jsNontrivialString(callFrame, "string"); + return globalData.smallStrings.stringString(&globalData); if (v.isObject()) { // Return "undefined" for objects that should be treated // as null when doing comparisons. if (asObject(v)->structure()->typeInfo().masqueradesAsUndefined()) - return jsNontrivialString(callFrame, "undefined"); + return globalData.smallStrings.undefinedString(&globalData); CallData callData; JSObject* object = asObject(v); if (object->methodTable()->getCallData(object, callData) != CallTypeNone) - return jsNontrivialString(callFrame, "function"); + return globalData.smallStrings.functionString(&globalData); } - return jsNontrivialString(callFrame, "object"); + return globalData.smallStrings.objectString(&globalData); } bool jsIsObjectType(JSValue v) diff --git a/Source/JavaScriptCore/runtime/Operations.h b/Source/JavaScriptCore/runtime/Operations.h index 945283899..b2081f3dd 100644 --- a/Source/JavaScriptCore/runtime/Operations.h +++ b/Source/JavaScriptCore/runtime/Operations.h @@ -47,7 +47,7 @@ namespace JSC { if ((length1 + length2) < length1) return throwOutOfMemoryError(exec); - return JSString::create(globalData, s1, s2); + return JSRopeString::create(globalData, s1, s2); } ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2, const UString& u3) @@ -69,13 +69,13 @@ namespace JSC { if ((length1 + length2 + length3) < length3) return throwOutOfMemoryError(exec); - return JSString::create(exec->globalData(), jsString(globalData, u1), jsString(globalData, u2), jsString(globalData, u3)); + return JSRopeString::create(exec->globalData(), jsString(globalData, u1), jsString(globalData, u2), jsString(globalData, u3)); } ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count) { JSGlobalData* globalData = &exec->globalData(); - JSString::RopeBuilder ropeBuilder(*globalData); + JSRopeString::RopeBuilder ropeBuilder(*globalData); unsigned oldLength = 0; @@ -93,7 +93,7 @@ namespace JSC { ALWAYS_INLINE JSValue jsStringFromArguments(ExecState* exec, JSValue thisValue) { JSGlobalData* globalData = &exec->globalData(); - JSString::RopeBuilder ropeBuilder(*globalData); + JSRopeString::RopeBuilder ropeBuilder(*globalData); ropeBuilder.append(thisValue.toString(exec)); unsigned oldLength = 0; diff --git a/Source/JavaScriptCore/runtime/Options.cpp b/Source/JavaScriptCore/runtime/Options.cpp index c4bf39db4..8f5a05067 100644 --- a/Source/JavaScriptCore/runtime/Options.cpp +++ b/Source/JavaScriptCore/runtime/Options.cpp @@ -75,6 +75,7 @@ double osrExitProminenceForFrequentExitSite; unsigned largeFailCountThresholdBase; unsigned largeFailCountThresholdBaseForLoop; +unsigned forcedOSRExitCountForReoptimization; unsigned reoptimizationRetryCounterMax; unsigned reoptimizationRetryCounterStep; @@ -174,8 +175,9 @@ void initializeOptions() SET(osrExitProminenceForFrequentExitSite, 0.3); - SET(largeFailCountThresholdBase, 20); - SET(largeFailCountThresholdBaseForLoop, 1); + SET(largeFailCountThresholdBase, 20); + SET(largeFailCountThresholdBaseForLoop, 1); + SET(forcedOSRExitCountForReoptimization, 250); SET(reoptimizationRetryCounterStep, 1); diff --git a/Source/JavaScriptCore/runtime/Options.h b/Source/JavaScriptCore/runtime/Options.h index fae6a7376..d1ad2ca87 100644 --- a/Source/JavaScriptCore/runtime/Options.h +++ b/Source/JavaScriptCore/runtime/Options.h @@ -61,6 +61,7 @@ extern double osrExitProminenceForFrequentExitSite; extern unsigned largeFailCountThresholdBase; extern unsigned largeFailCountThresholdBaseForLoop; +extern unsigned forcedOSRExitCountForReoptimization; extern unsigned reoptimizationRetryCounterMax; extern unsigned reoptimizationRetryCounterStep; diff --git a/Source/JavaScriptCore/runtime/PropertyNameArray.cpp b/Source/JavaScriptCore/runtime/PropertyNameArray.cpp index 8efb4065e..9bae94097 100644 --- a/Source/JavaScriptCore/runtime/PropertyNameArray.cpp +++ b/Source/JavaScriptCore/runtime/PropertyNameArray.cpp @@ -45,7 +45,7 @@ void PropertyNameArray::add(StringImpl* identifier) for (size_t i = 0; i < size; ++i) m_set.add(m_data->propertyNameVector()[i].impl()); } - if (!m_set.add(identifier).second) + if (!m_set.add(identifier).isNewEntry) return; } diff --git a/Source/JavaScriptCore/runtime/RegExp.cpp b/Source/JavaScriptCore/runtime/RegExp.cpp index 1a3362b2d..b0f67607e 100644 --- a/Source/JavaScriptCore/runtime/RegExp.cpp +++ b/Source/JavaScriptCore/runtime/RegExp.cpp @@ -131,11 +131,11 @@ void RegExpFunctionalTestCollector::outputOneTest(RegExp* regExp, UString s, int outputEscapedUString(s); fprintf(m_file, "\", %d, %d, (", startOffset, result); for (unsigned i = 0; i <= regExp->numSubpatterns(); i++) { - int subPatternBegin = ovector[i * 2]; - int subPatternEnd = ovector[i * 2 + 1]; - if (subPatternBegin == -1) - subPatternEnd = -1; - fprintf(m_file, "%d, %d", subPatternBegin, subPatternEnd); + int subpatternBegin = ovector[i * 2]; + int subpatternEnd = ovector[i * 2 + 1]; + if (subpatternBegin == -1) + subpatternEnd = -1; + fprintf(m_file, "%d, %d", subpatternBegin, subpatternEnd); if (i < regExp->numSubpatterns()) fputs(", ", m_file); } @@ -217,13 +217,6 @@ void RegExpFunctionalTestCollector::outputEscapedUString(const UString& s, bool } #endif -struct RegExpRepresentation { -#if ENABLE(YARR_JIT) - Yarr::YarrCodeBlock m_regExpJITCode; -#endif - OwnPtr<Yarr::BytecodePattern> m_regExpBytecode; -}; - RegExp::RegExp(JSGlobalData& globalData, const UString& patternString, RegExpFlags flags) : JSCell(globalData, globalData.regExpStructure.get()) , m_state(NotCompiled) @@ -279,23 +272,22 @@ void RegExp::compile(JSGlobalData* globalData, Yarr::YarrCharSize charSize) } ASSERT(m_numSubpatterns == pattern.m_numSubpatterns); - if (!m_representation) { + if (!hasCode()) { ASSERT(m_state == NotCompiled); - m_representation = adoptPtr(new RegExpRepresentation); globalData->regExpCache()->addToStrongCache(this); m_state = ByteCode; } #if ENABLE(YARR_JIT) - if (!pattern.m_containsBackreferences && globalData->canUseJIT()) { - Yarr::jitCompile(pattern, charSize, globalData, m_representation->m_regExpJITCode); + if (!pattern.m_containsBackreferences && globalData->canUseRegExpJIT()) { + Yarr::jitCompile(pattern, charSize, globalData, m_regExpJITCode); #if ENABLE(YARR_JIT_DEBUG) - if (!m_representation->m_regExpJITCode.isFallBack()) + if (!m_regExpJITCode.isFallBack()) m_state = JITCode; else m_state = ByteCode; #else - if (!m_representation->m_regExpJITCode.isFallBack()) { + if (!m_regExpJITCode.isFallBack()) { m_state = JITCode; return; } @@ -305,22 +297,18 @@ void RegExp::compile(JSGlobalData* globalData, Yarr::YarrCharSize charSize) UNUSED_PARAM(charSize); #endif - m_representation->m_regExpBytecode = Yarr::byteCompile(pattern, &globalData->m_regExpAllocator); + m_regExpBytecode = Yarr::byteCompile(pattern, &globalData->m_regExpAllocator); } void RegExp::compileIfNecessary(JSGlobalData& globalData, Yarr::YarrCharSize charSize) { - // If the state is NotCompiled or ParseError, then there is no representation. - // If there is a representation, and the state must be either JITCode or ByteCode. - ASSERT(!!m_representation == (m_state == JITCode || m_state == ByteCode)); - - if (m_representation) { + if (hasCode()) { #if ENABLE(YARR_JIT) if (m_state != JITCode) return; - if ((charSize == Yarr::Char8) && (m_representation->m_regExpJITCode.has8BitCode())) + if ((charSize == Yarr::Char8) && (m_regExpJITCode.has8BitCode())) return; - if ((charSize == Yarr::Char16) && (m_representation->m_regExpJITCode.has16BitCode())) + if ((charSize == Yarr::Char16) && (m_regExpJITCode.has16BitCode())) return; #else return; @@ -330,7 +318,7 @@ void RegExp::compileIfNecessary(JSGlobalData& globalData, Yarr::YarrCharSize cha compile(&globalData, charSize); } -int RegExp::match(JSGlobalData& globalData, const UString& s, unsigned startOffset, Vector<int, 32>* ovector) +int RegExp::match(JSGlobalData& globalData, const UString& s, unsigned startOffset, Vector<int, 32>& ovector) { #if ENABLE(REGEXP_TRACING) m_rtMatchCallCount++; @@ -340,30 +328,22 @@ int RegExp::match(JSGlobalData& globalData, const UString& s, unsigned startOffs compileIfNecessary(globalData, s.is8Bit() ? Yarr::Char8 : Yarr::Char16); int offsetVectorSize = (m_numSubpatterns + 1) * 2; - int* offsetVector; - Vector<int, 32> nonReturnedOvector; - if (ovector) { - ovector->resize(offsetVectorSize); - offsetVector = ovector->data(); - } else { - nonReturnedOvector.resize(offsetVectorSize); - offsetVector = nonReturnedOvector.data(); - } - ASSERT(offsetVector); + ovector.resize(offsetVectorSize); + int* offsetVector = ovector.data(); int result; #if ENABLE(YARR_JIT) if (m_state == JITCode) { if (s.is8Bit()) - result = Yarr::execute(m_representation->m_regExpJITCode, s.characters8(), startOffset, s.length(), offsetVector); + result = m_regExpJITCode.execute(s.characters8(), startOffset, s.length(), offsetVector).start; else - result = Yarr::execute(m_representation->m_regExpJITCode, s.characters16(), startOffset, s.length(), offsetVector); + result = m_regExpJITCode.execute(s.characters16(), startOffset, s.length(), offsetVector).start; #if ENABLE(YARR_JIT_DEBUG) matchCompareWithInterpreter(s, startOffset, offsetVector, result); #endif } else #endif - result = Yarr::interpret(m_representation->m_regExpBytecode.get(), s, startOffset, s.length(), reinterpret_cast<unsigned*>(offsetVector)); + result = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, reinterpret_cast<unsigned*>(offsetVector)); // FIXME: The YARR engine should handle unsigned or size_t length matches. // The YARR Interpreter is "unsigned" clean, while the YARR JIT hasn't been addressed. @@ -404,12 +384,113 @@ int RegExp::match(JSGlobalData& globalData, const UString& s, unsigned startOffs return result; } +void RegExp::compileMatchOnly(JSGlobalData* globalData, Yarr::YarrCharSize charSize) +{ + Yarr::YarrPattern pattern(m_patternString, ignoreCase(), multiline(), &m_constructionError); + if (m_constructionError) { + ASSERT_NOT_REACHED(); + m_state = ParseError; + return; + } + ASSERT(m_numSubpatterns == pattern.m_numSubpatterns); + + if (!hasCode()) { + ASSERT(m_state == NotCompiled); + globalData->regExpCache()->addToStrongCache(this); + m_state = ByteCode; + } + +#if ENABLE(YARR_JIT) + if (!pattern.m_containsBackreferences && globalData->canUseRegExpJIT()) { + Yarr::jitCompile(pattern, charSize, globalData, m_regExpJITCode, Yarr::MatchOnly); +#if ENABLE(YARR_JIT_DEBUG) + if (!m_regExpJITCode.isFallBack()) + m_state = JITCode; + else + m_state = ByteCode; +#else + if (!m_regExpJITCode.isFallBack()) { + m_state = JITCode; + return; + } +#endif + } +#else + UNUSED_PARAM(charSize); +#endif + + m_regExpBytecode = Yarr::byteCompile(pattern, &globalData->m_regExpAllocator); +} + +void RegExp::compileIfNecessaryMatchOnly(JSGlobalData& globalData, Yarr::YarrCharSize charSize) +{ + if (hasCode()) { +#if ENABLE(YARR_JIT) + if (m_state != JITCode) + return; + if ((charSize == Yarr::Char8) && (m_regExpJITCode.has8BitCodeMatchOnly())) + return; + if ((charSize == Yarr::Char16) && (m_regExpJITCode.has16BitCodeMatchOnly())) + return; +#else + return; +#endif + } + + compileMatchOnly(&globalData, charSize); +} + +MatchResult RegExp::match(JSGlobalData& globalData, const UString& s, unsigned startOffset) +{ +#if ENABLE(REGEXP_TRACING) + m_rtMatchCallCount++; +#endif + + ASSERT(m_state != ParseError); + compileIfNecessaryMatchOnly(globalData, s.is8Bit() ? Yarr::Char8 : Yarr::Char16); + +#if ENABLE(YARR_JIT) + if (m_state == JITCode) { + MatchResult result = s.is8Bit() ? + m_regExpJITCode.execute(s.characters8(), startOffset, s.length()) : + m_regExpJITCode.execute(s.characters16(), startOffset, s.length()); +#if ENABLE(REGEXP_TRACING) + if (!result) + m_rtMatchFoundCount++; +#endif + return result; + } +#endif + + int offsetVectorSize = (m_numSubpatterns + 1) * 2; + int* offsetVector; + Vector<int, 32> nonReturnedOvector; + nonReturnedOvector.resize(offsetVectorSize); + offsetVector = nonReturnedOvector.data(); + int r = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, reinterpret_cast<unsigned*>(offsetVector)); +#if REGEXP_FUNC_TEST_DATA_GEN + RegExpFunctionalTestCollector::get()->outputOneTest(this, s, startOffset, offsetVector, result); +#endif + + if (r >= 0) { +#if ENABLE(REGEXP_TRACING) + m_rtMatchFoundCount++; +#endif + return MatchResult(r, reinterpret_cast<unsigned*>(offsetVector)[1]); + } + + return MatchResult::failed(); +} + void RegExp::invalidateCode() { - if (!m_representation) + if (!hasCode()) return; m_state = NotCompiled; - m_representation.clear(); +#if ENABLE(YARR_JIT) + m_regExpJITCode.clear(); +#endif + m_regExpBytecode.clear(); } #if ENABLE(YARR_JIT_DEBUG) @@ -428,7 +509,7 @@ void RegExp::matchCompareWithInterpreter(const UString& s, int startOffset, int* for (unsigned j = 0, i = 0; i < m_numSubpatterns + 1; j += 2, i++) interpreterOffsetVector[j] = -1; - interpreterResult = Yarr::interpret(m_representation->m_regExpBytecode.get(), s, startOffset, s.length(), interpreterOffsetVector); + interpreterResult = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, interpreterOffsetVector); if (jitResult != interpreterResult) differences++; @@ -477,7 +558,7 @@ void RegExp::matchCompareWithInterpreter(const UString& s, int startOffset, int* snprintf(formattedPattern, 41, (pattLen <= 38) ? "/%.38s/" : "/%.36s...", rawPattern); #if ENABLE(YARR_JIT) - Yarr::YarrCodeBlock& codeBlock = m_representation->m_regExpJITCode; + Yarr::YarrCodeBlock& codeBlock = m_regExpJITCode; const size_t jitAddrSize = 20; char jitAddr[jitAddrSize]; diff --git a/Source/JavaScriptCore/runtime/RegExp.h b/Source/JavaScriptCore/runtime/RegExp.h index d0201cbfb..ad1020376 100644 --- a/Source/JavaScriptCore/runtime/RegExp.h +++ b/Source/JavaScriptCore/runtime/RegExp.h @@ -22,14 +22,19 @@ #ifndef RegExp_h #define RegExp_h -#include "UString.h" #include "ExecutableAllocator.h" -#include "Structure.h" +#include "MatchResult.h" #include "RegExpKey.h" +#include "Structure.h" +#include "UString.h" #include "yarr/Yarr.h" #include <wtf/Forward.h> #include <wtf/RefCounted.h> +#if ENABLE(YARR_JIT) +#include "yarr/YarrJIT.h" +#endif + namespace JSC { struct RegExpRepresentation; @@ -53,12 +58,13 @@ namespace JSC { bool isValid() const { return !m_constructionError && m_flags != InvalidFlags; } const char* errorMessage() const { return m_constructionError; } - JS_EXPORT_PRIVATE int match(JSGlobalData&, const UString&, unsigned startOffset, Vector<int, 32>* ovector = 0); + JS_EXPORT_PRIVATE int match(JSGlobalData&, const UString&, unsigned startOffset, Vector<int, 32>& ovector); + MatchResult match(JSGlobalData&, const UString&, unsigned startOffset); unsigned numSubpatterns() const { return m_numSubpatterns; } bool hasCode() { - return m_representation; + return m_state != NotCompiled; } void invalidateCode(); @@ -95,6 +101,9 @@ namespace JSC { void compile(JSGlobalData*, Yarr::YarrCharSize); void compileIfNecessary(JSGlobalData&, Yarr::YarrCharSize); + void compileMatchOnly(JSGlobalData*, Yarr::YarrCharSize); + void compileIfNecessaryMatchOnly(JSGlobalData&, Yarr::YarrCharSize); + #if ENABLE(YARR_JIT_DEBUG) void matchCompareWithInterpreter(const UString&, int startOffset, int* offsetVector, int jitResult); #endif @@ -108,7 +117,10 @@ namespace JSC { unsigned m_rtMatchFoundCount; #endif - OwnPtr<RegExpRepresentation> m_representation; +#if ENABLE(YARR_JIT) + Yarr::YarrCodeBlock m_regExpJITCode; +#endif + OwnPtr<Yarr::BytecodePattern> m_regExpBytecode; }; } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExpCache.cpp b/Source/JavaScriptCore/runtime/RegExpCache.cpp index d5edbbc7f..36ea326e6 100644 --- a/Source/JavaScriptCore/runtime/RegExpCache.cpp +++ b/Source/JavaScriptCore/runtime/RegExpCache.cpp @@ -46,7 +46,7 @@ RegExp* RegExpCache::lookupOrCreate(const UString& patternString, RegExpFlags fl // We need to do a second lookup to add the RegExp as // allocating it may have caused a gc cycle, which in // turn may have removed items from the cache. - m_weakCache.add(key, PassWeak<RegExp>(*m_globalData, regExp, this)); + m_weakCache.add(key, PassWeak<RegExp>(regExp, this)); return regExp; } diff --git a/Source/JavaScriptCore/runtime/RegExpCachedResult.cpp b/Source/JavaScriptCore/runtime/RegExpCachedResult.cpp new file mode 100644 index 000000000..07881451a --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpCachedResult.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RegExpCachedResult.h" + +#include "RegExpMatchesArray.h" + +namespace JSC { + +void RegExpCachedResult::visitChildren(SlotVisitor& visitor) +{ + if (m_result) { + visitor.append(&m_lastInput); + visitor.append(&m_lastRegExp); + } else { + visitor.append(&m_reifiedInput); + visitor.append(&m_reifiedResult); + } +} + +RegExpMatchesArray* RegExpCachedResult::lastResult(ExecState* exec, JSObject* owner) +{ + if (m_result) { + m_reifiedInput.set(exec->globalData(), owner, m_lastInput.get()); + m_reifiedResult.set(exec->globalData(), owner, RegExpMatchesArray::create(exec, m_lastInput.get(), m_lastRegExp.get(), m_result)); + m_result = MatchResult::failed(); + } + return m_reifiedResult.get(); +} + +void RegExpCachedResult::setInput(ExecState* exec, JSObject* owner, JSString* input) +{ + // Make sure we're reified, otherwise m_reifiedInput will be ignored. + lastResult(exec, owner); + ASSERT(!m_result); + m_reifiedInput.set(exec->globalData(), owner, input); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExpCachedResult.h b/Source/JavaScriptCore/runtime/RegExpCachedResult.h new file mode 100644 index 000000000..a72244025 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpCachedResult.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RegExpCachedResult_h +#define RegExpCachedResult_h + +#include "RegExpObject.h" + +namespace JSC { + + class JSString; + class RegExpMatchesArray; + + // RegExpCachedResult is used to track the cached results of the last + // match, stores on the RegExp constructor (e.g. $&, $_, $1, $2 ...). + // These values will be lazily generated on demand, so the cached result + // may be in a lazy or reified state. A lazy state is indicated by a + // value of m_result indicating a successful match, and a reified state + // is indicated by setting m_result to MatchResult::failed(). + // Following a successful match, m_result, m_lastInput and m_lastRegExp + // can be used to reify the results from the match, following reification + // m_reifiedResult and m_reifiedInput hold the cached results. + class RegExpCachedResult { + public: + RegExpCachedResult(JSGlobalData& globalData, JSObject* owner, RegExp* emptyRegExp) + : m_result(0, 0) + { + m_lastInput.set(globalData, owner, jsEmptyString(&globalData)); + m_lastRegExp.set(globalData, owner, emptyRegExp); + m_reifiedResult.clear(); + m_reifiedInput.clear(); + } + + ALWAYS_INLINE void record(JSGlobalData& globalData, JSObject* owner, RegExp* regExp, JSString* input, MatchResult result) + { + m_lastRegExp.set(globalData, owner, regExp); + m_lastInput.set(globalData, owner, input); + m_result = result; + } + + RegExpMatchesArray* lastResult(ExecState*, JSObject* owner); + void setInput(ExecState*, JSObject* owner, JSString*); + + JSString* input() + { + // If m_result showas a match then we're in a lazy state, so m_lastInput + // is the most recent value of the input property. If not then we have + // reified, in which case m_reifiedInput will contain the correct value. + return m_result ? m_lastInput.get() : m_reifiedInput.get(); + } + + void visitChildren(SlotVisitor&); + + private: + MatchResult m_result; + WriteBarrier<JSString> m_lastInput; + WriteBarrier<RegExp> m_lastRegExp; + WriteBarrier<RegExpMatchesArray> m_reifiedResult; + WriteBarrier<JSString> m_reifiedInput; + }; + +} // namespace JSC + +#endif // RegExpCachedResult_h diff --git a/Source/JavaScriptCore/runtime/RegExpConstructor.cpp b/Source/JavaScriptCore/runtime/RegExpConstructor.cpp index 90082f07e..fd03db569 100644 --- a/Source/JavaScriptCore/runtime/RegExpConstructor.cpp +++ b/Source/JavaScriptCore/runtime/RegExpConstructor.cpp @@ -22,21 +22,9 @@ #include "config.h" #include "RegExpConstructor.h" -#include "ArrayPrototype.h" #include "Error.h" -#include "ExceptionHelpers.h" -#include "JSArray.h" -#include "JSFunction.h" -#include "JSString.h" -#include "Lookup.h" -#include "ObjectPrototype.h" #include "RegExpMatchesArray.h" -#include "RegExpObject.h" #include "RegExpPrototype.h" -#include "RegExp.h" -#include "RegExpCache.h" -#include "UStringConcatenate.h" -#include <wtf/PassOwnPtr.h> namespace JSC { @@ -69,8 +57,6 @@ ASSERT_CLASS_FITS_IN_CELL(RegExpConstructor); const ClassInfo RegExpConstructor::s_info = { "Function", &InternalFunction::s_info, 0, ExecState::regExpConstructorTable, CREATE_METHOD_TABLE(RegExpConstructor) }; -const ClassInfo RegExpMatchesArray::s_info = {"Array", &JSArray::s_info, 0, 0, CREATE_METHOD_TABLE(RegExpMatchesArray)}; - /* Source for RegExpConstructor.lut.h @begin regExpConstructorTable input regExpConstructorInput None @@ -97,18 +83,10 @@ const ClassInfo RegExpMatchesArray::s_info = {"Array", &JSArray::s_info, 0, 0, C @end */ -RegExpResult& RegExpResult::operator=(const RegExpConstructorPrivate& rhs) -{ - this->input = rhs.input; - this->ovector = rhs.lastOvector(); - this->lastNumSubPatterns = rhs.lastNumSubPatterns; - - return *this; -} - - -RegExpConstructor::RegExpConstructor(JSGlobalObject* globalObject, Structure* structure) +RegExpConstructor::RegExpConstructor(JSGlobalObject* globalObject, Structure* structure, RegExpPrototype* regExpPrototype) : InternalFunction(globalObject, structure) + , m_cachedResult(globalObject->globalData(), this, regExpPrototype->regExp()) + , m_multiline(false) { } @@ -129,81 +107,51 @@ void RegExpConstructor::destroy(JSCell* cell) jsCast<RegExpConstructor*>(cell)->RegExpConstructor::~RegExpConstructor(); } -RegExpMatchesArray::RegExpMatchesArray(ExecState* exec) - : JSArray(exec->globalData(), exec->lexicalGlobalObject()->regExpMatchesArrayStructure()) - , m_didFillArrayInstance(false) -{ -} - -void RegExpMatchesArray::finishCreation(JSGlobalData& globalData, const RegExpConstructorPrivate& data) +void RegExpConstructor::visitChildren(JSCell* cell, SlotVisitor& visitor) { - Base::finishCreation(globalData, data.lastNumSubPatterns + 1); - m_regExpResult = data; -} - -void RegExpMatchesArray::destroy(JSCell* cell) -{ - jsCast<RegExpMatchesArray*>(cell)->RegExpMatchesArray::~RegExpMatchesArray(); -} - -void RegExpMatchesArray::fillArrayInstance(ExecState* exec) -{ - unsigned lastNumSubpatterns = m_regExpResult.lastNumSubPatterns; - - for (unsigned i = 0; i <= lastNumSubpatterns; ++i) { - int start = m_regExpResult.ovector[2 * i]; - if (start >= 0) - putDirectIndex(exec, i, jsSubstring(exec, m_regExpResult.input, start, m_regExpResult.ovector[2 * i + 1] - start), false); - else - putDirectIndex(exec, i, jsUndefined(), false); - } + RegExpConstructor* thisObject = jsCast<RegExpConstructor*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); - PutPropertySlot slot; - JSArray::put(this, exec, exec->propertyNames().index, jsNumber(m_regExpResult.ovector[0]), slot); - JSArray::put(this, exec, exec->propertyNames().input, jsString(exec, m_regExpResult.input), slot); - - m_didFillArrayInstance = true; + Base::visitChildren(thisObject, visitor); + thisObject->m_cachedResult.visitChildren(visitor); } -JSObject* RegExpConstructor::arrayOfMatches(ExecState* exec) const +JSValue RegExpConstructor::getBackref(ExecState* exec, unsigned i) { - return RegExpMatchesArray::create(exec, d); -} + RegExpMatchesArray* array = m_cachedResult.lastResult(exec, this); -JSValue RegExpConstructor::getBackref(ExecState* exec, unsigned i) const -{ - if (!d.lastOvector().isEmpty() && i <= d.lastNumSubPatterns) { - int start = d.lastOvector()[2 * i]; - if (start >= 0) - return jsSubstring(exec, d.lastInput, start, d.lastOvector()[2 * i + 1] - start); + if (i < array->length()) { + JSValue result = JSValue(array).get(exec, i); + ASSERT(result.isString() || result.isUndefined()); + if (!result.isUndefined()) + return result; } return jsEmptyString(exec); } -JSValue RegExpConstructor::getLastParen(ExecState* exec) const +JSValue RegExpConstructor::getLastParen(ExecState* exec) { - unsigned i = d.lastNumSubPatterns; - if (i > 0) { - ASSERT(!d.lastOvector().isEmpty()); - int start = d.lastOvector()[2 * i]; - if (start >= 0) - return jsSubstring(exec, d.lastInput, start, d.lastOvector()[2 * i + 1] - start); + RegExpMatchesArray* array = m_cachedResult.lastResult(exec, this); + unsigned length = array->length(); + if (length > 1) { + JSValue result = JSValue(array).get(exec, length - 1); + ASSERT(result.isString() || result.isUndefined()); + if (!result.isUndefined()) + return result; } return jsEmptyString(exec); } -JSValue RegExpConstructor::getLeftContext(ExecState* exec) const +JSValue RegExpConstructor::getLeftContext(ExecState* exec) { - if (!d.lastOvector().isEmpty()) - return jsSubstring(exec, d.lastInput, 0, d.lastOvector()[0]); - return jsEmptyString(exec); + return m_cachedResult.lastResult(exec, this)->leftContext(exec); } -JSValue RegExpConstructor::getRightContext(ExecState* exec) const +JSValue RegExpConstructor::getRightContext(ExecState* exec) { - if (!d.lastOvector().isEmpty()) - return jsSubstring(exec, d.lastInput, d.lastOvector()[1], d.lastInput.length() - d.lastOvector()[1]); - return jsEmptyString(exec); + return m_cachedResult.lastResult(exec, this)->rightContext(exec); } bool RegExpConstructor::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) @@ -261,9 +209,9 @@ JSValue regExpConstructorDollar9(ExecState* exec, JSValue slotBase, const Identi return asRegExpConstructor(slotBase)->getBackref(exec, 9); } -JSValue regExpConstructorInput(ExecState* exec, JSValue slotBase, const Identifier&) +JSValue regExpConstructorInput(ExecState*, JSValue slotBase, const Identifier&) { - return jsString(exec, asRegExpConstructor(slotBase)->input()); + return asRegExpConstructor(slotBase)->input(); } JSValue regExpConstructorMultiline(ExecState*, JSValue slotBase, const Identifier&) @@ -298,7 +246,7 @@ void RegExpConstructor::put(JSCell* cell, ExecState* exec, const Identifier& pro void setRegExpConstructorInput(ExecState* exec, JSObject* baseObject, JSValue value) { - asRegExpConstructor(baseObject)->setInput(value.toString(exec)->value(exec)); + asRegExpConstructor(baseObject)->setInput(exec, value.toString(exec)); } void setRegExpConstructorMultiline(ExecState* exec, JSObject* baseObject, JSValue value) @@ -367,26 +315,4 @@ CallType RegExpConstructor::getCallData(JSCell*, CallData& callData) return CallTypeHost; } -void RegExpConstructor::setInput(const UString& input) -{ - d.input = input; -} - -const UString& RegExpConstructor::input() const -{ - // Can detect a distinct initial state that is invisible to JavaScript, by checking for null - // state (since jsString turns null strings to empty strings). - return d.input; -} - -void RegExpConstructor::setMultiline(bool multiline) -{ - d.multiline = multiline; -} - -bool RegExpConstructor::multiline() const -{ - return d.multiline; -} - } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExpConstructor.h b/Source/JavaScriptCore/runtime/RegExpConstructor.h index 08a96b544..0093f9484 100644 --- a/Source/JavaScriptCore/runtime/RegExpConstructor.h +++ b/Source/JavaScriptCore/runtime/RegExpConstructor.h @@ -23,60 +23,22 @@ #include "InternalFunction.h" #include "RegExp.h" +#include "RegExpCachedResult.h" +#include "RegExpObject.h" #include <wtf/OwnPtr.h> + namespace JSC { - class RegExp; class RegExpPrototype; - struct RegExpConstructorPrivate; - - struct RegExpConstructorPrivate { - WTF_MAKE_FAST_ALLOCATED; - public: - // Global search cache / settings - RegExpConstructorPrivate() - : lastNumSubPatterns(0) - , multiline(false) - , lastOvectorIndex(0) - { - } - - const Vector<int, 32>& lastOvector() const { return ovector[lastOvectorIndex]; } - Vector<int, 32>& lastOvector() { return ovector[lastOvectorIndex]; } - Vector<int, 32>& tempOvector() { return ovector[lastOvectorIndex ? 0 : 1]; } - void changeLastOvector() { lastOvectorIndex = lastOvectorIndex ? 0 : 1; } - - UString input; - UString lastInput; - Vector<int, 32> ovector[2]; - unsigned lastNumSubPatterns : 30; - bool multiline : 1; - unsigned lastOvectorIndex : 1; - }; - struct RegExpResult { - WTF_MAKE_FAST_ALLOCATED; - public: - RegExpResult() - : lastNumSubPatterns(0) - { - } - - RegExpResult& operator=(const RegExpConstructorPrivate&); - - UString input; - unsigned lastNumSubPatterns; - Vector<int, 32> ovector; - }; - class RegExpConstructor : public InternalFunction { public: typedef InternalFunction Base; static RegExpConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, RegExpPrototype* regExpPrototype) { - RegExpConstructor* constructor = new (NotNull, allocateCell<RegExpConstructor>(*exec->heap())) RegExpConstructor(globalObject, structure); + RegExpConstructor* constructor = new (NotNull, allocateCell<RegExpConstructor>(*exec->heap())) RegExpConstructor(globalObject, structure, regExpPrototype); constructor->finishCreation(exec, regExpPrototype); return constructor; } @@ -93,31 +55,35 @@ namespace JSC { static const ClassInfo s_info; - void performMatch(JSGlobalData&, RegExp*, const UString&, int startOffset, int& position, int& length, int** ovector = 0); - JSObject* arrayOfMatches(ExecState*) const; + MatchResult performMatch(JSGlobalData&, RegExp*, JSString*, const UString&, int startOffset, int** ovector); + MatchResult performMatch(JSGlobalData&, RegExp*, JSString*, const UString&, int startOffset); + + void setMultiline(bool multiline) { m_multiline = multiline; } + bool multiline() const { return m_multiline; } - void setInput(const UString&); - const UString& input() const; + JSValue getBackref(ExecState*, unsigned); + JSValue getLastParen(ExecState*); + JSValue getLeftContext(ExecState*); + JSValue getRightContext(ExecState*); - void setMultiline(bool); - bool multiline() const; + void setInput(ExecState* exec, JSString* string) { m_cachedResult.setInput(exec, this, string); } + JSString* input() { return m_cachedResult.input(); } - JSValue getBackref(ExecState*, unsigned) const; - JSValue getLastParen(ExecState*) const; - JSValue getLeftContext(ExecState*) const; - JSValue getRightContext(ExecState*) const; + static void visitChildren(JSCell*, SlotVisitor&); protected: void finishCreation(ExecState*, RegExpPrototype*); - static const unsigned StructureFlags = OverridesGetOwnPropertySlot | ImplementsHasInstance | InternalFunction::StructureFlags; + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesVisitChildren | Base::StructureFlags; private: - RegExpConstructor(JSGlobalObject*, Structure*); + RegExpConstructor(JSGlobalObject*, Structure*, RegExpPrototype*); static void destroy(JSCell*); static ConstructType getConstructData(JSCell*, ConstructData&); static CallType getCallData(JSCell*, CallData&); - RegExpConstructorPrivate d; + RegExpCachedResult m_cachedResult; + bool m_multiline; + Vector<int, 32> m_ovector; }; RegExpConstructor* asRegExpConstructor(JSValue); @@ -135,23 +101,31 @@ namespace JSC { expression matching through the performMatch function. We use cached results to calculate, e.g., RegExp.lastMatch and RegExp.leftParen. */ - ALWAYS_INLINE void RegExpConstructor::performMatch(JSGlobalData& globalData, RegExp* r, const UString& s, int startOffset, int& position, int& length, int** ovector) + ALWAYS_INLINE MatchResult RegExpConstructor::performMatch(JSGlobalData& globalData, RegExp* regExp, JSString* string, const UString& input, int startOffset, int** ovector) { - position = r->match(globalData, s, startOffset, &d.tempOvector()); + int position = regExp->match(globalData, input, startOffset, m_ovector); if (ovector) - *ovector = d.tempOvector().data(); + *ovector = m_ovector.data(); - if (position != -1) { - ASSERT(!d.tempOvector().isEmpty()); + if (position == -1) + return MatchResult::failed(); - length = d.tempOvector()[1] - d.tempOvector()[0]; + ASSERT(!m_ovector.isEmpty()); + ASSERT(m_ovector[0] == position); + ASSERT(m_ovector[1] >= position); + size_t end = m_ovector[1]; - d.input = s; - d.lastInput = s; - d.changeLastOvector(); - d.lastNumSubPatterns = r->numSubpatterns(); - } + m_cachedResult.record(globalData, this, regExp, string, MatchResult(position, end)); + + return MatchResult(position, end); + } + ALWAYS_INLINE MatchResult RegExpConstructor::performMatch(JSGlobalData& globalData, RegExp* regExp, JSString* string, const UString& input, int startOffset) + { + MatchResult result = regExp->match(globalData, input, startOffset); + if (result) + m_cachedResult.record(globalData, this, regExp, string, result); + return result; } } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp b/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp new file mode 100644 index 000000000..80f1068f2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RegExpMatchesArray.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(RegExpMatchesArray); + +const ClassInfo RegExpMatchesArray::s_info = {"Array", &JSArray::s_info, 0, 0, CREATE_METHOD_TABLE(RegExpMatchesArray)}; + +void RegExpMatchesArray::finishCreation(JSGlobalData& globalData) +{ + Base::finishCreation(globalData, m_regExp->numSubpatterns() + 1); +} + +void RegExpMatchesArray::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(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_input); + visitor.append(&thisObject->m_regExp); +} + +void RegExpMatchesArray::reifyAllProperties(ExecState* exec) +{ + ASSERT(m_state != ReifiedAll); + ASSERT(m_result); + + reifyMatchPropertyIfNecessary(exec); + + if (unsigned numSubpatterns = m_regExp->numSubpatterns()) { + Vector<int, 32> subpatternResults; + int position = m_regExp->match(exec->globalData(), m_input->value(exec), m_result.start, subpatternResults); + ASSERT_UNUSED(position, position >= 0 && static_cast<size_t>(position) == m_result.start); + ASSERT(m_result.start == static_cast<size_t>(subpatternResults[0])); + ASSERT(m_result.end == static_cast<size_t>(subpatternResults[1])); + + for (unsigned i = 1; i <= numSubpatterns; ++i) { + int start = subpatternResults[2 * i]; + if (start >= 0) + putDirectIndex(exec, i, jsSubstring(exec, m_input.get(), start, subpatternResults[2 * i + 1] - start), false); + else + putDirectIndex(exec, i, jsUndefined(), false); + } + } + + PutPropertySlot slot; + JSArray::put(this, exec, exec->propertyNames().index, jsNumber(m_result.start), slot); + JSArray::put(this, exec, exec->propertyNames().input, m_input.get(), slot); + + m_state = ReifiedAll; +} + +void RegExpMatchesArray::reifyMatchProperty(ExecState* exec) +{ + ASSERT(m_state == ReifiedNone); + ASSERT(m_result); + putDirectIndex(exec, 0, jsSubstring(exec, m_input.get(), m_result.start, m_result.end - m_result.start), false); + m_state = ReifiedMatch; +} + +JSString* RegExpMatchesArray::leftContext(ExecState* exec) +{ + if (!m_result.start) + return jsEmptyString(exec); + return jsSubstring(exec, m_input.get(), 0, m_result.start); +} + +JSString* RegExpMatchesArray::rightContext(ExecState* exec) +{ + unsigned length = m_input->length(); + if (m_result.end == length) + return jsEmptyString(exec); + return jsSubstring(exec, m_input.get(), m_result.end, length - m_result.end); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExpMatchesArray.h b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h index a3c4497fc..595457bca 100644 --- a/Source/JavaScriptCore/runtime/RegExpMatchesArray.h +++ b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h @@ -21,23 +21,38 @@ #define RegExpMatchesArray_h #include "JSArray.h" +#include "JSGlobalObject.h" +#include "RegExpObject.h" namespace JSC { class RegExpMatchesArray : public JSArray { private: - RegExpMatchesArray(ExecState*); + RegExpMatchesArray(JSGlobalData& globalData, JSGlobalObject* globalObject, JSString* input, RegExp* regExp, MatchResult result) + : JSArray(globalData, globalObject->regExpMatchesArrayStructure()) + , m_result(result) + , m_state(ReifiedNone) + { + m_input.set(globalData, this, input); + m_regExp.set(globalData, this, regExp); + } + + enum ReifiedState { ReifiedNone, ReifiedMatch, ReifiedAll }; public: typedef JSArray Base; - static RegExpMatchesArray* create(ExecState* exec, const RegExpConstructorPrivate& ctorPrivate) + static RegExpMatchesArray* create(ExecState* exec, JSString* input, RegExp* regExp, MatchResult result) { - RegExpMatchesArray* regExp = new (NotNull, allocateCell<RegExpMatchesArray>(*exec->heap())) RegExpMatchesArray(exec); - regExp->finishCreation(exec->globalData(), ctorPrivate); - return regExp; + ASSERT(result); + JSGlobalData& globalData = exec->globalData(); + RegExpMatchesArray* array = new (NotNull, allocateCell<RegExpMatchesArray>(globalData.heap)) RegExpMatchesArray(globalData, exec->lexicalGlobalObject(), input, regExp, result); + array->finishCreation(globalData); + return array; } - static void destroy(JSCell*); + + JSString* leftContext(ExecState*); + JSString* rightContext(ExecState*); static const ClassInfo s_info; @@ -46,78 +61,99 @@ namespace JSC { return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); } + static void visitChildren(JSCell*, SlotVisitor&); + protected: - void finishCreation(JSGlobalData&, const RegExpConstructorPrivate& data); + void finishCreation(JSGlobalData&); + + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesVisitChildren | OverridesGetPropertyNames | Base::StructureFlags; private: + ALWAYS_INLINE void reifyAllPropertiesIfNecessary(ExecState* exec) + { + if (m_state != ReifiedAll) + reifyAllProperties(exec); + } + + ALWAYS_INLINE void reifyMatchPropertyIfNecessary(ExecState* exec) + { + if (m_state == ReifiedNone) + reifyMatchProperty(exec); + } + static bool getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell); - if (!thisObject->m_didFillArrayInstance) - thisObject->fillArrayInstance(exec); + thisObject->reifyAllPropertiesIfNecessary(exec); return JSArray::getOwnPropertySlot(thisObject, exec, propertyName, slot); } static bool getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot) { RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell); - if (!thisObject->m_didFillArrayInstance) - thisObject->fillArrayInstance(exec); + if (propertyName) + thisObject->reifyAllPropertiesIfNecessary(exec); + else + thisObject->reifyMatchPropertyIfNecessary(exec); return JSArray::getOwnPropertySlotByIndex(thisObject, exec, propertyName, slot); } static bool getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) { RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(object); - if (!thisObject->m_didFillArrayInstance) - thisObject->fillArrayInstance(exec); + thisObject->reifyAllPropertiesIfNecessary(exec); return JSArray::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); } static void put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue v, PutPropertySlot& slot) { RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell); - if (!thisObject->m_didFillArrayInstance) - thisObject->fillArrayInstance(exec); + thisObject->reifyAllPropertiesIfNecessary(exec); JSArray::put(thisObject, exec, propertyName, v, slot); } static void putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue v, bool shouldThrow) { RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell); - if (!thisObject->m_didFillArrayInstance) - thisObject->fillArrayInstance(exec); + thisObject->reifyAllPropertiesIfNecessary(exec); JSArray::putByIndex(thisObject, exec, propertyName, v, shouldThrow); } static bool deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName) { RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell); - if (!thisObject->m_didFillArrayInstance) - thisObject->fillArrayInstance(exec); + thisObject->reifyAllPropertiesIfNecessary(exec); return JSArray::deleteProperty(thisObject, exec, propertyName); } static bool deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName) { RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell); - if (!thisObject->m_didFillArrayInstance) - thisObject->fillArrayInstance(exec); + thisObject->reifyAllPropertiesIfNecessary(exec); return JSArray::deletePropertyByIndex(thisObject, exec, propertyName); } static void getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& arr, EnumerationMode mode = ExcludeDontEnumProperties) { RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(object); - if (!thisObject->m_didFillArrayInstance) - thisObject->fillArrayInstance(exec); + thisObject->reifyAllPropertiesIfNecessary(exec); JSArray::getOwnPropertyNames(thisObject, exec, arr, mode); } - void fillArrayInstance(ExecState*); + static bool defineOwnProperty(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool shouldThrow) + { + RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(object); + thisObject->reifyAllPropertiesIfNecessary(exec); + return JSArray::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow); + } + + void reifyAllProperties(ExecState*); + void reifyMatchProperty(ExecState*); - RegExpResult m_regExpResult; - bool m_didFillArrayInstance; + WriteBarrier<JSString> m_input; + WriteBarrier<RegExp> m_regExp; + MatchResult m_result; + ReifiedState m_state; }; } diff --git a/Source/JavaScriptCore/runtime/RegExpObject.cpp b/Source/JavaScriptCore/runtime/RegExpObject.cpp index a81799c46..da4ea0446 100644 --- a/Source/JavaScriptCore/runtime/RegExpObject.cpp +++ b/Source/JavaScriptCore/runtime/RegExpObject.cpp @@ -29,6 +29,7 @@ #include "Lexer.h" #include "Lookup.h" #include "RegExpConstructor.h" +#include "RegExpMatchesArray.h" #include "RegExpPrototype.h" #include "UStringBuilder.h" #include "UStringConcatenate.h" @@ -276,30 +277,22 @@ void RegExpObject::put(JSCell* cell, ExecState* exec, const Identifier& property lookupPut<RegExpObject, JSObject>(exec, propertyName, value, ExecState::regExpTable(exec), jsCast<RegExpObject*>(cell), slot); } -JSValue RegExpObject::test(ExecState* exec) +JSValue RegExpObject::exec(ExecState* exec, JSString* string) { - return jsBoolean(match(exec)); -} - -JSValue RegExpObject::exec(ExecState* exec) -{ - if (match(exec)) - return exec->lexicalGlobalObject()->regExpConstructor()->arrayOfMatches(exec); + if (MatchResult result = match(exec, string)) + return RegExpMatchesArray::create(exec, string, regExp(), result); return jsNull(); } // Shared implementation used by test and exec. -bool RegExpObject::match(ExecState* exec) +MatchResult RegExpObject::match(ExecState* exec, JSString* string) { + RegExp* regExp = this->regExp(); RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); - UString input = exec->argument(0).toString(exec)->value(exec); - JSGlobalData* globalData = &exec->globalData(); - if (!regExp()->global()) { - int position; - int length; - regExpConstructor->performMatch(*globalData, m_regExp.get(), input, 0, position, length); - return position >= 0; - } + UString input = string->value(exec); + JSGlobalData& globalData = exec->globalData(); + if (!regExp->global()) + return regExpConstructor->performMatch(globalData, regExp, string, input, 0); JSValue jsLastIndex = getLastIndex(); unsigned lastIndex; @@ -307,27 +300,20 @@ bool RegExpObject::match(ExecState* exec) lastIndex = jsLastIndex.asUInt32(); if (lastIndex > input.length()) { setLastIndex(exec, 0); - return false; + return MatchResult::failed(); } } else { double doubleLastIndex = jsLastIndex.toInteger(exec); if (doubleLastIndex < 0 || doubleLastIndex > input.length()) { setLastIndex(exec, 0); - return false; + return MatchResult::failed(); } lastIndex = static_cast<unsigned>(doubleLastIndex); } - int position; - int length = 0; - regExpConstructor->performMatch(*globalData, m_regExp.get(), input, lastIndex, position, length); - if (position < 0) { - setLastIndex(exec, 0); - return false; - } - - setLastIndex(exec, position + length); - return true; + MatchResult result = regExpConstructor->performMatch(globalData, regExp, string, input, lastIndex); + setLastIndex(exec, result.end); + return result; } } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExpObject.h b/Source/JavaScriptCore/runtime/RegExpObject.h index 456cfa683..a7dd54705 100644 --- a/Source/JavaScriptCore/runtime/RegExpObject.h +++ b/Source/JavaScriptCore/runtime/RegExpObject.h @@ -67,8 +67,8 @@ namespace JSC { return m_lastIndex.get(); } - JSValue test(ExecState*); - JSValue exec(ExecState*); + bool test(ExecState* exec, JSString* string) { return match(exec, string); } + JSValue exec(ExecState*, JSString*); static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&); static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); @@ -95,7 +95,7 @@ namespace JSC { JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow); private: - bool match(ExecState*); + MatchResult match(ExecState*, JSString*); WriteBarrier<RegExp> m_regExp; WriteBarrier<Unknown> m_lastIndex; diff --git a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp index 8e4b5a9d5..fba4de2b4 100644 --- a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp +++ b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp @@ -84,7 +84,7 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState* exec) JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&RegExpObject::s_info)) return throwVMTypeError(exec); - return JSValue::encode(asRegExpObject(thisValue)->test(exec)); + return JSValue::encode(jsBoolean(asRegExpObject(thisValue)->test(exec, exec->argument(0).toString(exec)))); } EncodedJSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState* exec) @@ -92,7 +92,7 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState* exec) JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&RegExpObject::s_info)) return throwVMTypeError(exec); - return JSValue::encode(asRegExpObject(thisValue)->exec(exec)); + return JSValue::encode(asRegExpObject(thisValue)->exec(exec, exec->argument(0).toString(exec))); } EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState* exec) diff --git a/Source/JavaScriptCore/runtime/SmallStrings.cpp b/Source/JavaScriptCore/runtime/SmallStrings.cpp index caf201c3d..f50f73d27 100644 --- a/Source/JavaScriptCore/runtime/SmallStrings.cpp +++ b/Source/JavaScriptCore/runtime/SmallStrings.cpp @@ -68,9 +68,15 @@ SmallStringsStorage::SmallStringsStorage() } SmallStrings::SmallStrings() + : m_emptyString(0) +#define JSC_COMMON_STRINGS_ATTRIBUTE_INITIALIZE(name) , m_##name(0) + JSC_COMMON_STRINGS_EACH_NAME(JSC_COMMON_STRINGS_ATTRIBUTE_INITIALIZE) +#undef JSC_COMMON_STRINGS_ATTRIBUTE_INITIALIZE { COMPILE_ASSERT(singleCharacterStringCount == sizeof(m_singleCharacterStrings) / sizeof(m_singleCharacterStrings[0]), IsNumCharactersConstInSyncWithClassUsage); - clear(); + + for (unsigned i = 0; i < singleCharacterStringCount; ++i) + m_singleCharacterStrings[i] = 0; } SmallStrings::~SmallStrings() @@ -82,25 +88,9 @@ void SmallStrings::finalizeSmallStrings() finalize(m_emptyString); for (unsigned i = 0; i < singleCharacterStringCount; ++i) finalize(m_singleCharacterStrings[i]); -} - -void SmallStrings::clear() -{ - m_emptyString = 0; - for (unsigned i = 0; i < singleCharacterStringCount; ++i) - m_singleCharacterStrings[i] = 0; -} - -unsigned SmallStrings::count() const -{ - unsigned count = 0; - if (m_emptyString) - ++count; - for (unsigned i = 0; i < singleCharacterStringCount; ++i) { - if (m_singleCharacterStrings[i]) - ++count; - } - return count; +#define JSC_COMMON_STRINGS_ATTRIBUTE_FINALIZE(name) finalize(m_##name); + JSC_COMMON_STRINGS_EACH_NAME(JSC_COMMON_STRINGS_ATTRIBUTE_FINALIZE) +#undef JSC_COMMON_STRINGS_ATTRIBUTE_FINALIZE } void SmallStrings::createEmptyString(JSGlobalData* globalData) @@ -124,4 +114,9 @@ StringImpl* SmallStrings::singleCharacterStringRep(unsigned char character) return m_storage->rep(character); } +void SmallStrings::initialize(JSGlobalData* globalData, JSString*& string, const char* value) const +{ + string = JSString::create(*globalData, StringImpl::create(value)); +} + } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/SmallStrings.h b/Source/JavaScriptCore/runtime/SmallStrings.h index cd8e63095..e609c5092 100644 --- a/Source/JavaScriptCore/runtime/SmallStrings.h +++ b/Source/JavaScriptCore/runtime/SmallStrings.h @@ -30,6 +30,17 @@ #include <wtf/FixedArray.h> #include <wtf/OwnPtr.h> +#define JSC_COMMON_STRINGS_EACH_NAME(macro) \ + macro(boolean) \ + macro(false) \ + macro(function) \ + macro(number) \ + macro(null) \ + macro(object) \ + macro(undefined) \ + macro(string) \ + macro(true) + namespace JSC { class HeapRootVisitor; @@ -63,19 +74,31 @@ namespace JSC { JS_EXPORT_PRIVATE StringImpl* singleCharacterStringRep(unsigned char character); void finalizeSmallStrings(); - void clear(); - - unsigned count() const; JSString** singleCharacterStrings() { return &m_singleCharacterStrings[0]; } +#define JSC_COMMON_STRINGS_ACCESSOR_DEFINITION(name) \ + JSString* name##String(JSGlobalData* globalData) const \ + { \ + if (!m_##name) \ + initialize(globalData, m_##name, #name); \ + return m_##name; \ + } + JSC_COMMON_STRINGS_EACH_NAME(JSC_COMMON_STRINGS_ACCESSOR_DEFINITION) +#undef JSC_COMMON_STRINGS_ACCESSOR_DEFINITION + private: static const unsigned singleCharacterStringCount = maxSingleCharacterString + 1; JS_EXPORT_PRIVATE void createEmptyString(JSGlobalData*); JS_EXPORT_PRIVATE void createSingleCharacterString(JSGlobalData*, unsigned char); + void initialize(JSGlobalData* globalData, JSString*& string, const char* value) const; + JSString* m_emptyString; +#define JSC_COMMON_STRINGS_ATTRIBUTE_DECLARATION(name) mutable JSString* m_##name; + JSC_COMMON_STRINGS_EACH_NAME(JSC_COMMON_STRINGS_ATTRIBUTE_DECLARATION) +#undef JSC_COMMON_STRINGS_ATTRIBUTE_DECLARATION JSString* m_singleCharacterStrings[singleCharacterStringCount]; OwnPtr<SmallStringsStorage> m_storage; }; diff --git a/Source/JavaScriptCore/runtime/StringPrototype.cpp b/Source/JavaScriptCore/runtime/StringPrototype.cpp index 708c1fb77..81129f2a2 100644 --- a/Source/JavaScriptCore/runtime/StringPrototype.cpp +++ b/Source/JavaScriptCore/runtime/StringPrototype.cpp @@ -35,6 +35,7 @@ #include "PropertyNameArray.h" #include "RegExpCache.h" #include "RegExpConstructor.h" +#include "RegExpMatchesArray.h" #include "RegExpObject.h" #include <wtf/ASCIICType.h> #include <wtf/MathExtras.h> @@ -239,7 +240,7 @@ static NEVER_INLINE UString substituteBackreferencesSlow(const UString& replacem static inline UString substituteBackreferences(const UString& replacement, const UString& source, const int* ovector, RegExp* reg) { - size_t i = replacement.find('$', 0); + size_t i = replacement.find('$'); if (UNLIKELY(i != notFound)) { if (replacement.is8Bit() && source.is8Bit()) return substituteBackreferencesSlow<LChar>(replacement, source, ovector, reg, i); @@ -404,7 +405,7 @@ static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, J static NEVER_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSString* string, const UString& source, RegExp* regExp) { - int lastIndex = 0; + size_t lastIndex = 0; unsigned startPosition = 0; Vector<StringRange, 16> sourceRanges; @@ -413,21 +414,18 @@ static NEVER_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSSt unsigned sourceLen = source.length(); while (true) { - int matchIndex; - int matchLen = 0; - int* ovector; - regExpConstructor->performMatch(*globalData, regExp, source, startPosition, matchIndex, matchLen, &ovector); - if (matchIndex < 0) + MatchResult result = regExpConstructor->performMatch(*globalData, regExp, string, source, startPosition); + if (!result) break; - if (lastIndex < matchIndex) - sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex)); + if (lastIndex < result.start) + sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); - lastIndex = matchIndex + matchLen; + lastIndex = result.end; startPosition = lastIndex; // special case of empty match - if (!matchLen) { + if (result.empty()) { startPosition++; if (startPosition > sourceLen) break; @@ -443,8 +441,9 @@ static NEVER_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSSt return JSValue::encode(jsSpliceSubstrings(exec, string, source, sourceRanges.data(), sourceRanges.size())); } -static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue) +static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSString* string, JSValue searchValue) { + JSValue replaceValue = exec->argument(1); UString replacementString; CallData callData; CallType callType = getCallData(replaceValue, callData); @@ -471,7 +470,7 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); - int lastIndex = 0; + size_t lastIndex = 0; unsigned startPosition = 0; Vector<StringRange, 16> sourceRanges; @@ -481,23 +480,20 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS if (global && callType == CallTypeJS) { // regExp->numSubpatterns() + 1 for pattern args, + 2 for match start and string int argCount = regExp->numSubpatterns() + 1 + 2; - JSFunction* func = asFunction(replaceValue); + JSFunction* func = jsCast<JSFunction*>(replaceValue); CachedCall cachedCall(exec, func, argCount); if (exec->hadException()) return JSValue::encode(jsNull()); JSGlobalData* globalData = &exec->globalData(); if (source.is8Bit()) { while (true) { - int matchIndex; - int matchLen = 0; int* ovector; - regExpConstructor->performMatch(*globalData, regExp, source, startPosition, matchIndex, matchLen, &ovector); - if (matchIndex < 0) + MatchResult result = regExpConstructor->performMatch(*globalData, regExp, string, source, startPosition, &ovector); + if (!result) break; - sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex)); + sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); - int completeMatchStart = ovector[0]; unsigned i = 0; for (; i < regExp->numSubpatterns() + 1; ++i) { int matchStart = ovector[i * 2]; @@ -509,20 +505,20 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS cachedCall.setArgument(i, jsSubstring8(globalData, source, matchStart, matchLen)); } - cachedCall.setArgument(i++, jsNumber(completeMatchStart)); + cachedCall.setArgument(i++, jsNumber(result.start)); cachedCall.setArgument(i++, string); cachedCall.setThis(jsUndefined()); - JSValue result = cachedCall.call(); - replacements.append(result.toString(cachedCall.newCallFrame(exec))->value(exec)); + JSValue jsResult = cachedCall.call(); + replacements.append(jsResult.toString(cachedCall.newCallFrame(exec))->value(exec)); if (exec->hadException()) break; - lastIndex = matchIndex + matchLen; + lastIndex = result.end; startPosition = lastIndex; // special case of empty match - if (!matchLen) { + if (result.empty()) { startPosition++; if (startPosition > sourceLen) break; @@ -530,16 +526,13 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS } } else { while (true) { - int matchIndex; - int matchLen = 0; int* ovector; - regExpConstructor->performMatch(*globalData, regExp, source, startPosition, matchIndex, matchLen, &ovector); - if (matchIndex < 0) + MatchResult result = regExpConstructor->performMatch(*globalData, regExp, string, source, startPosition, &ovector); + if (!result) break; - sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex)); + sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); - int completeMatchStart = ovector[0]; unsigned i = 0; for (; i < regExp->numSubpatterns() + 1; ++i) { int matchStart = ovector[i * 2]; @@ -551,20 +544,20 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS cachedCall.setArgument(i, jsSubstring(globalData, source, matchStart, matchLen)); } - cachedCall.setArgument(i++, jsNumber(completeMatchStart)); + cachedCall.setArgument(i++, jsNumber(result.start)); cachedCall.setArgument(i++, string); cachedCall.setThis(jsUndefined()); - JSValue result = cachedCall.call(); - replacements.append(result.toString(cachedCall.newCallFrame(exec))->value(exec)); + JSValue jsResult = cachedCall.call(); + replacements.append(jsResult.toString(cachedCall.newCallFrame(exec))->value(exec)); if (exec->hadException()) break; - lastIndex = matchIndex + matchLen; + lastIndex = result.end; startPosition = lastIndex; // special case of empty match - if (!matchLen) { + if (result.empty()) { startPosition++; if (startPosition > sourceLen) break; @@ -574,17 +567,14 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS } else { JSGlobalData* globalData = &exec->globalData(); do { - int matchIndex; - int matchLen = 0; int* ovector; - regExpConstructor->performMatch(*globalData, regExp, source, startPosition, matchIndex, matchLen, &ovector); - if (matchIndex < 0) + MatchResult result = regExpConstructor->performMatch(*globalData, regExp, string, source, startPosition, &ovector); + if (!result) break; if (callType != CallTypeNone) { - sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex)); + sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); - int completeMatchStart = ovector[0]; MarkedArgumentBuffer args; for (unsigned i = 0; i < regExp->numSubpatterns() + 1; ++i) { @@ -597,7 +587,7 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS args.append(jsSubstring(exec, source, matchStart, matchLen)); } - args.append(jsNumber(completeMatchStart)); + args.append(jsNumber(result.start)); args.append(string); replacements.append(call(exec, replaceValue, callType, callData, jsUndefined(), args).toString(exec)->value(exec)); @@ -605,8 +595,8 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS break; } else { int replLen = replacementString.length(); - if (lastIndex < matchIndex || replLen) { - sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex)); + if (lastIndex < result.start || replLen) { + sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); if (replLen) replacements.append(substituteBackreferences(replacementString, source, ovector, regExp)); @@ -615,11 +605,11 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS } } - lastIndex = matchIndex + matchLen; + lastIndex = result.end; startPosition = lastIndex; // special case of empty match - if (!matchLen) { + if (result.empty()) { startPosition++; if (startPosition > sourceLen) break; @@ -636,22 +626,24 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size())); } -static NEVER_INLINE EncodedJSValue replaceUsingStringSearch(ExecState* exec, JSString* jsString, JSValue searchValue, JSValue replaceValue) +static inline EncodedJSValue replaceUsingStringSearch(ExecState* exec, JSString* jsString, JSValue searchValue) { const UString& string = jsString->value(exec); - UString searchString = searchValue.toString(exec)->value(exec); + UString searchString = searchValue.toUString(exec); if (exec->hadException()) return JSValue::encode(jsUndefined()); size_t matchStart = string.find(searchString); + if (matchStart == notFound) return JSValue::encode(jsString); + JSValue replaceValue = exec->argument(1); CallData callData; CallType callType = getCallData(replaceValue, callData); if (callType != CallTypeNone) { MarkedArgumentBuffer args; - args.append(jsSubstring(exec, string, matchStart, searchString.length())); + args.append(jsSubstring(exec, string, matchStart, searchString.impl()->length())); args.append(jsNumber(matchStart)); args.append(jsString); replaceValue = call(exec, replaceValue, callType, callData, jsUndefined(), args); @@ -659,13 +651,20 @@ static NEVER_INLINE EncodedJSValue replaceUsingStringSearch(ExecState* exec, JSS return JSValue::encode(jsUndefined()); } - UString replaceString = replaceValue.toString(exec)->value(exec); + UString replaceString = replaceValue.toUString(exec); if (exec->hadException()) return JSValue::encode(jsUndefined()); - size_t matchEnd = matchStart + searchString.length(); + StringImpl* stringImpl = string.impl(); + UString leftPart(StringImpl::create(stringImpl, 0, matchStart)); + + size_t matchEnd = matchStart + searchString.impl()->length(); int ovector[2] = { matchStart, matchEnd}; - return JSValue::encode(JSC::jsString(exec, string.substringSharingImpl(0, matchStart), substituteBackreferences(replaceString, string, ovector, 0), string.substringSharingImpl(matchEnd))); + UString middlePart = substituteBackreferences(replaceString, string, ovector, 0); + + size_t leftLength = stringImpl->length() - matchEnd; + UString rightPart(StringImpl::create(stringImpl, matchEnd, leftLength)); + return JSValue::encode(JSC::jsString(exec, leftPart, middlePart, rightPart)); } EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec) @@ -675,11 +674,10 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec) return throwVMTypeError(exec); JSString* string = thisValue.toString(exec); JSValue searchValue = exec->argument(0); - JSValue replaceValue = exec->argument(1); if (searchValue.inherits(&RegExpObject::s_info)) - return replaceUsingRegExpSearch(exec, string, searchValue, replaceValue); - return replaceUsingStringSearch(exec, string, searchValue, replaceValue); + return replaceUsingRegExpSearch(exec, string, searchValue); + return replaceUsingStringSearch(exec, string, searchValue); } EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec) @@ -756,26 +754,30 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec) if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible return throwVMTypeError(exec); UString s = thisValue.toString(exec)->value(exec); - int len = s.length(); JSValue a0 = exec->argument(0); JSValue a1 = exec->argument(1); UString u2 = a0.toString(exec)->value(exec); - int pos; + + size_t result; if (a1.isUndefined()) - pos = 0; - else if (a1.isUInt32()) - pos = min<uint32_t>(a1.asUInt32(), len); + result = s.find(u2); else { - double dpos = a1.toInteger(exec); - if (dpos < 0) - dpos = 0; - else if (dpos > len) - dpos = len; - pos = static_cast<int>(dpos); + unsigned pos; + int len = s.length(); + if (a1.isUInt32()) + pos = min<uint32_t>(a1.asUInt32(), len); + else { + double dpos = a1.toInteger(exec); + if (dpos < 0) + dpos = 0; + else if (dpos > len) + dpos = len; + pos = static_cast<unsigned>(dpos); + } + result = s.find(u2, pos); } - size_t result = s.find(u2, pos); if (result == notFound) return JSValue::encode(jsNumber(-1)); return JSValue::encode(jsNumber(result)); @@ -810,17 +812,18 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec) JSValue thisValue = exec->hostThisValue(); if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible return throwVMTypeError(exec); - UString s = thisValue.toString(exec)->value(exec); + JSString* string = thisValue.toString(exec); + UString s = string->value(exec); JSGlobalData* globalData = &exec->globalData(); JSValue a0 = exec->argument(0); - RegExp* reg; + RegExp* regExp; bool global = false; if (a0.inherits(&RegExpObject::s_info)) { RegExpObject* regExpObject = asRegExpObject(a0); - reg = regExpObject->regExp(); - if ((global = reg->global())) { + regExp = regExpObject->regExp(); + if ((global = regExp->global())) { // ES5.1 15.5.4.10 step 8.a. regExpObject->setLastIndex(exec, 0); if (exec->hadException()) @@ -833,27 +836,25 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec) * replaced with the result of the expression new RegExp(regexp). * Per ECMA 15.10.4.1, if a0 is undefined substitute the empty string. */ - reg = RegExp::create(exec->globalData(), a0.isUndefined() ? UString("") : a0.toString(exec)->value(exec), NoFlags); - if (!reg->isValid()) - return throwVMError(exec, createSyntaxError(exec, reg->errorMessage())); + regExp = RegExp::create(exec->globalData(), a0.isUndefined() ? UString("") : a0.toString(exec)->value(exec), NoFlags); + if (!regExp->isValid()) + return throwVMError(exec, createSyntaxError(exec, regExp->errorMessage())); } RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); - int pos; - int matchLength = 0; - regExpConstructor->performMatch(*globalData, reg, s, 0, pos, matchLength); - if (!global) { - // case without 'g' flag is handled like RegExp.prototype.exec - if (pos < 0) - return JSValue::encode(jsNull()); - return JSValue::encode(regExpConstructor->arrayOfMatches(exec)); - } + MatchResult result = regExpConstructor->performMatch(*globalData, regExp, string, s, 0); + // case without 'g' flag is handled like RegExp.prototype.exec + if (!global) + return JSValue::encode(result ? RegExpMatchesArray::create(exec, string, regExp, result) : jsNull()); // return array of matches MarkedArgumentBuffer list; - while (pos >= 0) { - list.append(jsSubstring(exec, s, pos, matchLength)); - pos += matchLength == 0 ? 1 : matchLength; - regExpConstructor->performMatch(*globalData, reg, s, pos, pos, matchLength); + while (result) { + size_t end = result.end; + size_t length = end - result.start; + list.append(jsSubstring(exec, s, result.start, length)); + if (!length) + ++end; + result = regExpConstructor->performMatch(*globalData, regExp, string, s, end); } if (list.isEmpty()) { // if there are no matches at all, it's important to return @@ -870,7 +871,8 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState* exec) JSValue thisValue = exec->hostThisValue(); if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible return throwVMTypeError(exec); - UString s = thisValue.toString(exec)->value(exec); + JSString* string = thisValue.toString(exec); + UString s = string->value(exec); JSGlobalData* globalData = &exec->globalData(); JSValue a0 = exec->argument(0); @@ -890,10 +892,8 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState* exec) return throwVMError(exec, createSyntaxError(exec, reg->errorMessage())); } RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); - int pos; - int matchLength = 0; - regExpConstructor->performMatch(*globalData, reg, s, 0, pos, matchLength); - return JSValue::encode(jsNumber(pos)); + MatchResult result = regExpConstructor->performMatch(*globalData, reg, string, s, 0); + return JSValue::encode(result ? jsNumber(result.start) : jsNumber(-1)); } EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec) @@ -923,6 +923,35 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec) return JSValue::encode(jsEmptyString(exec)); } +// Return true in case of early return (resultLength got to limitLength). +template<typename CharacterType> +static ALWAYS_INLINE bool splitStringByOneCharacterImpl(ExecState* exec, JSArray* result, const UString& input, StringImpl* string, UChar separatorCharacter, size_t& position, unsigned& resultLength, unsigned limitLength) +{ + // 12. Let q = p. + size_t matchPosition; + const CharacterType* characters = string->getCharacters<CharacterType>(); + // 13. Repeat, while q != s + // a. Call SplitMatch(S, q, R) and let z be its MatchResult result. + // b. If z is failure, then let q = q+1. + // c. Else, z is not failure + while ((matchPosition = WTF::find(characters, string->length(), separatorCharacter, position)) != notFound) { + // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive) + // through q (exclusive). + // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), + // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + result->putDirectIndex(exec, resultLength, jsSubstring(exec, input, position, matchPosition - position), false); + // 3. Increment lengthA by 1. + // 4. If lengthA == lim, return A. + if (++resultLength == limitLength) + return true; + + // 5. Let p = e. + // 8. Let q = p. + position = matchPosition + 1; + } + return false; +} + // ES 5.1 - 15.5.4.14 String.prototype.split (separator, limit) EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) { @@ -976,7 +1005,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0", // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. // d. Return A. - if (reg->match(*globalData, input, 0) < 0) + if (!reg->match(*globalData, input, 0)) result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input), false); return JSValue::encode(result); } @@ -987,7 +1016,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) while (matchPosition < input.length()) { // a. Call SplitMatch(S, q, R) and let z be its MatchResult result. Vector<int, 32> ovector; - int mpos = reg->match(*globalData, input, matchPosition, &ovector); + int mpos = reg->match(*globalData, input, matchPosition, ovector); // b. If z is failure, then let q = q + 1. if (mpos < 0) break; @@ -1076,26 +1105,50 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) return JSValue::encode(result); } - // 12. Let q = p. - size_t matchPosition; - // 13. Repeat, while q != s - // a. Call SplitMatch(S, q, R) and let z be its MatchResult result. - // b. If z is failure, then let q = q+1. - // c. Else, z is not failure - while ((matchPosition = input.find(separator, position)) != notFound) { - // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive) - // through q (exclusive). - // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), - // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. - result->putDirectIndex(exec, resultLength, jsSubstring(exec, input, position, matchPosition - position), false); - // 3. Increment lengthA by 1. - // 4. If lengthA == lim, return A. - if (++resultLength == limit) - return JSValue::encode(result); + // 3 cases: + // -separator length == 1, 8 bits + // -separator length == 1, 16 bits + // -separator length > 1 + StringImpl* stringImpl = input.impl(); + StringImpl* separatorImpl = separator.impl(); + size_t separatorLength = separatorImpl->length(); + + if (separatorLength == 1) { + UChar separatorCharacter; + if (separatorImpl->is8Bit()) + separatorCharacter = separatorImpl->characters8()[0]; + else + separatorCharacter = separatorImpl->characters16()[0]; + + if (stringImpl->is8Bit()) { + if (splitStringByOneCharacterImpl<LChar>(exec, result, input, stringImpl, separatorCharacter, position, resultLength, limit)) + return JSValue::encode(result); + } else { + if (splitStringByOneCharacterImpl<UChar>(exec, result, input, stringImpl, separatorCharacter, position, resultLength, limit)) + return JSValue::encode(result); + } + } else { + // 12. Let q = p. + size_t matchPosition; + // 13. Repeat, while q != s + // a. Call SplitMatch(S, q, R) and let z be its MatchResult result. + // b. If z is failure, then let q = q+1. + // c. Else, z is not failure + while ((matchPosition = stringImpl->find(separatorImpl, position)) != notFound) { + // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive) + // through q (exclusive). + // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), + // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + result->putDirectIndex(exec, resultLength, jsSubstring(exec, input, position, matchPosition - position), false); + // 3. Increment lengthA by 1. + // 4. If lengthA == lim, return A. + if (++resultLength == limit) + return JSValue::encode(result); - // 5. Let p = e. - // 8. Let q = p. - position = matchPosition + separator.length(); + // 5. Let p = e. + // 8. Let q = p. + position = matchPosition + separator.length(); + } } } @@ -1116,7 +1169,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec) JSString* jsString = 0; UString uString; if (thisValue.isString()) { - jsString = static_cast<JSString*>(thisValue.asCell()); + jsString = jsCast<JSString*>(thisValue.asCell()); len = jsString->length(); } else if (thisValue.isUndefinedOrNull()) { // CheckObjectCoercible diff --git a/Source/JavaScriptCore/runtime/StringRecursionChecker.h b/Source/JavaScriptCore/runtime/StringRecursionChecker.h index e3408b08b..127d028e0 100644 --- a/Source/JavaScriptCore/runtime/StringRecursionChecker.h +++ b/Source/JavaScriptCore/runtime/StringRecursionChecker.h @@ -48,7 +48,7 @@ inline JSValue StringRecursionChecker::performCheck() int size = m_exec->globalData().stringRecursionCheckVisitedObjects.size(); if (size >= MaxSmallThreadReentryDepth && size >= m_exec->globalData().maxReentryDepth) return throwStackOverflowError(); - bool alreadyVisited = !m_exec->globalData().stringRecursionCheckVisitedObjects.add(m_thisObject).second; + bool alreadyVisited = !m_exec->globalData().stringRecursionCheckVisitedObjects.add(m_thisObject).isNewEntry; if (alreadyVisited) return emptyString(); // Return empty string to avoid infinite recursion. return JSValue(); // Indicate success. diff --git a/Source/JavaScriptCore/runtime/Structure.cpp b/Source/JavaScriptCore/runtime/Structure.cpp index 6ee419da6..074c8b354 100644 --- a/Source/JavaScriptCore/runtime/Structure.cpp +++ b/Source/JavaScriptCore/runtime/Structure.cpp @@ -102,12 +102,12 @@ inline void StructureTransitionTable::add(JSGlobalData& globalData, Structure* s // Newer versions of the STL have an std::make_pair function that takes rvalue references. // When either of the parameters are bitfields, the C++ compiler will try to bind them as lvalues, which is invalid. To work around this, use unary "+" to make the parameter an rvalue. // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details - std::pair<TransitionMap::iterator, bool> result = map()->add(globalData, make_pair(structure->m_nameInPrevious, +structure->m_attributesInPrevious), structure); - if (!result.second) { + TransitionMap::AddResult result = map()->add(globalData, make_pair(structure->m_nameInPrevious, +structure->m_attributesInPrevious), structure); + if (!result.isNewEntry) { // There already is an entry! - we should only hit this when despecifying. - ASSERT(result.first.get().second->m_specificValueInPrevious); + ASSERT(result.iterator.get().second->m_specificValueInPrevious); ASSERT(!structure->m_specificValueInPrevious); - map()->set(result.first, structure); + map()->set(globalData, result.iterator.get().first, structure); } } @@ -267,6 +267,13 @@ void Structure::growPropertyStorageCapacity() m_propertyStorageCapacity *= 2; } +size_t Structure::suggestedNewPropertyStorageSize() +{ + if (isUsingInlineStorage()) + return JSObject::baseExternalStorageCapacity; + return m_propertyStorageCapacity * 2; +} + void Structure::despecifyDictionaryFunction(JSGlobalData& globalData, const Identifier& propertyName) { StringImpl* rep = propertyName.impl(); @@ -787,6 +794,8 @@ void Structure::visitChildren(JSCell* cell, SlotVisitor& visitor) visitor.append(&ptr->specificValue); } } + if (thisObject->m_objectToStringValue) + visitor.append(&thisObject->m_objectToStringValue); } #if DO_PROPERTYMAP_CONSTENCY_CHECK diff --git a/Source/JavaScriptCore/runtime/Structure.h b/Source/JavaScriptCore/runtime/Structure.h index 46cf732e1..00bc76177 100644 --- a/Source/JavaScriptCore/runtime/Structure.h +++ b/Source/JavaScriptCore/runtime/Structure.h @@ -50,6 +50,7 @@ namespace JSC { class PropertyNameArrayData; class StructureChain; class SlotVisitor; + class JSString; class Structure : public JSCell { public: @@ -101,6 +102,8 @@ namespace JSC { bool isFrozen(JSGlobalData&); bool isExtensible() const { return !m_preventExtensions; } bool didTransition() const { return m_didTransition; } + bool shouldGrowPropertyStorage() { return propertyStorageCapacity() == propertyStorageSize(); } + JS_EXPORT_PRIVATE size_t suggestedNewPropertyStorageSize(); Structure* flattenDictionaryStructure(JSGlobalData&, JSObject*); @@ -169,6 +172,13 @@ namespace JSC { JSPropertyNameIterator* enumerationCache(); // Defined in JSPropertyNameIterator.h. void getPropertyNamesFromStructure(JSGlobalData&, PropertyNameArray&, EnumerationMode); + JSString* objectToStringValue() { return m_objectToStringValue.get(); } + + void setObjectToStringValue(JSGlobalData& globalData, const JSCell* owner, JSString* value) + { + m_objectToStringValue.set(globalData, owner, value); + } + bool staticFunctionsReified() { return m_staticFunctionReified; @@ -289,6 +299,8 @@ namespace JSC { uint32_t m_propertyStorageCapacity; + WriteBarrier<JSString> m_objectToStringValue; + // m_offset does not account for anonymous slots int m_offset; @@ -406,7 +418,7 @@ namespace JSC { { #if ENABLE(GC_VALIDATION) ASSERT(globalData.isInitializingObject()); - globalData.setInitializingObject(false); + globalData.setInitializingObjectClass(0); if (structure) #endif m_structure.setEarlyValue(globalData, this, structure); diff --git a/Source/JavaScriptCore/runtime/StructureTransitionTable.h b/Source/JavaScriptCore/runtime/StructureTransitionTable.h index 517992470..2067a8995 100644 --- a/Source/JavaScriptCore/runtime/StructureTransitionTable.h +++ b/Source/JavaScriptCore/runtime/StructureTransitionTable.h @@ -34,6 +34,7 @@ namespace JSC { +class JSCell; class Structure; class StructureTransitionTable { @@ -83,10 +84,10 @@ public: return; } - HandleSlot slot = this->slot(); - if (!slot) + WeakImpl* impl = this->weakImpl(); + if (!impl) return; - HandleHeap::heapFor(slot)->deallocate(slot); + WeakSet::deallocate(impl); } inline void add(JSGlobalData&, Structure*); @@ -105,18 +106,18 @@ private: return reinterpret_cast<TransitionMap*>(m_data); } - HandleSlot slot() const + WeakImpl* weakImpl() const { ASSERT(isUsingSingleSlot()); - return reinterpret_cast<HandleSlot>(m_data & ~UsingSingleSlotFlag); + return reinterpret_cast<WeakImpl*>(m_data & ~UsingSingleSlotFlag); } void setMap(TransitionMap* map) { ASSERT(isUsingSingleSlot()); - if (HandleSlot slot = this->slot()) - HandleHeap::heapFor(slot)->deallocate(slot); + if (WeakImpl* impl = this->weakImpl()) + WeakSet::deallocate(impl); // This implicitly clears the flag that indicates we're using a single transition m_data = reinterpret_cast<intptr_t>(map); @@ -127,24 +128,20 @@ private: Structure* singleTransition() const { ASSERT(isUsingSingleSlot()); - if (HandleSlot slot = this->slot()) { - if (*slot) - return reinterpret_cast<Structure*>(slot->asCell()); + if (WeakImpl* impl = this->weakImpl()) { + if (impl->state() == WeakImpl::Live) + return reinterpret_cast<Structure*>(impl->jsValue().asCell()); } return 0; } - void setSingleTransition(JSGlobalData& globalData, Structure* structure) + void setSingleTransition(JSGlobalData&, Structure* structure) { ASSERT(isUsingSingleSlot()); - HandleSlot slot = this->slot(); - if (!slot) { - slot = globalData.heap.handleHeap()->allocate(); - HandleHeap::heapFor(slot)->makeWeak(slot, 0, 0); - m_data = reinterpret_cast<intptr_t>(slot) | UsingSingleSlotFlag; - } - HandleHeap::heapFor(slot)->writeBarrier(slot, reinterpret_cast<JSCell*>(structure)); - *slot = reinterpret_cast<JSCell*>(structure); + if (WeakImpl* impl = this->weakImpl()) + WeakSet::deallocate(impl); + WeakImpl* impl = WeakSet::allocate(reinterpret_cast<JSCell*>(structure)); + m_data = reinterpret_cast<intptr_t>(impl) | UsingSingleSlotFlag; } intptr_t m_data; diff --git a/Source/JavaScriptCore/runtime/TimeoutChecker.cpp b/Source/JavaScriptCore/runtime/TimeoutChecker.cpp index 3065c99ed..8f3d1a578 100644 --- a/Source/JavaScriptCore/runtime/TimeoutChecker.cpp +++ b/Source/JavaScriptCore/runtime/TimeoutChecker.cpp @@ -38,7 +38,7 @@ #elif OS(WINDOWS) #include <windows.h> #else -#include "CurrentTime.h" +#include <wtf/CurrentTime.h> #endif using namespace std; diff --git a/Source/JavaScriptCore/runtime/UString.h b/Source/JavaScriptCore/runtime/UString.h index 668eb0489..7677161a3 100644 --- a/Source/JavaScriptCore/runtime/UString.h +++ b/Source/JavaScriptCore/runtime/UString.h @@ -121,8 +121,12 @@ public: // Find a single character or string, also with match function & latin1 forms. size_t find(UChar c, unsigned start = 0) const { return m_impl ? m_impl->find(c, start) : notFound; } - size_t find(const UString& str, unsigned start = 0) const + + size_t find(const UString& str) const + { return m_impl ? m_impl->find(str.impl()) : notFound; } + size_t find(const UString& str, unsigned start) const { return m_impl ? m_impl->find(str.impl(), start) : notFound; } + size_t find(const LChar* str, unsigned start = 0) const { return m_impl ? m_impl->find(str, start) : notFound; } diff --git a/Source/JavaScriptCore/runtime/WeakGCMap.h b/Source/JavaScriptCore/runtime/WeakGCMap.h index 1bb3cd5bb..ec010fb4b 100644 --- a/Source/JavaScriptCore/runtime/WeakGCMap.h +++ b/Source/JavaScriptCore/runtime/WeakGCMap.h @@ -51,7 +51,7 @@ class WeakGCMap : private WeakHandleOwner { WTF_MAKE_FAST_ALLOCATED; WTF_MAKE_NONCOPYABLE(WeakGCMap); - typedef HashMap<KeyType, HandleSlot, HashArg, KeyTraitsArg> MapType; + typedef HashMap<KeyType, WeakImpl*, HashArg, KeyTraitsArg> MapType; typedef typename HandleTypes<MappedType>::ExternalType ExternalType; typedef typename MapType::iterator map_iterator; @@ -64,8 +64,7 @@ public: { } - std::pair<KeyType, ExternalType> get() const { return std::make_pair(m_iterator->first, HandleTypes<MappedType>::getFromSlot(m_iterator->second)); } - std::pair<KeyType, HandleSlot> getSlot() const { return *m_iterator; } + std::pair<KeyType, ExternalType> get() const { return std::make_pair(m_iterator->first, HandleTypes<MappedType>::getFromSlot(const_cast<JSValue*>(&m_iterator->second->jsValue()))); } iterator& operator++() { ++m_iterator; return *this; } @@ -79,6 +78,8 @@ public: map_iterator m_iterator; }; + typedef WTF::HashTableAddResult<iterator> AddResult; + WeakGCMap() { } @@ -88,7 +89,7 @@ public: { map_iterator end = m_map.end(); for (map_iterator ptr = m_map.begin(); ptr != end; ++ptr) - HandleHeap::heapFor(ptr->second)->deallocate(ptr->second); + WeakSet::deallocate(ptr->second); m_map.clear(); } @@ -105,63 +106,42 @@ public: void remove(iterator iter) { ASSERT(iter.m_iterator != m_map.end()); - HandleSlot slot = iter.m_iterator->second; - ASSERT(slot); - HandleHeap::heapFor(slot)->deallocate(slot); + WeakImpl* impl = iter.m_iterator->second; + ASSERT(impl); + WeakSet::deallocate(impl); m_map.remove(iter.m_iterator); } ExternalType get(const KeyType& key) const { - return HandleTypes<MappedType>::getFromSlot(m_map.get(key)); + return HandleTypes<MappedType>::getFromSlot(const_cast<JSValue*>(&m_map.get(key)->jsValue())); } - HandleSlot getSlot(const KeyType& key) const + AddResult add(JSGlobalData&, const KeyType& key, ExternalType value) { - return m_map.get(key); - } + typename MapType::AddResult result = m_map.add(key, 0); + if (result.isNewEntry) + result.iterator->second = WeakSet::allocate(value, this, FinalizerCallback::finalizerContextFor(key)); - pair<iterator, bool> add(JSGlobalData& globalData, const KeyType& key, ExternalType value) - { - pair<typename MapType::iterator, bool> iter = m_map.add(key, 0); - if (iter.second) { - HandleSlot slot = globalData.heap.handleHeap()->allocate(); - iter.first->second = slot; - HandleHeap::heapFor(slot)->makeWeak(slot, this, FinalizerCallback::finalizerContextFor(key)); - HandleHeap::heapFor(slot)->writeBarrier(slot, value); - *slot = value; - } - return iter; - } - - void set(iterator iter, ExternalType value) - { - HandleSlot slot = iter.m_iterator->second; - ASSERT(slot); - HandleHeap::heapFor(slot)->writeBarrier(slot, value); - *slot = value; + // WeakGCMap exposes a different iterator, so we need to wrap it and create our own AddResult. + return AddResult(iterator(result.iterator), result.isNewEntry); } - void set(JSGlobalData& globalData, const KeyType& key, ExternalType value) + void set(JSGlobalData&, const KeyType& key, ExternalType value) { - pair<typename MapType::iterator, bool> iter = m_map.add(key, 0); - HandleSlot slot = iter.first->second; - if (iter.second) { - slot = globalData.heap.handleHeap()->allocate(); - HandleHeap::heapFor(slot)->makeWeak(slot, this, key); - iter.first->second = slot; - } - HandleHeap::heapFor(slot)->writeBarrier(slot, value); - *slot = value; + typename MapType::AddResult result = m_map.add(key, 0); + if (!result.isNewEntry) + WeakSet::deallocate(result.iterator->second); + result.iterator->second = WeakSet::allocate(value, this, FinalizerCallback::finalizerContextFor(key)); } ExternalType take(const KeyType& key) { - HandleSlot slot = m_map.take(key); - if (!slot) + WeakImpl* impl = m_map.take(key); + if (!impl) return HashTraits<ExternalType>::emptyValue(); - ExternalType result = HandleTypes<MappedType>::getFromSlot(slot); - HandleHeap::heapFor(slot)->deallocate(slot); + ExternalType result = HandleTypes<MappedType>::getFromSlot(const_cast<JSValue*>(&impl->jsValue())); + WeakSet::deallocate(impl); return result; } @@ -178,9 +158,9 @@ public: private: virtual void finalize(Handle<Unknown> handle, void* context) { - HandleSlot slot = m_map.take(FinalizerCallback::keyForFinalizer(context, HandleTypes<MappedType>::getFromSlot(handle.slot()))); - ASSERT(slot); - HandleHeap::heapFor(slot)->deallocate(slot); + WeakImpl* impl = m_map.take(FinalizerCallback::keyForFinalizer(context, HandleTypes<MappedType>::getFromSlot(handle.slot()))); + ASSERT(impl); + WeakSet::deallocate(impl); } MapType m_map; |